1414
1515class DatabricksCliScopeValidationTest {
1616
17- private static final String HOST = "https://my-workspace.cloud.databricks.com" ;
1817 private static final ObjectMapper MAPPER = new ObjectMapper ();
1918
2019 /** Builds a fake JWT (header.payload.signature) with the given claims. */
@@ -62,16 +61,24 @@ static List<Arguments> scopeValidationCases() {
6261 Arrays .asList ("all-apis" , "offline_access" ),
6362 false ,
6463 "offline_access_in_config_only" ),
65- // Scope claim as list instead of string .
64+ // Order should not matter .
6665 Arguments .of (
67- new HashMap <String , Object >() {
68- {
69- put ("scope" , Arrays .asList ("sql" , "offline_access" ));
70- }
71- },
66+ Collections .singletonMap ("scope" , "clusters sql" ),
67+ Arrays .asList ("sql" , "clusters" ),
68+ false ,
69+ "multiple_scopes_order_independent" ),
70+ // Partial overlap is still a mismatch.
71+ Arguments .of (
72+ Collections .singletonMap ("scope" , "sql clusters" ),
73+ Arrays .asList ("sql" , "compute" ),
74+ true ,
75+ "multiple_scopes_partial_overlap_mismatch" ),
76+ // No scope claim in token — validation is skipped.
77+ Arguments .of (
78+ Collections .singletonMap ("sub" , "user@example.com" ),
7279 Collections .singletonList ("sql" ),
7380 false ,
74- "scope_as_list " ));
81+ "no_scope_claim_skips_validation " ));
7582 }
7683
7784 @ ParameterizedTest (name = "{3}" )
@@ -86,31 +93,20 @@ void testScopeValidation(
8693 if (expectError ) {
8794 assertThrows (
8895 DatabricksCliCredentialsProvider .ScopeMismatchException .class ,
89- () ->
90- DatabricksCliCredentialsProvider .validateTokenScopes (token , configuredScopes , HOST ));
96+ () -> DatabricksCliCredentialsProvider .validateTokenScopes (token , configuredScopes ));
9197 } else {
9298 assertDoesNotThrow (
93- () ->
94- DatabricksCliCredentialsProvider .validateTokenScopes (token , configuredScopes , HOST ));
99+ () -> DatabricksCliCredentialsProvider .validateTokenScopes (token , configuredScopes ));
95100 }
96101 }
97102
98- @ Test
99- void testNoScopeClaimSkipsValidation () {
100- Token token = makeToken (Collections .singletonMap ("sub" , "user@example.com" ));
101- assertDoesNotThrow (
102- () ->
103- DatabricksCliCredentialsProvider .validateTokenScopes (
104- token , Collections .singletonList ("sql" ), HOST ));
105- }
106-
107103 @ Test
108104 void testNonJwtTokenSkipsValidation () {
109105 Token token = new Token ("opaque-token-string" , "Bearer" , Instant .now ().plusSeconds (3600 ));
110106 assertDoesNotThrow (
111107 () ->
112108 DatabricksCliCredentialsProvider .validateTokenScopes (
113- token , Collections .singletonList ("sql" ), HOST ));
109+ token , Collections .singletonList ("sql" )));
114110 }
115111
116112 @ Test
@@ -121,12 +117,27 @@ void testErrorMessageContainsReauthCommand() {
121117 DatabricksCliCredentialsProvider .ScopeMismatchException .class ,
122118 () ->
123119 DatabricksCliCredentialsProvider .validateTokenScopes (
124- token , Arrays .asList ("sql" , "offline_access" ), HOST ));
120+ token , Arrays .asList ("sql" , "offline_access" )));
125121 assertTrue (
126122 e .getMessage ().contains ("databricks auth login" ),
127123 "Expected re-auth command in error message, got: " + e .getMessage ());
128124 assertTrue (
129125 e .getMessage ().contains ("do not match the configured scopes" ),
130126 "Expected scope mismatch details in error message, got: " + e .getMessage ());
131127 }
128+
129+ @ Test
130+ void testScopesExplicitlySetFlag () {
131+ DatabricksConfig config = new DatabricksConfig ();
132+ assertFalse (config .isScopesExplicitlySet ());
133+
134+ config .setScopes (Arrays .asList ("sql" , "clusters" ));
135+ assertTrue (config .isScopesExplicitlySet ());
136+
137+ config .setScopes (Collections .emptyList ());
138+ assertFalse (config .isScopesExplicitlySet (), "Empty list should not count as explicitly set" );
139+
140+ config .setScopes (null );
141+ assertFalse (config .isScopesExplicitlySet (), "null should not count as explicitly set" );
142+ }
132143}
0 commit comments