i spent the last year just spinning
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>dev.relism</groupId>
|
||||
<artifactId>flash-extensions</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<version>1.1-indev5</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>flash-ext-oidc</artifactId>
|
||||
|
||||
@@ -295,7 +295,41 @@ public class OidcExtension implements FlashExtension {
|
||||
public java.util.List<String> requiredFor(Class<?> handlerClass) {
|
||||
return OidcAuthPolicy.openApiScopesFor(handlerClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.util.Map<Integer, String> autoResponsesFor(Class<?> handlerClass) {
|
||||
OidcAuthPolicy policy = OidcAuthPolicy.compileFromAnnotations(handlerClass);
|
||||
if (policy == null || policy.optionalAuth()) return java.util.Map.of();
|
||||
|
||||
java.util.LinkedHashMap<Integer, String> out = new java.util.LinkedHashMap<>();
|
||||
out.put(401, "Authentication required");
|
||||
|
||||
String[] roles = policy.requiredRoles();
|
||||
String[] scopes = policy.requiredScopes();
|
||||
if (roles.length == 0 && scopes.length == 0) return out;
|
||||
|
||||
String roleMessage = roles.length == 0 ? null : roleRequiredMessage(roles);
|
||||
String scopeMessage = scopes.length == 0 ? null : scopeRequiredMessage(scopes);
|
||||
if (roleMessage != null && scopeMessage != null) {
|
||||
out.put(403, roleMessage + "; " + scopeMessage);
|
||||
} else if (roleMessage != null) {
|
||||
out.put(403, roleMessage);
|
||||
} else {
|
||||
out.put(403, scopeMessage);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private static String roleRequiredMessage(String[] roles) {
|
||||
if (roles.length == 1) return "\"" + roles[0] + "\" role required";
|
||||
return "Roles \"" + String.join(", ", roles) + "\" are required";
|
||||
}
|
||||
|
||||
private static String scopeRequiredMessage(String[] scopes) {
|
||||
if (scopes.length == 1) return "\"" + scopes[0] + "\" scope required";
|
||||
return "Scopes \"" + String.join(", ", scopes) + "\" are required";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
package dev.relism.ext.oidc;
|
||||
|
||||
import dev.relism.ext.openapi.OpenApiSecurityContributor;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class OidcOpenApiInteropTest {
|
||||
|
||||
@Authenticated
|
||||
static class AuthOnly {}
|
||||
|
||||
@Authenticated(optional = true)
|
||||
static class AuthOptional {}
|
||||
|
||||
@RolesAllowed("admin")
|
||||
static class OneRole {}
|
||||
|
||||
@RolesAllowed({"admin", "operator"})
|
||||
static class MultiRole {}
|
||||
|
||||
@ScopesAllowed("orders:write")
|
||||
static class OneScope {}
|
||||
|
||||
@ScopesAllowed({"orders:write", "payments:write"})
|
||||
static class MultiScope {}
|
||||
|
||||
@RolesAllowed("admin")
|
||||
@ScopesAllowed("orders:write")
|
||||
static class RoleAndScope {}
|
||||
|
||||
@Test
|
||||
void autoResponses_authOnly() throws Exception {
|
||||
Map<Integer, String> responses = contributor().autoResponsesFor(AuthOnly.class);
|
||||
assertEquals("Authentication required", responses.get(401));
|
||||
assertFalse(responses.containsKey(403));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoResponses_optionalAuth_addsNothing() throws Exception {
|
||||
Map<Integer, String> responses = contributor().autoResponsesFor(AuthOptional.class);
|
||||
assertTrue(responses.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoResponses_oneRole_formatsSingular() throws Exception {
|
||||
Map<Integer, String> responses = contributor().autoResponsesFor(OneRole.class);
|
||||
assertEquals("Authentication required", responses.get(401));
|
||||
assertEquals("\"admin\" role required", responses.get(403));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoResponses_multiRoles_formatsPlural() throws Exception {
|
||||
Map<Integer, String> responses = contributor().autoResponsesFor(MultiRole.class);
|
||||
assertEquals("Roles \"admin, operator\" are required", responses.get(403));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoResponses_oneScope_formatsSingular() throws Exception {
|
||||
Map<Integer, String> responses = contributor().autoResponsesFor(OneScope.class);
|
||||
assertEquals("\"orders:write\" scope required", responses.get(403));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoResponses_multiScopes_formatsPlural() throws Exception {
|
||||
Map<Integer, String> responses = contributor().autoResponsesFor(MultiScope.class);
|
||||
assertEquals("Scopes \"orders:write, payments:write\" are required", responses.get(403));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoResponses_roleAndScope_combinesMessages() throws Exception {
|
||||
Map<Integer, String> responses = contributor().autoResponsesFor(RoleAndScope.class);
|
||||
assertEquals("\"admin\" role required; \"orders:write\" scope required", responses.get(403));
|
||||
}
|
||||
|
||||
private static OpenApiSecurityContributor contributor() throws Exception {
|
||||
Class<?> clazz = Class.forName("dev.relism.ext.oidc.OidcExtension$OpenApiIntegration");
|
||||
Constructor<?> ctor = clazz.getDeclaredConstructor();
|
||||
ctor.setAccessible(true);
|
||||
Object instance = ctor.newInstance();
|
||||
|
||||
Method m = clazz.getDeclaredMethod("register", dev.relism.extension.FlashContext.class, OidcConfig.class, OidcProviderMetadata.class);
|
||||
m.setAccessible(true);
|
||||
|
||||
dev.relism.extension.FlashContext ctx = new dev.relism.extension.FlashContext();
|
||||
dev.relism.ext.openapi.OpenApiSecurityRegistry registry = new dev.relism.ext.openapi.OpenApiSecurityRegistry();
|
||||
ctx.provide(dev.relism.ext.openapi.OpenApiSecurityRegistry.class, registry);
|
||||
|
||||
OidcConfig config = OidcConfig.builder("https://issuer", "c", "s", "/cb").build();
|
||||
OidcProviderMetadata meta = new OidcProviderMetadata("a", "t", "u", "j", "e");
|
||||
m.invoke(instance, ctx, config, meta);
|
||||
|
||||
return registry.contributors().getFirst();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user