Skip to content

Commit 2700890

Browse files
authored
Merge pull request quarkusio#36361 from sberyozkin/fix_oidc_scope_to_permission_conversion
Check semicolon during the OIDC scope to permission conversion
2 parents ea956d0 + ded3836 commit 2700890

File tree

4 files changed

+42
-9
lines changed

4 files changed

+42
-9
lines changed

extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcUtils.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,11 +302,17 @@ public Uni<Boolean> apply(Permission requiredPermission) {
302302
}
303303
}
304304

305-
private static Permission[] transformScopesToPermissions(Collection<String> scopes) {
305+
static Permission[] transformScopesToPermissions(Collection<String> scopes) {
306306
final Permission[] permissions = new Permission[scopes.size()];
307307
int i = 0;
308308
for (String scope : scopes) {
309-
permissions[i++] = new StringPermission(scope);
309+
int semicolonIndex = scope.indexOf(':');
310+
if (semicolonIndex > 0 && semicolonIndex < scope.length() - 1) {
311+
permissions[i++] = new StringPermission(scope.substring(0, semicolonIndex),
312+
scope.substring(semicolonIndex + 1));
313+
} else {
314+
permissions[i++] = new StringPermission(scope);
315+
}
310316
}
311317
return permissions;
312318
}

extensions/oidc/runtime/src/test/java/io/quarkus/oidc/runtime/OidcUtilsTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.io.InputStream;
1212
import java.io.InputStreamReader;
1313
import java.nio.charset.StandardCharsets;
14+
import java.security.Permission;
1415
import java.util.Collections;
1516
import java.util.List;
1617
import java.util.stream.Collectors;
@@ -664,6 +665,22 @@ public void testDecodeJwt() throws Exception {
664665
assertTrue(json.containsKey("jti"));
665666
}
666667

668+
@Test
669+
public void testTransformScopeToPermission() throws Exception {
670+
Permission[] perms = OidcUtils.transformScopesToPermissions(
671+
List.of("read", "read:d", "read:", ":read"));
672+
assertEquals(4, perms.length);
673+
674+
assertEquals("read", perms[0].getName());
675+
assertNull(perms[0].getActions());
676+
assertEquals("read", perms[1].getName());
677+
assertEquals("d", perms[1].getActions());
678+
assertEquals("read:", perms[2].getName());
679+
assertNull(perms[2].getActions());
680+
assertEquals(":read", perms[3].getName());
681+
assertNull(perms[3].getActions());
682+
}
683+
667684
public static JsonObject read(InputStream input) throws IOException {
668685
try (BufferedReader buffer = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {
669686
return new JsonObject(buffer.lines().collect(Collectors.joining("\n")));

integration-tests/oidc-tenancy/src/main/java/io/quarkus/it/keycloak/ServiceProtectedResource.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.eclipse.microprofile.jwt.JsonWebToken;
88

99
import io.quarkus.security.Authenticated;
10+
import io.quarkus.security.PermissionsAllowed;
1011

1112
@Path("/service/tenant-public-key")
1213
@Authenticated
@@ -16,6 +17,7 @@ public class ServiceProtectedResource {
1617
JsonWebToken accessToken;
1718

1819
@GET
20+
@PermissionsAllowed("read:data")
1921
public String getName() {
2022
return "tenant-public-key" + ":" + accessToken.getName();
2123
}
Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package io.quarkus.it.keycloak;
22

3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
35
import java.io.IOException;
46
import java.time.Instant;
57

6-
import org.junit.jupiter.api.Assertions;
78
import org.junit.jupiter.api.Test;
89

910
import io.quarkus.test.junit.QuarkusTest;
@@ -15,21 +16,28 @@
1516
public class ServicePublicKeyTestCase {
1617

1718
@Test
18-
public void testAccessTokenInjection() throws IOException, InterruptedException {
19-
String jwt = Jwt.claims().preferredUserName("alice").sign();
20-
Assertions.assertEquals("tenant-public-key:alice", RestAssured.given().auth()
19+
public void testAccessTokenInjection() {
20+
String jwt = Jwt.claim("scope", "read:data").preferredUserName("alice").sign();
21+
assertEquals("tenant-public-key:alice", RestAssured.given().auth()
2122
.oauth2(jwt)
2223
.get("/service/tenant-public-key").getBody().asString());
2324
}
2425

2526
@Test
26-
public void testModifiedSignature() throws IOException, InterruptedException {
27+
public void testAccessTokenInjection403() {
28+
String jwt = Jwt.claim("scope", "read:doc").preferredUserName("alice").sign();
29+
RestAssured.given().auth().oauth2(jwt)
30+
.get("/service/tenant-public-key").then().statusCode(403);
31+
}
32+
33+
@Test
34+
public void testModifiedSignature() {
2735
String jwt = Jwt.claims().preferredUserName("alice").sign();
2836
// the last section of the jwt token is a signature
2937
Response r = RestAssured.given().auth()
3038
.oauth2(jwt + "1")
3139
.get("/service/tenant-public-key");
32-
Assertions.assertEquals(401, r.getStatusCode());
40+
assertEquals(401, r.getStatusCode());
3341
}
3442

3543
@Test
@@ -39,6 +47,6 @@ public void testExpiredToken() throws IOException, InterruptedException {
3947
Response r = RestAssured.given().auth()
4048
.oauth2(jwt)
4149
.get("/service/tenant-public-key");
42-
Assertions.assertEquals(401, r.getStatusCode());
50+
assertEquals(401, r.getStatusCode());
4351
}
4452
}

0 commit comments

Comments
 (0)