This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch 4.1.x-fixes in repository https://gitbox.apache.org/repos/asf/cxf.git
commit c8bda469698739e85335e534ef4785792ba70c6d Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Fri Oct 30 14:21:53 2026 +0000 Resolve ClaimsAuthorizingInterceptor annotation claims by method sign… (#3646) * Resolve ClaimsAuthorizingInterceptor annotation claims by method signature * Resolve ClaimsAuthorizingInterceptor claims by method signature (cherry picked from commit 7816e8e7022fb1a56f382e06fdcce9069797ef7c) --- .../interceptor/ClaimsAuthorizingInterceptor.java | 115 ++++- .../ClaimsAuthorizingInterceptorTest.java | 508 ++++++++++++++++++++- 2 files changed, 603 insertions(+), 20 deletions(-) diff --git a/rt/security/src/main/java/org/apache/cxf/rt/security/claims/interceptor/ClaimsAuthorizingInterceptor.java b/rt/security/src/main/java/org/apache/cxf/rt/security/claims/interceptor/ClaimsAuthorizingInterceptor.java index 94a9c9d7b11..e61755804ba 100644 --- a/rt/security/src/main/java/org/apache/cxf/rt/security/claims/interceptor/ClaimsAuthorizingInterceptor.java +++ b/rt/security/src/main/java/org/apache/cxf/rt/security/claims/interceptor/ClaimsAuthorizingInterceptor.java @@ -31,6 +31,7 @@ import java.util.logging.Logger; import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.util.ClassHelper; +import org.apache.cxf.common.util.ReflectionUtil; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.interceptor.security.AccessDeniedException; import org.apache.cxf.message.Message; @@ -53,7 +54,18 @@ public class ClaimsAuthorizingInterceptor extends AbstractPhaseInterceptor<Messa private static final Set<String> SKIP_METHODS = new HashSet<>( Arrays.asList("wait", "notify", "notifyAll", "equals", "toString", "hashCode")); + // Claims configured explicitly via setClaims, keyed by method name private Map<String, List<ClaimBean>> claims = new HashMap<>(); + // The method-level and class-level @Claim/@Claims declarations which apply to each method + // signature in the secured object's hierarchy. Keying by signature means that overloaded + // methods do not share a rule + private final Map<String, List<ClaimsDeclaration>> declarationsBySignature = new HashMap<>(); + // The erased signatures a method overrides with a different signature, e.g. update(Object) + // for update(String) implementing update(T) of Repository<String> + private final Map<String, Set<String>> overriddenSignatures = new HashMap<>(); + private final Map<String, Set<String>> signaturesByName = new HashMap<>(); + private final Set<Class<?>> securedClasses = new HashSet<>(); + private boolean annotationClaimsFound; private Map<String, String> nameAliases = Collections.emptyMap(); private Map<String, String> formatAliases = Collections.emptyMap(); @@ -82,7 +94,63 @@ public class ClaimsAuthorizingInterceptor extends AbstractPhaseInterceptor<Messa } protected boolean authorize(ClaimsSecurityContext sc, Method method) { - List<ClaimBean> list = claims.get(method.getName()); + String signature = ReflectionUtil.getMethodSignature(method); + boolean securedMethod = declarationsBySignature.containsKey(signature); + Set<String> signatures = new HashSet<>(); + if (securedMethod) { + signatures.add(signature); + signatures.addAll(overriddenSignatures.getOrDefault(signature, Collections.emptySet())); + } else { + // The method is not one of the secured object's methods, so every rule + // recorded for its name applies + signatures.addAll(signaturesByName.getOrDefault(method.getName(), Collections.emptySet())); + } + List<ClaimBean> requiredClaims = new ArrayList<>(); + for (String s : signatures) { + requiredClaims.addAll(getAnnotationClaims(s)); + } + List<ClaimBean> configuredClaims = claims.get(method.getName()); + if (configuredClaims != null) { + requiredClaims.addAll(configuredClaims); + } else if (!securedMethod && requiredClaims.isEmpty()) { + // A method without claims requirements is only permitted if it is one of + // the secured object's methods + return false; + } + // Every claim which applies to the method has to be matched + return isAuthorized(sc, requiredClaims); + } + + private List<ClaimBean> getAnnotationClaims(String signature) { + // Every @Claim which applies to the method has to be met, from any class or interface + // in the hierarchy. A class-level @Claim is replaced by a method-level @Claim of the same + // name and format declared on the same class or interface, or on a subclass of it + List<ClaimsDeclaration> declarations = + declarationsBySignature.getOrDefault(signature, Collections.emptyList()); + List<ClaimBean> requiredClaims = new ArrayList<>(); + for (ClaimsDeclaration declaration : declarations) { + for (ClaimBean bean : declaration.claims) { + if (declaration.methodLevel + || !isClaimOverridden(bean, declaration.declaringClass, declarations)) { + requiredClaims.add(bean); + } + } + } + return requiredClaims; + } + + private static boolean isClaimOverridden(ClaimBean bean, Class<?> cls, + List<ClaimsDeclaration> declarations) { + for (ClaimsDeclaration declaration : declarations) { + if (declaration.methodLevel && cls.isAssignableFrom(declaration.declaringClass) + && isClaimOverridden(bean, declaration.claims)) { + return true; + } + } + return false; + } + + private static boolean isAuthorized(ClaimsSecurityContext sc, List<ClaimBean> list) { org.apache.cxf.rt.security.claims.ClaimCollection actualClaims = sc.getClaims(); for (ClaimBean claimBean : list) { @@ -129,13 +197,13 @@ public class ClaimsAuthorizingInterceptor extends AbstractPhaseInterceptor<Messa public void setSecuredObject(Object object) { Class<?> cls = ClassHelper.getRealClass(object); findClaims(cls); - if (claims.isEmpty()) { + if (!annotationClaimsFound) { LOG.warning("The claims list is empty, the service object is not protected"); } } protected void findClaims(Class<?> cls) { - if (cls == null || cls == Object.class) { + if (cls == null || cls == Object.class || !securedClasses.add(cls)) { return; } List<ClaimBean> clsClaims = @@ -147,26 +215,26 @@ public class ClaimsAuthorizingInterceptor extends AbstractPhaseInterceptor<Messa List<ClaimBean> methodClaims = getClaims(m.getAnnotation(Claims.class), m.getAnnotation(Claim.class)); - List<ClaimBean> allClaims = new ArrayList<>(methodClaims); - for (ClaimBean bean : clsClaims) { - if (isClaimOverridden(bean, methodClaims)) { - continue; + String signature = ReflectionUtil.getMethodSignature(m); + List<ClaimsDeclaration> declarations = + declarationsBySignature.computeIfAbsent(signature, k -> new ArrayList<>()); + if (!methodClaims.isEmpty()) { + declarations.add(new ClaimsDeclaration(m.getDeclaringClass(), methodClaims, true)); + } + if (!clsClaims.isEmpty()) { + declarations.add(new ClaimsDeclaration(cls, clsClaims, false)); + } + signaturesByName.computeIfAbsent(m.getName(), k -> new HashSet<>()).add(signature); + annotationClaimsFound |= !methodClaims.isEmpty() || !clsClaims.isEmpty(); + if (!m.isBridge()) { + for (Method overridden : ReflectionUtil.getGenericallyOverriddenMethods(cls, m)) { + overriddenSignatures.computeIfAbsent(signature, k -> new HashSet<>()) + .add(ReflectionUtil.getMethodSignature(overridden)); } - allClaims.add(bean); } - - claims.put(m.getName(), allClaims); - } - if (!claims.isEmpty()) { - return; } findClaims(cls.getSuperclass()); - - if (!claims.isEmpty()) { - return; - } - for (Class<?> interfaceCls : cls.getInterfaces()) { findClaims(interfaceCls); } @@ -224,4 +292,15 @@ public class ClaimsAuthorizingInterceptor extends AbstractPhaseInterceptor<Messa this.formatAliases = formatAliases; } + private static final class ClaimsDeclaration { + private final Class<?> declaringClass; + private final List<ClaimBean> claims; + private final boolean methodLevel; + + ClaimsDeclaration(Class<?> declaringClass, List<ClaimBean> claims, boolean methodLevel) { + this.declaringClass = declaringClass; + this.claims = claims; + this.methodLevel = methodLevel; + } + } } diff --git a/rt/security/src/test/java/org/apache/cxf/rt/security/claims/interceptor/ClaimsAuthorizingInterceptorTest.java b/rt/security/src/test/java/org/apache/cxf/rt/security/claims/interceptor/ClaimsAuthorizingInterceptorTest.java index 2a2e19db945..c75e9d63e0d 100644 --- a/rt/security/src/test/java/org/apache/cxf/rt/security/claims/interceptor/ClaimsAuthorizingInterceptorTest.java +++ b/rt/security/src/test/java/org/apache/cxf/rt/security/claims/interceptor/ClaimsAuthorizingInterceptorTest.java @@ -22,6 +22,7 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.lang.reflect.Method; import java.security.Principal; import java.util.Arrays; import java.util.Collections; @@ -226,6 +227,275 @@ public class ClaimsAuthorizingInterceptorTest { } } + @Test + public void testOverloadedMethodWithoutClaimsDoesNotRemoveClaims() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new OverloadingService()); + Method adminMethod = OverloadingService.class.getMethod("delete", String.class); + Method unsecuredMethod = OverloadingService.class.getMethod("delete"); + + in.handleMessage(prepareMessage(adminMethod, createDefaultClaim("admin"))); + assertDenied(in, prepareMessage(adminMethod, createDefaultClaim("user"))); + + // A method without claims of its own is not secured by the claims of its overloads + in.handleMessage(prepareMessage(unsecuredMethod, createDefaultClaim("user"))); + } + + @Test + public void testOverloadedMethodsKeepTheirOwnClaims() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new OverloadingService()); + Method adminMethod = OverloadingService.class.getMethod("update", String.class); + Method userMethod = OverloadingService.class.getMethod("update"); + + in.handleMessage(prepareMessage(adminMethod, createDefaultClaim("admin"))); + assertDenied(in, prepareMessage(adminMethod, createDefaultClaim("user"))); + in.handleMessage(prepareMessage(userMethod, createDefaultClaim("user"))); + assertDenied(in, prepareMessage(userMethod, createDefaultClaim("admin"))); + } + + @Test + public void testOverridingMethodClaimsApplyTogetherWithSuperclassClaims() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new OverridingService()); + Method method = OverridingService.class.getMethod("update"); + + in.handleMessage(prepareMessage(method, createDefaultClaim("admin", "user"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("admin"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("user"))); + } + + @Test + public void testOverridingMethodCannotWeakenSuperclassClaims() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new UserOverridingService()); + Method method = UserOverridingService.class.getMethod("update"); + + in.handleMessage(prepareMessage(method, createDefaultClaim("admin", "user"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("user"))); + } + + @Test + public void testSubclassClaimsApplyTogetherWithSuperclassMethodClaims() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new AdminSubService()); + Method method = AdminSubService.class.getMethod("update"); + + in.handleMessage(prepareMessage(method, createDefaultClaim("admin", "user"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("admin"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("user"))); + } + + @Test + public void testMethodClaimsFromUnrelatedSupertypesApplyTogether() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new AdminApiUserService()); + Method method = AdminApiUserService.class.getMethod("update"); + + in.handleMessage(prepareMessage(method, createDefaultClaim("admin", "user"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("admin"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("user"))); + } + + @Test + public void testInterfaceClaimsAreEnforced() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new InterfaceService()); + Method implMethod = InterfaceService.class.getMethod("delete"); + Method interfaceMethod = SecuredApi.class.getMethod("delete"); + + in.handleMessage(prepareMessage(implMethod, createDefaultClaim("admin"))); + assertDenied(in, prepareMessage(implMethod, createDefaultClaim("user"))); + assertDenied(in, prepareMessage(interfaceMethod, createDefaultClaim("user"))); + } + + @Test + public void testGenericInterfaceMethodClaimsAreEnforced() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new StringRepository()); + Method updateMethod = StringRepository.class.getMethod("update", String.class); + + // update(String) overrides the erased update(Object), but not the update() overload + in.handleMessage(prepareMessage(updateMethod, createDefaultClaim("admin"))); + assertDenied(in, prepareMessage(updateMethod, createDefaultClaim("user"))); + + Method userMethod = StringRepository.class.getMethod("update"); + in.handleMessage(prepareMessage(userMethod, createDefaultClaim("user"))); + assertDenied(in, prepareMessage(userMethod, createDefaultClaim("admin"))); + } + + @Test + public void testGenericInterfaceMethodClaimsApplyTogetherWithClassClaims() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new ClassClaimsStringRepository()); + Method method = ClassClaimsStringRepository.class.getMethod("update", String.class); + + in.handleMessage(prepareMessage(method, createDefaultClaim("admin", "user"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("admin"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("user"))); + } + + @Test + public void testGenericInterfaceMethodClaimsApplyToInheritedImplementation() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new InheritingStringRepository()); + Method method = InheritingStringRepository.class.getMethod("update", String.class); + + in.handleMessage(prepareMessage(method, createDefaultClaim("admin"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("user"))); + } + + @Test + public void testMethodWithoutConfiguredClaimsIsDenied() throws Exception { + ClaimsAuthorizingInterceptor in = new ClaimsAuthorizingInterceptor(); + in.setClaims(Collections.singletonMap("update", + Collections.singletonList(createDefaultClaimBean("admin")))); + + in.handleMessage(prepareMessage(OverloadingService.class.getMethod("update"), + createDefaultClaim("admin"))); + assertDenied(in, prepareMessage(OverloadingService.class.getMethod("delete"), + createDefaultClaim("admin"))); + } + + @Test + public void testConfiguredClaimsAreEnforcedTogetherWithAnnotations() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new OverloadingService()); + in.setClaims(Collections.singletonMap("update", + Collections.singletonList(createDefaultClaimBean("manager")))); + Method method = OverloadingService.class.getMethod("update"); + + in.handleMessage(prepareMessage(method, createDefaultClaim("user", "manager"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("user"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("manager"))); + } + + @Test + public void testOverloadedSubclassMethodKeepsItsOwnClaims() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new OverloadingSubService()); + Method adminMethod = OverloadingSubService.class.getMethod("update", String.class); + Method userMethod = OverloadingSubService.class.getMethod("update"); + + in.handleMessage(prepareMessage(adminMethod, createDefaultClaim("admin"))); + assertDenied(in, prepareMessage(adminMethod, createDefaultClaim("user"))); + in.handleMessage(prepareMessage(userMethod, createDefaultClaim("user"))); + assertDenied(in, prepareMessage(userMethod, createDefaultClaim("admin"))); + } + + @Test + public void testOverridingMethodClaimsReplaceSuperclassClassClaims() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new OverridingSubResource()); + Method method = OverridingSubResource.class.getMethod("update"); + + in.handleMessage(prepareMessage(method, createDefaultClaim("admin"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("user"))); + } + + @Test + public void testOverridingMethodClaimsReplaceInterfaceClassClaims() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new UserApiAdminService()); + Method method = UserApiAdminService.class.getMethod("update"); + + in.handleMessage(prepareMessage(method, createDefaultClaim("admin"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("user"))); + } + + @Test + public void testClassClaimsOfOtherTypesStillApply() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new LocationSubService()); + Method method = LocationSubService.class.getMethod("update"); + org.apache.cxf.rt.security.claims.Claim location = createClaim("http://location", + "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified", "UK"); + + in.handleMessage(prepareMessage(method, createDefaultClaim("admin"), location)); + assertDenied(in, prepareMessage(method, createDefaultClaim("admin"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("user"), location)); + } + + @Test + public void testMethodClaimsReplaceSuperclassClassClaimsForUnannotatedOverride() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new UnannotatedSubResource()); + Method method = UnannotatedSubResource.class.getMethod("update"); + + in.handleMessage(prepareMessage(method, createDefaultClaim("admin"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("user"))); + } + + @Test + public void testSuperclassClassClaimsDoNotApplyToSubclassMethods() throws Exception { + // @Claim is not @Inherited, so a class-level @Claim only applies to the methods of + // the class which declares it + ClaimsAuthorizingInterceptor in = createInterceptor(new SubclassOnlyService()); + Method adminMethod = SubclassOnlyService.class.getMethod("adminOperation"); + + in.handleMessage(prepareMessage(adminMethod, createDefaultClaim("admin"))); + assertDenied(in, prepareMessage(adminMethod, createDefaultClaim("user"))); + in.handleMessage(prepareMessage(SubclassOnlyService.class.getMethod("update"), + createDefaultClaim("user"))); + } + + @Test + public void testClaimMatchAllIsResolvedPerSignature() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new MatchAllService()); + Method matchAllMethod = MatchAllService.class.getMethod("update", String.class); + Method matchAnyMethod = MatchAllService.class.getMethod("update"); + + in.handleMessage(prepareMessage(matchAllMethod, createDefaultClaim("admin", "manager"))); + assertDenied(in, prepareMessage(matchAllMethod, createDefaultClaim("admin"))); + in.handleMessage(prepareMessage(matchAnyMethod, createDefaultClaim("user"))); + } + + @Test + public void testInheritedMethodClaimsStillApply() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new InheritingService()); + Method method = InheritingService.class.getMethod("update"); + + in.handleMessage(prepareMessage(method, createDefaultClaim("user"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("admin"))); + } + + @Test + public void testUnannotatedOverrideInheritsSuperclassClaims() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new UnannotatedOverridingService()); + Method method = UnannotatedOverridingService.class.getMethod("update"); + + in.handleMessage(prepareMessage(method, createDefaultClaim("user"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("admin"))); + } + + @Test + public void testUnrelatedOverloadIsNotLinkedToGenericInterfaceMethod() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new OverloadingStringRepository()); + Method integerMethod = OverloadingStringRepository.class.getMethod("update", Integer.class); + Method stringMethod = OverloadingStringRepository.class.getMethod("update", String.class); + + in.handleMessage(prepareMessage(integerMethod, createDefaultClaim("user"))); + assertDenied(in, prepareMessage(integerMethod, createDefaultClaim("admin"))); + in.handleMessage(prepareMessage(stringMethod, createDefaultClaim("admin"))); + assertDenied(in, prepareMessage(stringMethod, createDefaultClaim("user"))); + } + + @Test + public void testGenericInterfaceMethodClaimsApplyThroughTypeVariables() throws Exception { + ClaimsAuthorizingInterceptor in = createInterceptor(new TypedStringRepository()); + Method method = TypedStringRepository.class.getMethod("update", String.class); + + in.handleMessage(prepareMessage(method, createDefaultClaim("admin"))); + assertDenied(in, prepareMessage(method, createDefaultClaim("user"))); + } + + private static ClaimsAuthorizingInterceptor createInterceptor(Object service) { + ClaimsAuthorizingInterceptor in = new ClaimsAuthorizingInterceptor(); + in.setSecuredObject(service); + return in; + } + + private static void assertDenied(ClaimsAuthorizingInterceptor in, Message m) { + try { + in.handleMessage(m); + fail("AccessDeniedException expected"); + } catch (AccessDeniedException ex) { + // expected + } + } + + private static ClaimBean createDefaultClaimBean(String value) { + org.apache.cxf.rt.security.claims.Claim claim = new org.apache.cxf.rt.security.claims.Claim(); + claim.setClaimType(SAMLClaim.SAML_ROLE_ATTRIBUTENAME_DEFAULT); + claim.addValue(value); + return new ClaimBean(claim, "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified", + ClaimMode.STRICT, false); + } + private void doTestClaims(String methodName, org.apache.cxf.rt.security.claims.Claim... claim) throws Exception { @@ -245,6 +515,17 @@ public class ClaimsAuthorizingInterceptorTest { String roleName, org.apache.cxf.rt.security.claims.Claim... claim) throws Exception { + return prepareMessage(cls.getMethod(methodName, new Class[]{}), roleName, claim); + } + + private Message prepareMessage(Method method, + org.apache.cxf.rt.security.claims.Claim... claim) { + return prepareMessage(method, SAMLClaim.SAML_ROLE_ATTRIBUTENAME_DEFAULT, claim); + } + + private Message prepareMessage(Method method, + String roleName, + org.apache.cxf.rt.security.claims.Claim... claim) { ClaimCollection claims = new ClaimCollection(); Collections.addAll(claims, claim); @@ -293,8 +574,7 @@ public class ClaimsAuthorizingInterceptorTest { Message m = new MessageImpl(); m.setExchange(new ExchangeImpl()); m.put(SecurityContext.class, sc); - m.put("org.apache.cxf.resource.method", - cls.getMethod(methodName, new Class[]{})); + m.put("org.apache.cxf.resource.method", method); return m; } @@ -366,6 +646,230 @@ public class ClaimsAuthorizingInterceptorTest { } } + public static class OverloadingService { + @Claim("admin") + public void delete(String id) { + + } + + public void delete() { + + } + + @Claim("admin") + public void update(String value) { + + } + + @Claim("user") + public void update() { + + } + } + public static class BaseService { + @Claim("user") + public void update() { + + } + } + public static class OverridingService extends BaseService { + @Override + @Claim("admin") + public void update() { + + } + } + public static class AdminBaseService { + @Claim("admin") + public void update() { + + } + } + public static class UserOverridingService extends AdminBaseService { + @Override + @Claim("user") + public void update() { + + } + } + @Claim("admin") + public static class AdminSubService extends BaseService { + @Override + public void update() { + + } + } + public interface AdminApi { + @Claim("admin") + void update(); + } + public static class AdminApiUserService extends BaseService implements AdminApi { + @Override + public void update() { + + } + } + public interface SecuredApi { + @Claim("admin") + void delete(); + } + public static class InterfaceService implements SecuredApi { + @Override + public void delete() { + + } + } + public interface Repository<T> { + @Claim("admin") + void update(T value); + + @Claim("user") + void update(); + } + public static class StringRepository implements Repository<String> { + @Override + public void update(String value) { + + } + + @Override + public void update() { + + } + } + @Claim("user") + public static class ClassClaimsStringRepository implements Repository<String> { + @Override + public void update(String value) { + + } + + @Override + public void update() { + + } + } + public static class StringUpdater { + public void update(String value) { + + } + + public void update() { + + } + } + public static class InheritingStringRepository extends StringUpdater implements Repository<String> { + } + public static class OverloadingSubService extends BaseService { + @Claim("admin") + public void update(String value) { + + } + } + @Claim("user") + public static class ClaimsBaseResource { + public void update() { + + } + } + public static class OverridingSubResource extends ClaimsBaseResource { + @Override + @Claim("admin") + public void update() { + + } + } + public static class SubclassOnlyService extends ClaimsBaseResource { + @Override + public void update() { + + } + + @Claim("admin") + public void adminOperation() { + + } + } + @Claim("user") + public interface UserApi { + void update(); + } + public static class UserApiAdminService implements UserApi { + @Override + @Claim("admin") + public void update() { + + } + } + @Claim(name = "http://location", value = "UK") + public static class LocationBaseService { + public void update() { + + } + } + public static class LocationSubService extends LocationBaseService { + @Override + @Claim("admin") + public void update() { + + } + } + @Claim("user") + public static class ClaimsAndMethodBaseResource { + @Claim("admin") + public void update() { + + } + } + public static class UnannotatedSubResource extends ClaimsAndMethodBaseResource { + @Override + public void update() { + + } + } + public static class MatchAllService extends BaseService { + @Claim(value = {"admin", "manager" }, matchAll = true) + public void update(String value) { + + } + } + public static class InheritingService extends BaseService { + } + public static class UnannotatedOverridingService extends BaseService { + @Override + public void update() { + + } + } + public static class OverloadingStringRepository implements Repository<String> { + @Override + public void update(String value) { + + } + + @Override + public void update() { + + } + + @Claim("user") + public void update(Integer value) { + + } + } + public abstract static class AbstractRepository<T> implements Repository<T> { // NOPMD + } + public static class TypedStringRepository extends AbstractRepository<String> { + @Override + public void update(String value) { + + } + + @Override + public void update() { + + } + } @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface SecureRole {
