{"record":{"id":"ab5947fc662f25dd","repo":"quarkusio/quarkus","slug":"retrieving-all-roles-not-supported-when-jax-rs-sec","errorCode":null,"errorMessage":"retrieving all roles not supported when JAX-RS security context has been replaced","messagePattern":"retrieving all roles not supported when JAX-RS security context has been replaced","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/SecurityContextFilter.java","lineNumber":65,"sourceCode":"            return;\n        }\n        Set<Credential> oldCredentials = old.getCredentials();\n        Set<Permission> oldPermissions = old.getPermissions();\n        Map<String, Object> oldAttributes = old.getAttributes();\n        SecurityIdentity newIdentity = new SecurityIdentity() {\n            @Override\n            public Principal getPrincipal() {\n                return modified.getUserPrincipal();\n            }\n\n            @Override\n            public boolean isAnonymous() {\n                return modified.getUserPrincipal() == null;\n            }\n\n            @Override\n            public Set<String> getRoles() {\n                throw new UnsupportedOperationException(\n                        \"retrieving all roles not supported when JAX-RS security context has been replaced\");\n            }\n\n            @Override\n            public boolean hasRole(String role) {\n                return modified.isUserInRole(role);\n            }\n\n            @Override\n            public <T extends Credential> T getCredential(Class<T> credentialType) {\n                for (Credential cred : getCredentials()) {\n                    if (credentialType.isAssignableFrom(cred.getClass())) {\n                        return (T) cred;\n                    }\n                }\n                return null;\n            }\n","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/SecurityContextFilter.java#L47-L83","documentation":"SecurityContextFilter wraps a developer-supplied JAX-RS SecurityContext (one replacing the Quarkus-managed one) in an adapter whose getRoles() cannot enumerate roles, because the replaced context only supports point-wise role checks via isUserInRole. Quarkus throws UnsupportedOperationException to signal that retrieving the full role set is impossible with such a custom context.","triggerScenarios":"Code calls SecurityIdentity.getRoles() (or equivalent role enumeration) on a SecurityContext installed via SecurityContextFilter / a custom @Context SecurityContext replacement in a JAX-RS resource; i.e. any API that requests ALL roles instead of checking a single role via hasRole(role).","commonSituations":"Developers replace the JAX-RS SecurityContext (e.g. to integrate a custom auth scheme) and then use frameworks or monitoring code that enumerate roles, such as SecurityIdentity.getRoles(), authorization policies that inspect role sets, or debug endpoints that dump the caller's roles.","solutions":["Do not enumerate roles; call hasRole(role) / isUserInRole(role) for each specific role you need to check.","If you need the full role set, avoid replacing the JAX-RS SecurityContext and instead integrate via Quarkus SecurityIdentity / IdentityProvider so roles are backed by a real identity.","If you must replace the context, wrap a SecurityIdentity that carries the actual roles so the adapter can delegate getRoles() instead of throwing."],"exampleFix":"// before: enumerating roles from a replaced security context\nSet<String> roles = securityContext.getRoles();\n\n// after: point-wise check\ntenantAllowed = securityContext.isUserInRole(\"tenant-admin\");","handlingStrategy":"fallback","validationCode":"boolean canEnumerate = !(securityContext instanceof io.quarkus.resteasy.runtime.SecurityContextFilter.WrappedSecurityContext);\n// or simply: prefer hasRole checks\nboolean isAdmin = securityContext.isUserInRole(\"admin\");","typeGuard":"boolean supportsRoleEnumeration(jakarta.ws.rs.core.SecurityContext ctx) {\n    // replaced contexts delegate point-wise only\n    return ctx.getClass().getName().startsWith(\"io.quarkus\") && !ctx.getClass().getSimpleName().contains(\"Wrapped\");\n}","tryCatchPattern":"try {\n    roles = securityIdentity.getRoles();\n} catch (UnsupportedOperationException e) {\n    roles = Set.of(); // or derive via candidate role list + hasRole\n}","preventionTips":["Never enumerate roles from a replaced SecurityContext; always use isUserInRole/hasRole.","Integrate custom auth via SecurityIdentity/IdentityProvider instead of swapping SecurityContext.","Document which role-check style your custom context supports."],"tags":["jaxrs","security","unsupported-operation"],"backgroundTag":"security-context-roles-unsupported","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}