Skip to content

Commit aacf882

Browse files
authored
Update auth layer so dryRun operations only require read-only privs (#4800)
1 parent 94d0a59 commit aacf882

File tree

5 files changed

+27
-10
lines changed

5 files changed

+27
-10
lines changed

app/src/main/java/io/apicurio/registry/auth/Authorized.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
package io.apicurio.registry.auth;
22

3+
import jakarta.enterprise.util.Nonbinding;
4+
import jakarta.interceptor.InterceptorBinding;
5+
36
import java.lang.annotation.ElementType;
47
import java.lang.annotation.Inherited;
58
import java.lang.annotation.Retention;
69
import java.lang.annotation.RetentionPolicy;
710
import java.lang.annotation.Target;
811

9-
import jakarta.enterprise.util.Nonbinding;
10-
import jakarta.interceptor.InterceptorBinding;
11-
1212
@InterceptorBinding
1313
@Inherited
1414
@Retention(RetentionPolicy.RUNTIME)
@@ -21,4 +21,7 @@
2121
@Nonbinding
2222
AuthorizedLevel level() default AuthorizedLevel.Read;
2323

24+
@Nonbinding
25+
int dryRunParam() default -1;
26+
2427
}

app/src/main/java/io/apicurio/registry/auth/AuthorizedInterceptor.java

+7-5
Original file line numberDiff line numberDiff line change
@@ -71,27 +71,29 @@ public Object authorizeMethod(InvocationContext context) throws Exception {
7171
// If the securityIdentity is not set (or is anonymous)...
7272
try {
7373
if (securityIdentity == null || securityIdentity.isAnonymous()) {
74-
System.out.println("=====> Identity was null or anon: " + securityIdentity);
75-
74+
log.debug("Identity was null or anonymous: " + securityIdentity);
75+
7676
// Anonymous users are allowed to perform "None" operations.
7777
if (annotation.level() == AuthorizedLevel.None) {
7878
log.trace("Anonymous user is being granted access to unprotected operation.");
7979
return context.proceed();
8080
}
81-
81+
8282
// Anonymous users are allowed to perform read-only operations, but only if
8383
// apicurio.auth.anonymous-read-access.enabled is set to 'true'
8484
if (authConfig.anonymousReadAccessEnabled.get() && annotation.level() == AuthorizedLevel.Read) {
8585
log.trace("Anonymous user is being granted access to read-only operation.");
8686
return context.proceed();
8787
}
88-
88+
8989
// Otherwise just fail - auth was enabled but no credentials provided.
9090
log.warn("Authentication credentials missing and required for protected endpoint.");
9191
throw new UnauthorizedException("User is not authenticated.");
9292
}
93+
} catch (UnauthorizedException e) {
94+
throw e;
9395
} catch (Throwable t) {
94-
t.printStackTrace();
96+
log.error("Error enforcing access.", t);
9597
throw t;
9698
}
9799

app/src/main/java/io/apicurio/registry/auth/RoleBasedAccessController.java

+8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ public boolean isAuthorized(InvocationContext context) {
2424
Authorized annotation = context.getMethod().getAnnotation(Authorized.class);
2525
AuthorizedLevel level = annotation.level();
2626

27+
// If the method has a "dryRun" query param set to True then downgrade the required level from Write to Read
28+
if (annotation.dryRunParam() != -1 && level == AuthorizedLevel.Write) {
29+
Boolean dryRun = (Boolean) context.getParameters()[annotation.dryRunParam()];
30+
if (dryRun != null && dryRun.equals(Boolean.TRUE)) {
31+
level = AuthorizedLevel.Read;
32+
}
33+
}
34+
2735
switch (level) {
2836
case Admin:
2937
return isAdmin();

app/src/main/java/io/apicurio/registry/rest/v3/GroupsResourceImpl.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ public void deleteArtifactsInGroup(String groupId) {
600600

601601
@Override
602602
@Audited(extractParameters = {"0", KEY_GROUP_ID, "1", KEY_IF_EXISTS, "2", KEY_CANONICAL, "3", "dryRun"})
603-
@Authorized(style = AuthorizedStyle.GroupOnly, level = AuthorizedLevel.Write)
603+
@Authorized(style = AuthorizedStyle.GroupOnly, level = AuthorizedLevel.Write, dryRunParam = 3)
604604
public CreateArtifactResponse createArtifact(String groupId, IfArtifactExists ifExists, Boolean canonical, Boolean dryRun, CreateArtifact data) {
605605
requireParameter("groupId", groupId);
606606
if (data.getFirstVersion() != null) {
@@ -763,7 +763,7 @@ public VersionSearchResults listArtifactVersions(String groupId, String artifact
763763

764764
@Override
765765
@Audited(extractParameters = {"0", KEY_GROUP_ID, "1", KEY_ARTIFACT_ID, "2", "dryRun"})
766-
@Authorized(style = AuthorizedStyle.GroupAndArtifact, level = AuthorizedLevel.Write)
766+
@Authorized(style = AuthorizedStyle.GroupAndArtifact, level = AuthorizedLevel.Write, dryRunParam = 2)
767767
public VersionMetaData createArtifactVersion(String groupId, String artifactId, Boolean dryRun, CreateVersion data) {
768768
requireParameter("content", data.getContent());
769769
requireParameter("groupId", groupId);

app/src/test/java/io/apicurio/registry/auth/SimpleAuthTest.java

+4
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ public void testReadOnly() throws Exception {
105105
client.groups().byGroupId("testReadOnly").artifacts().post(createArtifact);
106106
});
107107
assertForbidden(exception3);
108+
// Try the create again but with dryRun set to true (should work)
109+
client.groups().byGroupId("testReadOnly").artifacts().post(createArtifact, config -> {
110+
config.queryParameters.dryRun = true;
111+
});
108112

109113
var devAdapter = new VertXRequestAdapter(buildOIDCWebClient(authServerUrlConfigured, JWKSMockServer.DEVELOPER_CLIENT_ID, "test1"));
110114
devAdapter.setBaseUrl(registryV3ApiUrl);

0 commit comments

Comments
 (0)