Skip to content

Commit 667f424

Browse files
improve comments, refcator tests and remove unused field
1 parent b19225a commit 667f424

File tree

3 files changed

+42
-42
lines changed

3 files changed

+42
-42
lines changed

databricks-sdk-java/src/main/java/com/databricks/sdk/core/DatabricksCliCredentialsProvider.java

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public OAuthHeaderFactory configure(DatabricksConfig config) {
123123
@Override
124124
public Token getToken() {
125125
Token t = tokenSource.getToken();
126-
validateTokenScopes(t, scopes, host);
126+
validateTokenScopes(t, scopes);
127127
return t;
128128
}
129129
};
@@ -171,7 +171,7 @@ public Token getToken() {
171171
* will silently use the wrong scopes. This check surfaces that mismatch early with an actionable
172172
* error telling the user how to re-authenticate with the correct scopes.
173173
*/
174-
static void validateTokenScopes(Token token, List<String> requestedScopes, String host) {
174+
static void validateTokenScopes(Token token, List<String> requestedScopes) {
175175
Map<String, Object> claims = getJwtClaims(token.getAccessToken());
176176
if (claims == null) {
177177
LOG.debug("Could not decode token as JWT to validate scopes");
@@ -204,15 +204,16 @@ static void validateTokenScopes(Token token, List<String> requestedScopes, Strin
204204
String.format(
205205
"Token issued by Databricks CLI has scopes %s which do not match "
206206
+ "the configured scopes %s. Please re-authenticate "
207-
+ "with the desired scopes by running `databricks auth login` with the --scopes flag."
207+
+ "with the desired scopes by running `databricks auth login` with the --scopes flag. "
208208
+ "Scopes default to all-apis.",
209209
sortedTokenScopes, sortedRequested));
210210
}
211211
}
212212

213213
/**
214214
* Decode a JWT access token and return its payload claims. Returns null if the token is not a
215-
* valid JWT.
215+
* valid JWT. No signature verification is performed — the token was already authenticated by the
216+
* CLI, and we only need to read the scope claim for comparison.
216217
*/
217218
private static Map<String, Object> getJwtClaims(String accessToken) {
218219
String[] parts = accessToken.split("\\.");
@@ -233,18 +234,12 @@ private static Map<String, Object> getJwtClaims(String accessToken) {
233234
}
234235

235236
/**
236-
* Parse the JWT "scope" claim, which can be either a space-delimited string or a JSON array.
237-
* Returns null if the type is unexpected.
237+
* Parse the JWT "scope" claim. Per RFC 9068, this is a space-delimited string. Returns null if
238+
* the type is unexpected.
238239
*/
239240
private static Set<String> parseScopeClaim(Object scopeClaim) {
240241
if (scopeClaim instanceof String) {
241242
return new HashSet<>(Arrays.asList(((String) scopeClaim).split("\\s+")));
242-
} else if (scopeClaim instanceof List) {
243-
Set<String> scopes = new HashSet<>();
244-
for (Object s : (List<?>) scopeClaim) {
245-
scopes.add(String.valueOf(s));
246-
}
247-
return scopes;
248243
}
249244
return null;
250245
}

databricks-sdk-java/src/test/java/com/databricks/sdk/core/DatabricksCliCredentialsProviderTest.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,4 @@ void testBuildHostArgs_UnifiedHostFalse_WithAccountHost() {
139139
CLI_PATH, "auth", "token", "--host", ACCOUNT_HOST, "--account-id", ACCOUNT_ID),
140140
cmd);
141141
}
142-
143-
@Test
144-
void testScopesExplicitlySetFlag() {
145-
DatabricksConfig config = new DatabricksConfig();
146-
assertFalse(config.isScopesExplicitlySet());
147-
148-
config.setScopes(Arrays.asList("sql", "clusters"));
149-
assertTrue(config.isScopesExplicitlySet());
150-
}
151142
}

databricks-sdk-java/src/test/java/com/databricks/sdk/core/DatabricksCliScopeValidationTest.java

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
class DatabricksCliScopeValidationTest {
1616

17-
private static final String HOST = "https://my-workspace.cloud.databricks.com";
17+
1818
private static final ObjectMapper MAPPER = new ObjectMapper();
1919

2020
/** Builds a fake JWT (header.payload.signature) with the given claims. */
@@ -62,16 +62,24 @@ static List<Arguments> scopeValidationCases() {
6262
Arrays.asList("all-apis", "offline_access"),
6363
false,
6464
"offline_access_in_config_only"),
65-
// Scope claim as list instead of string.
65+
// Order should not matter.
66+
Arguments.of(
67+
Collections.singletonMap("scope", "clusters sql"),
68+
Arrays.asList("sql", "clusters"),
69+
false,
70+
"multiple_scopes_order_independent"),
71+
// Partial overlap is still a mismatch.
72+
Arguments.of(
73+
Collections.singletonMap("scope", "sql clusters"),
74+
Arrays.asList("sql", "compute"),
75+
true,
76+
"multiple_scopes_partial_overlap_mismatch"),
77+
// No scope claim in token — validation is skipped.
6678
Arguments.of(
67-
new HashMap<String, Object>() {
68-
{
69-
put("scope", Arrays.asList("sql", "offline_access"));
70-
}
71-
},
79+
Collections.singletonMap("sub", "user@example.com"),
7280
Collections.singletonList("sql"),
7381
false,
74-
"scope_as_list"));
82+
"no_scope_claim_skips_validation"));
7583
}
7684

7785
@ParameterizedTest(name = "{3}")
@@ -87,30 +95,21 @@ void testScopeValidation(
8795
assertThrows(
8896
DatabricksCliCredentialsProvider.ScopeMismatchException.class,
8997
() ->
90-
DatabricksCliCredentialsProvider.validateTokenScopes(token, configuredScopes, HOST));
98+
DatabricksCliCredentialsProvider.validateTokenScopes(token, configuredScopes));
9199
} else {
92100
assertDoesNotThrow(
93101
() ->
94-
DatabricksCliCredentialsProvider.validateTokenScopes(token, configuredScopes, HOST));
102+
DatabricksCliCredentialsProvider.validateTokenScopes(token, configuredScopes));
95103
}
96104
}
97105

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-
107106
@Test
108107
void testNonJwtTokenSkipsValidation() {
109108
Token token = new Token("opaque-token-string", "Bearer", Instant.now().plusSeconds(3600));
110109
assertDoesNotThrow(
111110
() ->
112111
DatabricksCliCredentialsProvider.validateTokenScopes(
113-
token, Collections.singletonList("sql"), HOST));
112+
token, Collections.singletonList("sql")));
114113
}
115114

116115
@Test
@@ -121,12 +120,27 @@ void testErrorMessageContainsReauthCommand() {
121120
DatabricksCliCredentialsProvider.ScopeMismatchException.class,
122121
() ->
123122
DatabricksCliCredentialsProvider.validateTokenScopes(
124-
token, Arrays.asList("sql", "offline_access"), HOST));
123+
token, Arrays.asList("sql", "offline_access")));
125124
assertTrue(
126125
e.getMessage().contains("databricks auth login"),
127126
"Expected re-auth command in error message, got: " + e.getMessage());
128127
assertTrue(
129128
e.getMessage().contains("do not match the configured scopes"),
130129
"Expected scope mismatch details in error message, got: " + e.getMessage());
131130
}
131+
132+
@Test
133+
void testScopesExplicitlySetFlag() {
134+
DatabricksConfig config = new DatabricksConfig();
135+
assertFalse(config.isScopesExplicitlySet());
136+
137+
config.setScopes(Arrays.asList("sql", "clusters"));
138+
assertTrue(config.isScopesExplicitlySet());
139+
140+
config.setScopes(Collections.emptyList());
141+
assertFalse(config.isScopesExplicitlySet(), "Empty list should not count as explicitly set");
142+
143+
config.setScopes(null);
144+
assertFalse(config.isScopesExplicitlySet(), "null should not count as explicitly set");
145+
}
132146
}

0 commit comments

Comments
 (0)