1818import java .util .Collections ;
1919import java .util .HashMap ;
2020import java .util .Map ;
21- import java .util .Locale ;
2221
2322/**
24- * The storage authorization policy which supports challenge .
23+ * The storage authorization policy which supports challenges .
2524 */
2625public class StorageBearerTokenChallengeAuthorizationPolicy extends BearerTokenAuthenticationPolicy {
27-
2826 private static final ClientLogger LOGGER = new ClientLogger (StorageBearerTokenChallengeAuthorizationPolicy .class );
2927
3028 private static final String DEFAULT_SCOPE = "/.default" ;
31- static final String BEARER_TOKEN_PREFIX = "Bearer " ;
29+ private static final String BEARER_TOKEN_PREFIX = "Bearer" ;
30+ private static final String RESOURCE_ID = "resource_id" ;
31+ private static final String AUTHORIZATION_URI = "authorization_uri" ;
3232
33- private String [] scopes ;
33+ // Immutable constructor scopes (base class retains them); challenge may supply new scopes dynamically.
34+ private final String [] initialScopes ;
3435
3536 /**
3637 * Creates StorageBearerTokenChallengeAuthorizationPolicy.
@@ -40,56 +41,45 @@ public class StorageBearerTokenChallengeAuthorizationPolicy extends BearerTokenA
4041 */
4142 public StorageBearerTokenChallengeAuthorizationPolicy (TokenCredential credential , String ... scopes ) {
4243 super (credential , scopes );
43- this .scopes = scopes ;
44+ this .initialScopes = CoreUtils . clone ( scopes ) ;
4445 }
4546
4647 @ Override
4748 public Mono <Void > authorizeRequest (HttpPipelineCallContext context ) {
48- String [] scopes = this .scopes ;
49- scopes = getScopes (context , scopes );
50- if (scopes == null ) {
51- return Mono .empty ();
52- } else {
53- return setAuthorizationHeader (context , new TokenRequestContext ().addScopes (scopes ));
54- }
49+ // Delegate to superclass to maintain previous behavior
50+ return super .authorizeRequest (context );
5551 }
5652
5753 @ Override
5854 public void authorizeRequestSync (HttpPipelineCallContext context ) {
59- String [] scopes = this .scopes ;
60- scopes = getScopes (context , scopes );
61-
62- if (scopes != null ) {
63- setAuthorizationHeaderSync (context , new TokenRequestContext ().addScopes (scopes ));
64- }
55+ // Delegate to superclass to maintain previous behavior
56+ super .authorizeRequestSync (context );
6557 }
6658
6759 @ Override
6860 public Mono <Boolean > authorizeRequestOnChallenge (HttpPipelineCallContext context , HttpResponse response ) {
6961 String authHeader = response .getHeaderValue (HttpHeaderName .WWW_AUTHENTICATE );
70- Map <String , String > challenges = extractChallengeAttributes (authHeader , BEARER_TOKEN_PREFIX );
71-
72- String scope = getScopeFromChallenges (challenges );
73- String authorization = getAuthorizationFromChallenges (challenges );
62+ Map <String , String > attributes = extractChallengeAttributes (authHeader );
7463
75- if (scope != null ) {
76- scope += DEFAULT_SCOPE ;
77- scopes = new String [] { scope };
78- scopes = getScopes (context , scopes );
64+ if (attributes .isEmpty ()) {
65+ return Mono .just (false );
7966 }
8067
81- if (authorization != null ) {
82- String tenantId = extractTenantIdFromUri (authorization );
83- TokenRequestContext tokenRequestContext = new TokenRequestContext ().addScopes (scopes ).setTenantId (tenantId );
84- return setAuthorizationHeader (context , tokenRequestContext ).thenReturn (true );
85- }
68+ String resource = attributes .get (RESOURCE_ID );
69+ String authUrl = attributes .get (AUTHORIZATION_URI );
8670
87- if (scope != null ) {
88- TokenRequestContext tokenRequestContext = new TokenRequestContext ().addScopes (scopes );
89- return setAuthorizationHeader (context , tokenRequestContext ).thenReturn (true );
71+ // Create new scopes array to avoid mutating the original
72+ String [] scopesToUse = initialScopes ;
73+ if (!CoreUtils .isNullOrEmpty (resource )) {
74+ scopesToUse = new String [] { resource .endsWith (DEFAULT_SCOPE ) ? resource : resource + DEFAULT_SCOPE };
9075 }
9176
92- return Mono .just (false );
77+ TokenRequestContext tokenRequestContext = new TokenRequestContext ().addScopes (scopesToUse ).setCaeEnabled (true );
78+
79+ if (!CoreUtils .isNullOrEmpty (authUrl )) {
80+ tokenRequestContext .setTenantId (extractTenantIdFromUri (authUrl ));
81+ }
82+ return setAuthorizationHeader (context , tokenRequestContext ).thenReturn (true );
9383 }
9484
9585 String extractTenantIdFromUri (String uri ) {
@@ -108,66 +98,60 @@ String extractTenantIdFromUri(String uri) {
10898 @ Override
10999 public boolean authorizeRequestOnChallengeSync (HttpPipelineCallContext context , HttpResponse response ) {
110100 String authHeader = response .getHeaderValue (HttpHeaderName .WWW_AUTHENTICATE );
111- Map <String , String > challenges = extractChallengeAttributes (authHeader , BEARER_TOKEN_PREFIX );
112-
113- String scope = getScopeFromChallenges (challenges );
114- String authorization = getAuthorizationFromChallenges (challenges );
101+ Map <String , String > attributes = extractChallengeAttributes (authHeader );
115102
116- if (scope != null ) {
117- scope += DEFAULT_SCOPE ;
118- scopes = new String [] { scope };
119- scopes = getScopes (context , scopes );
103+ if (attributes .isEmpty ()) {
104+ return false ;
120105 }
121106
122- if (authorization != null ) {
123- String tenantId = extractTenantIdFromUri (authorization );
124- TokenRequestContext tokenRequestContext = new TokenRequestContext ().addScopes (scopes ).setTenantId (tenantId );
125- setAuthorizationHeaderSync (context , tokenRequestContext );
126- return true ;
127- }
107+ String resource = attributes .get (RESOURCE_ID );
108+ String authUrl = attributes .get (AUTHORIZATION_URI );
128109
129- if ( scope != null ) {
130- TokenRequestContext tokenRequestContext = new TokenRequestContext (). addScopes ( scopes ) ;
131- setAuthorizationHeaderSync ( context , tokenRequestContext );
132- return true ;
110+ // Create new scopes array to avoid mutating the original
111+ String [] scopesToUse = initialScopes ;
112+ if (! CoreUtils . isNullOrEmpty ( resource )) {
113+ scopesToUse = new String [] { resource . endsWith ( DEFAULT_SCOPE ) ? resource : resource + DEFAULT_SCOPE } ;
133114 }
134115
135- return false ;
136- }
116+ TokenRequestContext tokenRequestContext = new TokenRequestContext ().addScopes (scopesToUse ).setCaeEnabled (true );
137117
138- String [] getScopes (HttpPipelineCallContext context , String [] scopes ) {
139- return CoreUtils .clone (scopes );
118+ if (!CoreUtils .isNullOrEmpty (authUrl )) {
119+ tokenRequestContext .setTenantId (extractTenantIdFromUri (authUrl ));
120+ }
121+
122+ setAuthorizationHeaderSync (context , tokenRequestContext );
123+ return true ;
140124 }
141125
142- Map <String , String > extractChallengeAttributes (String header , String authChallengePrefix ) {
143- if (!isBearerChallenge (header , authChallengePrefix )) {
126+ static Map <String , String > extractChallengeAttributes (String header ) {
127+ if (!isBearerChallenge (header )) {
144128 return Collections .emptyMap ();
145129 }
146130
147- header = header .toLowerCase (Locale .ROOT ).replace (authChallengePrefix .toLowerCase (Locale .ROOT ), "" );
131+ // Don't lowercase the entire header as values can be corrupted. Also don't mutate the original header.
132+ String remainingHeader = header .substring (BEARER_TOKEN_PREFIX .length ());
148133
149- String [] attributes = header .split (" " );
134+ // Split on commas first; if no commas present fall back to spaces.
135+ String [] attributes = remainingHeader .contains ("," ) ? remainingHeader .split ("," ) : remainingHeader .split (" " );
150136 Map <String , String > attributeMap = new HashMap <>();
151137
152138 for (String pair : attributes ) {
139+ // Skip empty strings and any strings that don't contain '='.
140+ // Allows scenarios where there is only an auth URI and no resource ID.
141+ if (CoreUtils .isNullOrEmpty (pair ) || !pair .contains ("=" )) {
142+ continue ;
143+ }
153144 String [] keyValue = pair .split ("=" );
154145
155- attributeMap .put (keyValue [0 ].replaceAll ("\" " , "" ), keyValue [1 ].replaceAll ("\" " , "" ));
146+ // Support spaces after commas by trimming.
147+ attributeMap .put (keyValue [0 ].replaceAll ("\" " , "" ).trim (), keyValue [1 ].replaceAll ("\" " , "" ).trim ());
156148 }
157149
158150 return attributeMap ;
159151 }
160152
161- static boolean isBearerChallenge (String authenticateHeader , String authChallengePrefix ) {
153+ static boolean isBearerChallenge (String authenticateHeader ) {
162154 return (!CoreUtils .isNullOrEmpty (authenticateHeader )
163- && authenticateHeader .toLowerCase (Locale .ROOT ).startsWith (authChallengePrefix .toLowerCase (Locale .ROOT )));
164- }
165-
166- String getScopeFromChallenges (Map <String , String > challenges ) {
167- return challenges .get ("resource_id" );
168- }
169-
170- String getAuthorizationFromChallenges (Map <String , String > challenges ) {
171- return challenges .get ("authorization_uri" );
155+ && authenticateHeader .regionMatches (true , 0 , BEARER_TOKEN_PREFIX , 0 , BEARER_TOKEN_PREFIX .length ()));
172156 }
173157}
0 commit comments