{"record":{"id":"3f98b3316500697f","repo":"apereo/cas","slug":"policy-status-exception","errorCode":null,"errorMessage":"<policy status exception>","messagePattern":"<policy status exception>","errorType":"exception","errorClass":"GeneralSecurityException","httpStatus":null,"severity":"error","filePath":"core/cas-server-core-authentication-api/src/main/java/org/apereo/cas/authentication/policy/RestfulAuthenticationPolicy.java","lineNumber":96,"sourceCode":"        val principal = authentication.getPrincipal();\n        try {\n            val entity = MAPPER.writeValueAsString(principal);\n            val headers = CollectionUtils.<String, String>wrap(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);\n            headers.putAll(Objects.requireNonNull(properties).getHeaders());\n            val exec = HttpExecutionRequest.builder()\n                .url(properties.getUrl())\n                .basicAuthUsername(properties.getBasicAuthUsername())\n                .basicAuthPassword(properties.getBasicAuthPassword())\n                .method(HttpMethod.POST)\n                .entity(entity)\n                .headers(headers)\n                .maximumRetryAttempts(properties.getMaximumRetryAttempts())\n                .build();\n            response = HttpUtils.execute(exec);\n            val statusCode = HttpStatus.valueOf(response.getCode());\n            if (statusCode != HttpStatus.OK) {\n                val ex = handleResponseStatusCode(statusCode, principal);\n                throw new GeneralSecurityException(ex);\n            }\n            return AuthenticationPolicyExecutionResult.success();\n        } finally {\n            HttpUtils.close(response);\n        }\n    }\n\n    @Override\n    public Map<String, Object> toConfiguration() {\n        val config = super.toConfiguration();\n        config.put(\"url\", properties.getUrl());\n        config.put(\"basicAuthUsername\", properties.getBasicAuthUsername());\n        config.put(\"basicAuthPassword\", properties.getBasicAuthPassword());\n        config.put(\"maximumRetryAttempts\", properties.getMaximumRetryAttempts());\n        config.put(\"headers\", properties.getHeaders());\n        return config;\n    }\n}","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/core/cas-server-core-authentication-api/src/main/java/org/apereo/cas/authentication/policy/RestfulAuthenticationPolicy.java#L78-L114","documentation":"RestfulAuthenticationPolicy delegates authentication-policy decisions to a remote REST endpoint. After executing the HTTP call, any status code other than 200 OK is converted via handleResponseStatusCode and rethrown as a GeneralSecurityException carrying the resulting AuthenticationHandlerExecutionResult, so the policy is reported as unsatisfied/failed rather than silently passing.","triggerScenarios":"Calling isSatisfiedBy on a policy backed by cas.authn.policy.rest[...] when the configured REST endpoint returns a non-200 status (401/403 for rejected credentials, 404 for a wrong URL, 500 for a backend crash) for the given principal.","commonSituations":"REST endpoint URL misconfigured or pointing at the wrong context path; the remote service rejects the principal (blocked/locked account); the remote service is down and a gateway returns 502/503; TLS or proxy issues producing error responses; API version change so the endpoint no longer returns 200 for success.","solutions":["Check the REST endpoint is reachable and returns 200 for a valid principal: curl -i the configured URL with equivalent payload.","Verify cas.authn.policy.rest.url points at the correct service and context path.","Inspect remote service logs for why it returned 401/403/500 for this principal.","Confirm network/proxy/TLS configuration between CAS and the endpoint (status 404/502/503 usually means infrastructure, not policy)."],"exampleFix":"// before\nmanagement: # wrong path\ncas:\n  authn:\n    policy:\n      rest:\n        url: https://auth.example.com/api/wrong\n// after\ncas:\n  authn:\n    policy:\n      rest:\n        url: https://auth.example.com/api/policy","handlingStrategy":"try-catch","validationCode":"val probe = HttpUtils.execute(HttpUtils.httpBuilder(properties.getUrl()).GET().build())\nif (probe.getCode() != 200) throw new IllegalStateException(\"Policy REST endpoint not healthy: \" + probe.getCode())","typeGuard":null,"tryCatchPattern":"try { return policy.isSatisfiedBy(authentication, service, context); }\ncatch (GeneralSecurityException e) {\n  LOGGER.warn(\"RESTful authentication policy failed\", e);\n  return AuthenticationPolicyExecutionResult.failure();\n}","preventionTips":["Monitor the remote policy endpoint for availability","Validate the URL with a curl smoke test after config changes","Ensure the remote service returns 200 (not 204/201) for satisfied policy"],"tags":["http","authentication-policy","rest"],"backgroundTag":"http-error-response","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"}