{"record":{"id":"2923cd952b7f8827","repo":"apereo/cas","slug":"impersonating-s-is-not-allowed","errorCode":null,"errorMessage":"Impersonating %s is not allowed","messagePattern":"Impersonating (.+?) is not allowed","errorType":"exception","errorClass":"SurrogateAuthenticationException","httpStatus":null,"severity":"error","filePath":"support/cas-server-support-surrogate-core/src/main/java/org/apereo/cas/ticket/SurrogateServiceTicketGeneratorAuthority.java","lineNumber":62,"sourceCode":"        LOGGER.debug(\"Checking if service ticket generation is allowed for [{}] and [{}]\", authentication, service);\n        return findSurrogateCredentialTrait(authentication).isPresent();\n    }\n\n    @Override\n    public boolean shouldGenerate(final AuthenticationResult authenticationResult, final Service service) throws Throwable {\n        val authentication = authenticationResult.getAuthentication();\n        val result = findSurrogateCredentialTrait(authentication);\n        if (result.isPresent()) {\n            val pair = result.get();\n            val givenService = serviceSelectionPlan.resolveService(service);\n            val principal = resolvedPrincipal(pair.getKey().getId());\n            val surrogateUser = pair.getRight().getSurrogateUsername();\n            LOGGER.debug(\"Checking if [{}] can impersonate [{}] for service [{}]\", principal, surrogateUser, givenService);\n            if (surrogateAuthenticationService.canImpersonate(surrogateUser, principal, Optional.ofNullable(givenService))) {\n                return true;\n            }\n            LOGGER.warn(\"Impersonation is not allowed for [{}]\", surrogateUser);\n            throw new SurrogateAuthenticationException(\"Impersonating %s is not allowed\".formatted(surrogateUser));\n        }\n        return true;\n    }\n\n    protected Optional<Pair<Credential, SurrogateCredentialTrait>> findSurrogateCredentialTrait(\n        final Authentication authentication) {\n        return authentication.getCredentials()\n            .stream()\n            .filter(Objects::nonNull)\n            .filter(credential -> Objects.nonNull(credential.getCredentialMetadata()))\n            .filter(credential -> credential.getCredentialMetadata().getTrait(SurrogateCredentialTrait.class).isPresent())\n            .map(credential -> {\n                val credentialTrait = credential.getCredentialMetadata().getTrait(SurrogateCredentialTrait.class).orElseThrow();\n                return Pair.of(credential, credentialTrait);\n            })\n            .findFirst();\n    }\n","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-surrogate-core/src/main/java/org/apereo/cas/ticket/SurrogateServiceTicketGeneratorAuthority.java#L44-L80","documentation":"SurrogateServiceTicketGeneratorAuthority.shouldGenerate throws SurrogateAuthenticationException when a service ticket is requested on behalf of a surrogate user but surrogateAuthenticationService.canImpersonate(surrogateUser, principal, service) returns false. This is a per-service, per-ticket authorization gate: even if authentication succeeded earlier, ticket generation re-checks impersonation authority and hard-fails if denied.","triggerScenarios":"Requesting a service ticket with a credential whose SurrogateCredentialTrait names a surrogate user that the principal cannot impersonate for the given service (canImpersonate returns false).","commonSituations":"Surrogate mapping changed after authentication; the service-specific impersonation rules exclude this service; stale cached session still holding a surrogate trait after eligibility was revoked; mismatch between attribute-based eligibility config and the requested service.","solutions":["Confirm canImpersonate(target, principal, service) would return true for the exact service URL (e.g. via the surrogate eligibility attributes/groups or LDAP search filter)","Update the surrogate authorization data (group/attribute membership or LDAP filter, including service scoping) so the principal is eligible for that service","Force the user to re-authenticate to refresh the surrogate credential trait after fixing authorization data","Check for stale caches in the SurrogateAuthenticationService and clear them"],"exampleFix":"// before\ncas.authn.surrogate.ldap.searchFilter=(&(uid={principal})(ssoRole=employee))\n// after\ncas.authn.surrogate.ldap.searchFilter=(&(uid={surrogate})(member={principal})(ssoRole=employee))","handlingStrategy":"try-catch","validationCode":"boolean ok = surrogateAuthenticationService.canImpersonate(surrogateUser, principalId, Optional.ofNullable(service));\nif (!ok) throw new IllegalStateException(\"not allowed to impersonate \" + surrogateUser);","typeGuard":null,"tryCatchPattern":"try {\n    return centralAuthenticationService.grantServiceTicket(tgtId, service, surrogateCredential);\n} catch (SurrogateAuthenticationException e) {\n    LOGGER.warn(\"Ticket denied for surrogate: {}\", e.getMessage());\n    throw e; // rethrow as it is authorization, not transient\n}","preventionTips":["Re-check canImpersonate before every ticket request for a surrogate credential","Include the exact service URL in your eligibility checks to mirror the generator's logic","Invalidate cached surrogate sessions after changing eligibility attributes","Prefer wildcard '~*' eligibility only when impersonating anyone is acceptable"],"tags":["surrogate-authentication","impersonation-unauthorized","service-ticket","cas"],"backgroundTag":"authentication-required","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}