@@ -11,69 +11,81 @@ namespace Microsoft.Azure.SignalR
1111 internal static class ConnectionStringParser
1212 {
1313 private const string AccessKeyProperty = "accesskey" ;
14+
1415 private const string AuthTypeProperty = "authtype" ;
16+
1517 private const string ClientCertProperty = "clientCert" ;
16- private const string ClientEndpointProperty = "ClientEndpoint" ;
18+
19+ private const string ClientEndpointProperty = "clientEndpoint" ;
20+
1721 private const string ClientIdProperty = "clientId" ;
22+
1823 private const string ClientSecretProperty = "clientSecret" ;
24+
1925 private const string EndpointProperty = "endpoint" ;
20- private const string ServerEndpoint = "ServerEndpoint" ;
26+
2127 private const string InvalidVersionValueFormat = "Version {0} is not supported." ;
28+
2229 private const string PortProperty = "port" ;
30+
31+ private const string ServerEndpoint = "ServerEndpoint" ;
32+
2333 // For SDK 1.x, only support Azure SignalR Service 1.x
2434 private const string SupportedVersion = "1" ;
2535
2636 private const string TenantIdProperty = "tenantId" ;
37+
38+ private const string TypeAzure = "azure" ;
39+
40+ private const string TypeAzureAD = "aad" ;
41+
42+ private const string TypeAzureApp = "azure.app" ;
43+
44+ private const string TypeAzureMsi = "azure.msi" ;
45+
2746 private const string ValidVersionRegex = "^" + SupportedVersion + @"\.\d+(?:[\w-.]+)?$" ;
47+
2848 private const string VersionProperty = "version" ;
29- private static readonly string InvalidPortValue = $ "Invalid value for { PortProperty } property.";
49+
50+ private static readonly string InvalidClientEndpointProperty = $ "Invalid value for { ClientEndpointProperty } property, it must be a valid URI.";
51+
52+ private static readonly string InvalidEndpointProperty = $ "Invalid value for { EndpointProperty } property, it must be a valid URI.";
53+
54+ private static readonly string InvalidPortValue = $ "Invalid value for { PortProperty } property, it must be an positive integer between (0, 65536)";
3055
3156 private static readonly char [ ] KeyValueSeparator = { '=' } ;
3257
3358 private static readonly string MissingAccessKeyProperty =
3459 $ "{ AccessKeyProperty } is required.";
3560
61+ private static readonly string MissingClientIdProperty =
62+ $ "Connection string missing required properties { ClientIdProperty } .";
63+
3664 private static readonly string MissingClientSecretProperty =
3765 $ "Connection string missing required properties { ClientSecretProperty } or { ClientCertProperty } .";
3866
3967 private static readonly string MissingEndpointProperty =
4068 $ "Connection string missing required properties { EndpointProperty } .";
4169
70+ private static readonly string MissingTenantIdProperty =
71+ $ "Connection string missing required properties { TenantIdProperty } .";
72+
4273 private static readonly char [ ] PropertySeparator = { ';' } ;
4374
4475 internal static ParsedConnectionString Parse ( string connectionString )
4576 {
46- var properties = connectionString . Split ( PropertySeparator , StringSplitOptions . RemoveEmptyEntries ) ;
47- if ( properties . Length < 2 )
48- {
49- throw new ArgumentException ( MissingEndpointProperty , nameof ( connectionString ) ) ;
50- }
51-
52- var dict = new Dictionary < string , string > ( StringComparer . OrdinalIgnoreCase ) ;
53- foreach ( var property in properties )
54- {
55- var kvp = property . Split ( KeyValueSeparator , 2 ) ;
56- if ( kvp . Length != 2 ) continue ;
57-
58- var key = kvp [ 0 ] . Trim ( ) ;
59- if ( dict . ContainsKey ( key ) )
60- {
61- throw new ArgumentException ( $ "Duplicate properties found in connection string: { key } .") ;
62- }
63-
64- dict . Add ( key , kvp [ 1 ] . Trim ( ) ) ;
65- }
77+ var dict = ToDictionary ( connectionString ) ;
6678
6779 // parse and validate endpoint.
6880 if ( ! dict . TryGetValue ( EndpointProperty , out var endpoint ) )
6981 {
70- throw new ArgumentException ( MissingEndpointProperty , nameof ( connectionString ) ) ;
82+ throw new ArgumentException ( MissingEndpointProperty , nameof ( endpoint ) ) ;
7183 }
7284 endpoint = endpoint . TrimEnd ( '/' ) ;
7385
7486 if ( ! TryGetEndpointUri ( endpoint , out var endpointUri ) )
7587 {
76- throw new ArgumentException ( $ "Endpoint property in connection string is not a valid URI: { dict [ EndpointProperty ] } ." ) ;
88+ throw new ArgumentException ( InvalidEndpointProperty , nameof ( endpoint ) ) ;
7789 }
7890 var builder = new UriBuilder ( endpointUri ) ;
7991
@@ -83,38 +95,43 @@ internal static ParsedConnectionString Parse(string connectionString)
8395 {
8496 if ( ! Regex . IsMatch ( v , ValidVersionRegex ) )
8597 {
86- throw new ArgumentException ( string . Format ( InvalidVersionValueFormat , v ) , nameof ( connectionString ) ) ;
98+ throw new ArgumentException ( string . Format ( InvalidVersionValueFormat , v ) , nameof ( version ) ) ;
8799 }
88100 version = v ;
89101 }
90102
91103 // parse and validate port.
92104 if ( dict . TryGetValue ( PortProperty , out var s ) )
93105 {
94- if ( int . TryParse ( s , out var p ) && p > 0 && p <= 0xFFFF )
106+ if ( int . TryParse ( s , out var port ) && port > 0 && port <= 0xFFFF )
95107 {
96- builder . Port = p ;
108+ builder . Port = port ;
97109 }
98110 else
99111 {
100- throw new ArgumentException ( InvalidPortValue , nameof ( connectionString ) ) ;
112+ throw new ArgumentException ( InvalidPortValue , nameof ( port ) ) ;
101113 }
102114 }
103115
104116 Uri clientEndpointUri = null ;
117+
105118 // parse and validate clientEndpoint.
106119 if ( dict . TryGetValue ( ClientEndpointProperty , out var clientEndpoint ) )
107120 {
108121 if ( ! TryGetEndpointUri ( clientEndpoint , out clientEndpointUri ) )
109122 {
110- throw new ArgumentException ( $ " { ClientEndpointProperty } property in connection string is not a valid URI: { clientEndpoint } ." ) ;
123+ throw new ArgumentException ( InvalidClientEndpointProperty , nameof ( clientEndpoint ) ) ;
111124 }
112125 }
113126
127+ // try building accesskey.
114128 dict . TryGetValue ( AuthTypeProperty , out var type ) ;
115129 var accessKey = type ? . ToLower ( ) switch
116130 {
117- "aad" => BuildAadAccessKey ( builder . Uri , dict ) ,
131+ TypeAzureAD => BuildAadAccessKey ( builder . Uri , dict ) ,
132+ TypeAzure => BuildAzureAccessKey ( builder . Uri , dict ) ,
133+ TypeAzureApp => BuildAzureAppAccessKey ( builder . Uri , dict ) ,
134+ TypeAzureMsi => BuildAzureMsiAccessKey ( builder . Uri , dict ) ,
118135 _ => BuildAccessKey ( builder . Uri , dict ) ,
119136 } ;
120137
@@ -180,5 +197,70 @@ private static AccessKey BuildAccessKey(Uri uri, Dictionary<string, string> dict
180197 }
181198 throw new ArgumentException ( MissingAccessKeyProperty , AccessKeyProperty ) ;
182199 }
200+
201+ private static AccessKey BuildAzureAccessKey ( Uri uri , Dictionary < string , string > dict )
202+ {
203+ return new AadAccessKey ( uri , new DefaultAzureCredential ( ) ) ;
204+ }
205+
206+ private static AccessKey BuildAzureAppAccessKey ( Uri uri , Dictionary < string , string > dict )
207+ {
208+ if ( ! dict . TryGetValue ( ClientIdProperty , out var clientId ) )
209+ {
210+ throw new ArgumentException ( MissingClientIdProperty , ClientIdProperty ) ;
211+ }
212+
213+ if ( ! dict . TryGetValue ( TenantIdProperty , out var tenantId ) )
214+ {
215+ throw new ArgumentException ( MissingTenantIdProperty , TenantIdProperty ) ;
216+ }
217+
218+ if ( dict . TryGetValue ( ClientSecretProperty , out var clientSecret ) )
219+ {
220+ return new AadAccessKey ( uri , new ClientSecretCredential ( tenantId , clientId , clientSecret ) ) ;
221+ }
222+ else if ( dict . TryGetValue ( ClientCertProperty , out var clientCertPath ) )
223+ {
224+ return new AadAccessKey ( uri , new ClientCertificateCredential ( tenantId , clientId , clientCertPath ) ) ;
225+ }
226+ throw new ArgumentException ( MissingClientSecretProperty , ClientSecretProperty ) ;
227+ }
228+
229+ private static AccessKey BuildAzureMsiAccessKey ( Uri uri , Dictionary < string , string > dict )
230+ {
231+ if ( dict . TryGetValue ( ClientIdProperty , out var clientId ) )
232+ {
233+ return new AadAccessKey ( uri , new ManagedIdentityCredential ( clientId ) ) ;
234+ }
235+ return new AadAccessKey ( uri , new ManagedIdentityCredential ( ) ) ;
236+ }
237+
238+ private static Dictionary < string , string > ToDictionary ( string connectionString )
239+ {
240+ var properties = connectionString . Split ( PropertySeparator , StringSplitOptions . RemoveEmptyEntries ) ;
241+ if ( properties . Length < 2 )
242+ {
243+ throw new ArgumentException ( MissingEndpointProperty , nameof ( connectionString ) ) ;
244+ }
245+
246+ var dict = new Dictionary < string , string > ( StringComparer . OrdinalIgnoreCase ) ;
247+ foreach ( var property in properties )
248+ {
249+ var kvp = property . Split ( KeyValueSeparator , 2 ) ;
250+ if ( kvp . Length != 2 )
251+ {
252+ continue ;
253+ }
254+
255+ var key = kvp [ 0 ] . Trim ( ) ;
256+ if ( dict . ContainsKey ( key ) )
257+ {
258+ throw new ArgumentException ( $ "Duplicate properties found in connection string: { key } .") ;
259+ }
260+
261+ dict . Add ( key , kvp [ 1 ] . Trim ( ) ) ;
262+ }
263+ return dict ;
264+ }
183265 }
184266}
0 commit comments