Simplifying the CORS SPI and the default implementation
Closes #27646 Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
parent
cbce548e71
commit
a65508ca13
33 changed files with 182 additions and 205 deletions
|
@ -18,13 +18,11 @@
|
|||
package org.keycloak.services.cors;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.Response.ResponseBuilder;
|
||||
|
||||
import org.keycloak.http.HttpRequest;
|
||||
import org.keycloak.http.HttpResponse;
|
||||
import org.keycloak.common.util.Resteasy;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
@ -36,59 +34,67 @@ import org.keycloak.utils.KeycloakSessionUtil;
|
|||
*/
|
||||
public interface Cors extends Provider {
|
||||
|
||||
public static final long DEFAULT_MAX_AGE = TimeUnit.HOURS.toSeconds(1);
|
||||
public static final String DEFAULT_ALLOW_METHODS = "GET, HEAD, OPTIONS";
|
||||
public static final String DEFAULT_ALLOW_HEADERS = "Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, DPoP";
|
||||
long DEFAULT_MAX_AGE = TimeUnit.HOURS.toSeconds(1);
|
||||
String DEFAULT_ALLOW_METHODS = "GET, HEAD, OPTIONS";
|
||||
String DEFAULT_ALLOW_HEADERS = "Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, DPoP";
|
||||
|
||||
public static final String ORIGIN_HEADER = "Origin";
|
||||
public static final String AUTHORIZATION_HEADER = "Authorization";
|
||||
String ORIGIN_HEADER = "Origin";
|
||||
String AUTHORIZATION_HEADER = "Authorization";
|
||||
|
||||
public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
|
||||
public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
|
||||
public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
|
||||
public static final String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers";
|
||||
public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
|
||||
public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age";
|
||||
String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
|
||||
String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
|
||||
String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
|
||||
String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers";
|
||||
String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
|
||||
String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age";
|
||||
|
||||
public static final String ACCESS_CONTROL_ALLOW_ORIGIN_WILDCARD = "*";
|
||||
public static final String INCLUDE_REDIRECTS = "+";
|
||||
String ACCESS_CONTROL_ALLOW_ORIGIN_WILDCARD = "*";
|
||||
|
||||
public static Cors add(HttpRequest request, ResponseBuilder response) {
|
||||
static Cors builder() {
|
||||
KeycloakSession session = KeycloakSessionUtil.getKeycloakSession();
|
||||
return session.getProvider(Cors.class).request(request).builder(response);
|
||||
return session.getProvider(Cors.class);
|
||||
}
|
||||
|
||||
public static Cors add(HttpRequest request) {
|
||||
KeycloakSession session = KeycloakSessionUtil.getKeycloakSession();
|
||||
return session.getProvider(Cors.class).request(request);
|
||||
Cors builder(ResponseBuilder builder);
|
||||
|
||||
Cors preflight();
|
||||
|
||||
Cors auth();
|
||||
|
||||
Cors allowAllOrigins();
|
||||
|
||||
Cors allowedOrigins(KeycloakSession session, ClientModel client);
|
||||
|
||||
Cors allowedOrigins(AccessToken token);
|
||||
|
||||
Cors allowedOrigins(String... allowedOrigins);
|
||||
|
||||
Cors allowedMethods(String... allowedMethods);
|
||||
|
||||
Cors exposedHeaders(String... exposedHeaders);
|
||||
|
||||
/**
|
||||
* Add the CORS headers to the current {@link org.keycloak.http.HttpResponse}.
|
||||
*/
|
||||
void add();
|
||||
|
||||
/**
|
||||
* <p>Add the CORS headers to the current server {@link org.keycloak.http.HttpResponse} and returns a {@link Response} based
|
||||
* on the given {@code builder}.
|
||||
*
|
||||
* <p>This is a convenient method to make it easier to return a {@link Response} from methods while at the same time
|
||||
* adding the corresponding CORS headers to the underlying server response.
|
||||
*
|
||||
* @param builder the response builder
|
||||
* @return the response built from the response builder
|
||||
*/
|
||||
default Response add(ResponseBuilder builder) {
|
||||
if (builder == null) {
|
||||
throw new IllegalStateException("builder is not set");
|
||||
}
|
||||
|
||||
add();
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public Cors request(HttpRequest request);
|
||||
|
||||
public Cors builder(ResponseBuilder builder);
|
||||
|
||||
public Cors preflight();
|
||||
|
||||
public Cors auth();
|
||||
|
||||
public Cors allowAllOrigins();
|
||||
|
||||
public Cors allowedOrigins(KeycloakSession session, ClientModel client);
|
||||
|
||||
public Cors allowedOrigins(AccessToken token);
|
||||
|
||||
public Cors allowedOrigins(String... allowedOrigins);
|
||||
|
||||
public Cors allowedMethods(String... allowedMethods);
|
||||
|
||||
public Cors exposedHeaders(String... exposedHeaders);
|
||||
|
||||
public Cors addExposedHeaders(String... exposedHeaders);
|
||||
|
||||
public Response build();
|
||||
|
||||
public boolean build(HttpResponse response);
|
||||
|
||||
public boolean build(BiConsumer<String, String> addHeader);
|
||||
|
||||
}
|
||||
|
|
|
@ -273,10 +273,11 @@ public class AuthorizationTokenService {
|
|||
}
|
||||
|
||||
private Response createSuccessfulResponse(Object response, KeycloakAuthorizationRequest request) {
|
||||
return Cors.add(request.getHttpRequest(), Response.status(Status.OK).type(MediaType.APPLICATION_JSON_TYPE).entity(response))
|
||||
return Cors.builder()
|
||||
.allowedOrigins(request.getKeycloakSession(), request.getKeycloakSession().getContext().getClient())
|
||||
.allowedMethods(HttpMethod.POST)
|
||||
.exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();
|
||||
.exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS)
|
||||
.add(Response.status(Status.OK).type(MediaType.APPLICATION_JSON_TYPE).entity(response));
|
||||
}
|
||||
|
||||
private boolean isPublicClientRequestingEntitlementWithClaims(KeycloakAuthorizationRequest request) {
|
||||
|
|
|
@ -279,7 +279,7 @@ public class DefaultTokenExchangeProvider implements TokenExchangeProvider {
|
|||
throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "Client not allowed to exchange", Response.Status.FORBIDDEN);
|
||||
}
|
||||
Response response = ((ExchangeTokenToIdentityProviderToken)provider).exchangeFromToken(session.getContext().getUri(), event, client, targetUserSession, targetUser, formParams);
|
||||
return cors.builder(Response.fromResponse(response)).build();
|
||||
return cors.add(Response.fromResponse(response));
|
||||
|
||||
}
|
||||
|
||||
|
@ -451,7 +451,7 @@ public class DefaultTokenExchangeProvider implements TokenExchangeProvider {
|
|||
|
||||
event.success();
|
||||
|
||||
return cors.builder(Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).build();
|
||||
return cors.add(Response.ok(res, MediaType.APPLICATION_JSON_TYPE));
|
||||
}
|
||||
|
||||
protected Response exchangeClientToSAML2Client(UserModel targetUser, UserSessionModel targetUserSession, String requestedTokenType, ClientModel targetClient) {
|
||||
|
@ -501,7 +501,7 @@ public class DefaultTokenExchangeProvider implements TokenExchangeProvider {
|
|||
|
||||
event.success();
|
||||
|
||||
return cors.builder(Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).build();
|
||||
return cors.add(Response.ok(res, MediaType.APPLICATION_JSON_TYPE));
|
||||
}
|
||||
|
||||
protected Response exchangeExternalToken(String issuer, String subjectToken) {
|
||||
|
|
|
@ -199,7 +199,7 @@ public class OIDCLoginProtocolService {
|
|||
@Path("certs")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getVersionPreflight() {
|
||||
return Cors.add(request, Response.ok()).allowedMethods("GET").preflight().auth().build();
|
||||
return Cors.builder().allowedMethods("GET").preflight().auth().add(Response.ok());
|
||||
}
|
||||
|
||||
@GET
|
||||
|
@ -232,7 +232,7 @@ public class OIDCLoginProtocolService {
|
|||
keySet.setKeys(jwks);
|
||||
|
||||
Response.ResponseBuilder responseBuilder = Response.ok(keySet).cacheControl(CacheControlUtil.getDefaultCacheControl());
|
||||
return Cors.add(request, responseBuilder).allowedOrigins("*").auth().build();
|
||||
return Cors.builder().allowedOrigins("*").auth().add(responseBuilder);
|
||||
}
|
||||
|
||||
@Path("userinfo")
|
||||
|
@ -276,7 +276,7 @@ public class OIDCLoginProtocolService {
|
|||
private void checkSsl() {
|
||||
if (!session.getContext().getUri().getBaseUri().getScheme().equals("https")
|
||||
&& realm.getSslRequired().isRequired(clientConnection)) {
|
||||
Cors cors = Cors.add(request).auth().allowedMethods(request.getHttpMethod()).auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
|
||||
Cors cors = Cors.builder().auth().allowedMethods(request.getHttpMethod()).auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
|
||||
throw new CorsErrorResponseException(cors.allowAllOrigins(), OAuthErrorException.INVALID_REQUEST, "HTTPS required",
|
||||
Response.Status.FORBIDDEN);
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@ public class LogoutEndpoint {
|
|||
@Path("/")
|
||||
@OPTIONS
|
||||
public Response issueUserInfoPreflight() {
|
||||
return Cors.add(this.request, Response.ok()).auth().preflight().build();
|
||||
return Cors.builder().auth().preflight().add(Response.ok());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -496,7 +496,7 @@ public class LogoutEndpoint {
|
|||
* @return
|
||||
*/
|
||||
private Response logoutToken() {
|
||||
cors = Cors.add(request).auth().allowedMethods("POST").auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
|
||||
cors = Cors.builder().auth().allowedMethods("POST").auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
|
||||
|
||||
MultivaluedMap<String, String> form = request.getDecodedFormParameters();
|
||||
checkSsl();
|
||||
|
@ -550,7 +550,7 @@ public class LogoutEndpoint {
|
|||
}
|
||||
}
|
||||
|
||||
return cors.builder(Response.noContent()).build();
|
||||
return cors.add(Response.noContent());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -618,18 +618,16 @@ public class LogoutEndpoint {
|
|||
session.getProvider(SecurityHeadersProvider.class).options().allowEmptyContentType();
|
||||
|
||||
if (oneOrMoreDownstreamLogoutsFailed(backchannelLogoutResponse)) {
|
||||
return Cors.add(request)
|
||||
return Cors.builder()
|
||||
.auth()
|
||||
.builder(Response.status(Response.Status.GATEWAY_TIMEOUT)
|
||||
.type(MediaType.APPLICATION_JSON_TYPE))
|
||||
.build();
|
||||
.add(Response.status(Response.Status.GATEWAY_TIMEOUT)
|
||||
.type(MediaType.APPLICATION_JSON_TYPE));
|
||||
}
|
||||
|
||||
return Cors.add(request)
|
||||
return Cors.builder()
|
||||
.auth()
|
||||
.builder(Response.ok()
|
||||
.type(MediaType.APPLICATION_JSON_TYPE))
|
||||
.build();
|
||||
.add(Response.ok()
|
||||
.type(MediaType.APPLICATION_JSON_TYPE));
|
||||
}
|
||||
|
||||
private BackchannelLogoutResponse backchannelLogoutWithSessionId(String sessionId,
|
||||
|
|
|
@ -108,7 +108,7 @@ public class TokenEndpoint {
|
|||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
@POST
|
||||
public Response processGrantRequest() {
|
||||
cors = Cors.add(request).auth().allowedMethods("POST").auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
|
||||
cors = Cors.builder().auth().allowedMethods("POST").auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
|
||||
|
||||
MultivaluedMap<String, String> formParameters = request.getDecodedFormParameters();
|
||||
|
||||
|
@ -150,7 +150,7 @@ public class TokenEndpoint {
|
|||
if (logger.isDebugEnabled()) {
|
||||
logger.debugv("CORS preflight from: {0}", headers.getRequestHeaders().getFirst("Origin"));
|
||||
}
|
||||
return Cors.add(request, Response.ok()).auth().preflight().allowedMethods("POST", "OPTIONS").build();
|
||||
return Cors.builder().auth().preflight().allowedMethods("POST", "OPTIONS").add(Response.ok());
|
||||
}
|
||||
|
||||
private void checkSsl() {
|
||||
|
|
|
@ -91,7 +91,7 @@ public class TokenRevocationEndpoint {
|
|||
public Response revoke() {
|
||||
event.event(EventType.REVOKE_GRANT);
|
||||
|
||||
cors = Cors.add(request).auth().allowedMethods("POST").auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
|
||||
cors = Cors.builder().auth().allowedMethods("POST").auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
|
||||
|
||||
checkSsl();
|
||||
checkRealm();
|
||||
|
@ -130,12 +130,12 @@ public class TokenRevocationEndpoint {
|
|||
}
|
||||
|
||||
session.getProvider(SecurityHeadersProvider.class).options().allowEmptyContentType();
|
||||
return cors.builder(Response.ok()).build();
|
||||
return cors.add(Response.ok());
|
||||
}
|
||||
|
||||
@OPTIONS
|
||||
public Response preflight() {
|
||||
return Cors.add(request, Response.ok()).auth().preflight().allowedMethods("POST", "OPTIONS").build();
|
||||
return Cors.builder().auth().preflight().allowedMethods("POST", "OPTIONS").add(Response.ok());
|
||||
}
|
||||
|
||||
private void checkSsl() {
|
||||
|
|
|
@ -112,7 +112,7 @@ public class UserInfoEndpoint {
|
|||
@Path("/")
|
||||
@OPTIONS
|
||||
public Response issueUserInfoPreflight() {
|
||||
return Cors.add(this.request, Response.ok()).auth().preflight().build();
|
||||
return Cors.builder().auth().preflight().add(Response.ok());
|
||||
}
|
||||
|
||||
@Path("/")
|
||||
|
@ -322,7 +322,7 @@ public class UserInfoEndpoint {
|
|||
|
||||
event.success();
|
||||
|
||||
return cors.builder(responseBuilder).build();
|
||||
return cors.add(responseBuilder);
|
||||
}
|
||||
|
||||
private String jweFromContent(String content, String jweContentType) {
|
||||
|
@ -363,7 +363,7 @@ public class UserInfoEndpoint {
|
|||
}
|
||||
|
||||
private void setupCors() {
|
||||
cors = Cors.add(request).auth().allowedMethods(request.getHttpMethod()).auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
|
||||
cors = Cors.builder().auth().allowedMethods(request.getHttpMethod()).auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
|
||||
error.cors(cors);
|
||||
}
|
||||
|
||||
|
|
|
@ -180,7 +180,7 @@ public class ClientCredentialsGrantType extends OAuth2GrantTypeBase {
|
|||
}
|
||||
event.success();
|
||||
|
||||
return cors.builder(Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).build();
|
||||
return cors.add(Response.ok(res, MediaType.APPLICATION_JSON_TYPE));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -159,7 +159,7 @@ public abstract class OAuth2GrantTypeBase implements OAuth2GrantType {
|
|||
|
||||
event.success();
|
||||
|
||||
return cors.builder(Response.ok(res).type(MediaType.APPLICATION_JSON_TYPE)).build();
|
||||
return cors.add(Response.ok(res).type(MediaType.APPLICATION_JSON_TYPE));
|
||||
}
|
||||
|
||||
protected void checkAndBindMtlsHoKToken(TokenManager.AccessTokenResponseBuilder responseBuilder, boolean useRefreshToken) {
|
||||
|
|
|
@ -96,7 +96,7 @@ public class PreAuthorizedCodeGrantType extends OAuth2GrantTypeBase {
|
|||
|
||||
event.success();
|
||||
|
||||
return cors.allowAllOrigins().builder(Response.ok(tokenResponse).type(MediaType.APPLICATION_JSON_TYPE)).build();
|
||||
return cors.allowAllOrigins().add(Response.ok(tokenResponse).type(MediaType.APPLICATION_JSON_TYPE));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -108,7 +108,7 @@ public class RefreshTokenGrantType extends OAuth2GrantTypeBase {
|
|||
|
||||
event.success();
|
||||
|
||||
return cors.builder(Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).build();
|
||||
return cors.add(Response.ok(res, MediaType.APPLICATION_JSON_TYPE));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -31,7 +31,6 @@ import org.keycloak.events.EventType;
|
|||
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.ClientSessionContext;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.utils.AuthenticationFlowResolver;
|
||||
|
@ -113,7 +112,7 @@ public class ResourceOwnerPasswordCredentialsGrantType extends OAuth2GrantTypeBa
|
|||
if (challenge != null) {
|
||||
// Remove authentication session as "Resource Owner Password Credentials Grant" is single-request scoped authentication
|
||||
new AuthenticationSessionManager(session).removeAuthenticationSession(realm, authSession, false);
|
||||
cors.build(response);
|
||||
cors.add();
|
||||
return challenge;
|
||||
}
|
||||
processor.evaluateRequiredActionTriggers();
|
||||
|
@ -161,7 +160,7 @@ public class ResourceOwnerPasswordCredentialsGrantType extends OAuth2GrantTypeBa
|
|||
event.success();
|
||||
AuthenticationManager.logSuccess(session, authSession);
|
||||
|
||||
return cors.builder(Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).build();
|
||||
return cors.add(Response.ok(res, MediaType.APPLICATION_JSON_TYPE));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -101,7 +101,7 @@ public class DeviceEndpoint extends AuthorizationEndpointBase implements RealmRe
|
|||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response handleDeviceRequest() {
|
||||
cors = Cors.add(request).auth().allowedMethods("POST").auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
|
||||
cors = Cors.builder().auth().allowedMethods("POST").auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
|
||||
|
||||
logger.trace("Processing @POST request");
|
||||
event.event(EventType.OAUTH2_DEVICE_AUTH);
|
||||
|
@ -186,7 +186,7 @@ public class DeviceEndpoint extends AuthorizationEndpointBase implements RealmRe
|
|||
response.setVerificationUri(deviceUrl);
|
||||
response.setVerificationUriComplete(deviceUrl + "?user_code=" + response.getUserCode());
|
||||
|
||||
return cors.builder(Response.ok(JsonSerialization.writeValueAsBytes(response)).type(MediaType.APPLICATION_JSON_TYPE)).build();
|
||||
return cors.add(Response.ok(JsonSerialization.writeValueAsBytes(response)).type(MediaType.APPLICATION_JSON_TYPE));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error creating OAuth 2.0 Device Authorization Response.", e);
|
||||
}
|
||||
|
@ -197,7 +197,7 @@ public class DeviceEndpoint extends AuthorizationEndpointBase implements RealmRe
|
|||
if (logger.isDebugEnabled()) {
|
||||
logger.debugv("CORS preflight from: {0}", headers.getRequestHeaders().getFirst("Origin"));
|
||||
}
|
||||
return Cors.add(request, Response.ok()).auth().preflight().allowedMethods("POST", "OPTIONS").build();
|
||||
return Cors.builder().auth().preflight().allowedMethods("POST", "OPTIONS").add(Response.ok());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -80,7 +80,7 @@ public class ParEndpoint extends AbstractParEndpoint {
|
|||
|
||||
ProfileHelper.requireFeature(Profile.Feature.PAR);
|
||||
|
||||
cors = Cors.add(httpRequest).auth().allowedMethods("POST").auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
|
||||
cors = Cors.builder().auth().allowedMethods("POST").auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
|
||||
|
||||
event.event(EventType.PUSHED_AUTHORIZATION_REQUEST);
|
||||
|
||||
|
@ -163,10 +163,9 @@ public class ParEndpoint extends AbstractParEndpoint {
|
|||
ParResponse parResponse = new ParResponse(requestUri, expiresIn);
|
||||
|
||||
session.getProvider(SecurityHeadersProvider.class).options().allowEmptyContentType();
|
||||
return cors.builder(Response.status(Response.Status.CREATED)
|
||||
.entity(parResponse)
|
||||
.type(MediaType.APPLICATION_JSON_TYPE))
|
||||
.build();
|
||||
return cors.add(Response.status(Response.Status.CREATED)
|
||||
.entity(parResponse)
|
||||
.type(MediaType.APPLICATION_JSON_TYPE));
|
||||
}
|
||||
|
||||
}
|
|
@ -18,7 +18,6 @@
|
|||
package org.keycloak.protocol.oidc.utils;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.http.HttpResponse;
|
||||
import org.keycloak.authentication.AuthenticationProcessor;
|
||||
import org.keycloak.authentication.ClientAuthenticator;
|
||||
import org.keycloak.authentication.ClientAuthenticatorFactory;
|
||||
|
@ -51,8 +50,7 @@ public class AuthorizeClientUtil {
|
|||
if (response != null) {
|
||||
if (cors != null) {
|
||||
cors.allowAllOrigins();
|
||||
HttpResponse httpResponse = session.getContext().getHttpResponse();
|
||||
cors.build(httpResponse);
|
||||
cors.add();
|
||||
}
|
||||
throw new WebApplicationException(response);
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ public class CorsErrorResponseException extends WebApplicationException {
|
|||
public Response getResponse() {
|
||||
OAuth2ErrorRepresentation errorRep = new OAuth2ErrorRepresentation(error, errorDescription);
|
||||
Response.ResponseBuilder builder = Response.status(status).entity(errorRep).type(MediaType.APPLICATION_JSON_TYPE);
|
||||
return cors.builder(builder).build();
|
||||
return cors.add(builder);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,9 +21,7 @@ import java.util.Arrays;
|
|||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.Response.ResponseBuilder;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
@ -42,7 +40,8 @@ public class DefaultCors implements Cors {
|
|||
|
||||
private static final Logger logger = Logger.getLogger(DefaultCors.class);
|
||||
|
||||
private HttpRequest request;
|
||||
private final HttpRequest request;
|
||||
private final HttpResponse response;
|
||||
private ResponseBuilder builder;
|
||||
private Set<String> allowedOrigins;
|
||||
private Set<String> allowedMethods;
|
||||
|
@ -51,14 +50,9 @@ public class DefaultCors implements Cors {
|
|||
private boolean preflight;
|
||||
private boolean auth;
|
||||
|
||||
DefaultCors(HttpRequest request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cors request(HttpRequest request) {
|
||||
this.request = request;
|
||||
return this;
|
||||
DefaultCors(KeycloakSession session) {
|
||||
this.request = session.getContext().getHttpRequest();
|
||||
this.response = session.getContext().getHttpResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -117,89 +111,65 @@ public class DefaultCors implements Cors {
|
|||
|
||||
@Override
|
||||
public Cors exposedHeaders(String... exposedHeaders) {
|
||||
this.exposedHeaders = new HashSet<>(Arrays.asList(exposedHeaders));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cors addExposedHeaders(String... exposedHeaders) {
|
||||
if (this.exposedHeaders == null) {
|
||||
this.exposedHeaders(exposedHeaders);
|
||||
} else {
|
||||
this.exposedHeaders.addAll(Arrays.asList(exposedHeaders));
|
||||
this.exposedHeaders = new HashSet<>();
|
||||
}
|
||||
|
||||
this.exposedHeaders.addAll(Arrays.asList(exposedHeaders));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response build() {
|
||||
if (builder == null) {
|
||||
throw new IllegalStateException("builder is not set");
|
||||
}
|
||||
|
||||
if (build(builder::header)) {
|
||||
logger.debug("Added CORS headers to response");
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean build(HttpResponse response) {
|
||||
if (build(response::addHeader)) {
|
||||
logger.debug("Added CORS headers to response");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean build(BiConsumer<String, String> addHeader) {
|
||||
public void add() {
|
||||
if (request == null) {
|
||||
throw new IllegalStateException("request is not set");
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
throw new IllegalStateException("response is not set");
|
||||
}
|
||||
|
||||
String origin = request.getHttpHeaders().getRequestHeaders().getFirst(ORIGIN_HEADER);
|
||||
if (origin == null) {
|
||||
logger.trace("No Origin header, ignoring");
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!preflight && (allowedOrigins == null || (!allowedOrigins.contains(origin) && !allowedOrigins.contains(ACCESS_CONTROL_ALLOW_ORIGIN_WILDCARD)))) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debugv("Invalid CORS request: origin {0} not in allowed origins {1}", origin, allowedOrigins);
|
||||
}
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
addHeader.accept(ACCESS_CONTROL_ALLOW_ORIGIN, origin);
|
||||
response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN, origin);
|
||||
|
||||
if (preflight) {
|
||||
if (allowedMethods != null) {
|
||||
addHeader.accept(ACCESS_CONTROL_ALLOW_METHODS, CollectionUtil.join(allowedMethods));
|
||||
response.setHeader(ACCESS_CONTROL_ALLOW_METHODS, CollectionUtil.join(allowedMethods));
|
||||
} else {
|
||||
addHeader.accept(ACCESS_CONTROL_ALLOW_METHODS, DEFAULT_ALLOW_METHODS);
|
||||
response.setHeader(ACCESS_CONTROL_ALLOW_METHODS, DEFAULT_ALLOW_METHODS);
|
||||
}
|
||||
}
|
||||
|
||||
if (!preflight && exposedHeaders != null) {
|
||||
addHeader.accept(ACCESS_CONTROL_EXPOSE_HEADERS, CollectionUtil.join(exposedHeaders));
|
||||
response.setHeader(ACCESS_CONTROL_EXPOSE_HEADERS, CollectionUtil.join(exposedHeaders));
|
||||
}
|
||||
|
||||
addHeader.accept(ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.toString(auth));
|
||||
response.setHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.toString(auth));
|
||||
|
||||
if (preflight) {
|
||||
if (auth) {
|
||||
addHeader.accept(ACCESS_CONTROL_ALLOW_HEADERS, String.format("%s, %s", DEFAULT_ALLOW_HEADERS, AUTHORIZATION_HEADER));
|
||||
response.setHeader(ACCESS_CONTROL_ALLOW_HEADERS, String.format("%s, %s", DEFAULT_ALLOW_HEADERS, AUTHORIZATION_HEADER));
|
||||
} else {
|
||||
addHeader.accept(ACCESS_CONTROL_ALLOW_HEADERS, DEFAULT_ALLOW_HEADERS);
|
||||
response.setHeader(ACCESS_CONTROL_ALLOW_HEADERS, DEFAULT_ALLOW_HEADERS);
|
||||
}
|
||||
}
|
||||
|
||||
if (preflight) {
|
||||
addHeader.accept(ACCESS_CONTROL_MAX_AGE, String.valueOf(DEFAULT_MAX_AGE));
|
||||
response.setHeader(ACCESS_CONTROL_MAX_AGE, String.valueOf(DEFAULT_MAX_AGE));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -30,7 +30,7 @@ public class DefaultCorsFactory implements CorsFactory {
|
|||
|
||||
@Override
|
||||
public Cors create(KeycloakSession session) {
|
||||
return new DefaultCors(session.getContext().getHttpRequest());
|
||||
return new DefaultCors(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -448,7 +448,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
|||
@Path("{provider_alias}/token")
|
||||
@OPTIONS
|
||||
public Response retrieveTokenPreflight() {
|
||||
return Cors.add(this.request, Response.ok()).auth().preflight().build();
|
||||
return Cors.builder().auth().preflight().add(Response.ok());
|
||||
}
|
||||
|
||||
@GET
|
||||
|
@ -1346,7 +1346,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
|||
}
|
||||
|
||||
private Response corsResponse(Response response, ClientModel clientModel) {
|
||||
return Cors.add(this.request, Response.fromResponse(response)).auth().allowedOrigins(session, clientModel).build();
|
||||
return Cors.builder().auth().allowedOrigins(session, clientModel).add(Response.fromResponse(response));
|
||||
}
|
||||
|
||||
private void fireErrorEvent(String message, Throwable throwable) {
|
||||
|
|
|
@ -127,7 +127,7 @@ public class JsResource {
|
|||
}
|
||||
|
||||
String contentType = "text/javascript";
|
||||
Cors cors = Cors.add(session.getContext().getHttpRequest()).allowAllOrigins();
|
||||
Cors cors = Cors.builder().allowAllOrigins();
|
||||
|
||||
ResourceEncodingProvider encodingProvider = ResourceEncodingHelper.getResourceEncodingProvider(session, contentType);
|
||||
|
||||
|
@ -143,9 +143,9 @@ public class JsResource {
|
|||
if (encodingProvider != null) {
|
||||
rb.encoding(encodingProvider.getEncoding());
|
||||
}
|
||||
return cors.builder(rb).build();
|
||||
return cors.add(rb);
|
||||
} else {
|
||||
return cors.builder(Response.status(Response.Status.NOT_FOUND)).build();
|
||||
return cors.add(Response.status(Response.Status.NOT_FOUND));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ public class PublicRealmResource {
|
|||
@Path("/")
|
||||
@OPTIONS
|
||||
public Response accountPreflight() {
|
||||
return Cors.add(request, Response.ok()).auth().preflight().build();
|
||||
return Cors.builder().auth().preflight().add(Response.ok());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,7 +80,7 @@ public class PublicRealmResource {
|
|||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public PublishedRealmRepresentation getRealm() {
|
||||
Cors.add(request).allowedOrigins(Cors.ACCESS_CONTROL_ALLOW_ORIGIN_WILDCARD).auth().build(response);
|
||||
Cors.builder().allowedOrigins(Cors.ACCESS_CONTROL_ALLOW_ORIGIN_WILDCARD).auth().add();
|
||||
return realmRep(session, realm, session.getContext().getUri());
|
||||
}
|
||||
|
||||
|
|
|
@ -217,7 +217,7 @@ public class RealmsResource {
|
|||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getVersionPreflight(final @PathParam("realm") String name,
|
||||
final @PathParam("provider") String providerName) {
|
||||
return Cors.add(session.getContext().getHttpRequest(), Response.ok()).allowedMethods("GET").preflight().auth().build();
|
||||
return Cors.builder().allowedMethods("GET").preflight().auth().add(Response.ok());
|
||||
}
|
||||
|
||||
@GET
|
||||
|
@ -240,7 +240,7 @@ public class RealmsResource {
|
|||
|
||||
if (wellKnown != null) {
|
||||
ResponseBuilder responseBuilder = Response.ok(wellKnown.getConfig()).cacheControl(CacheControlUtil.noCache());
|
||||
return Cors.add(session.getContext().getHttpRequest(), responseBuilder).allowedOrigins("*").auth().build();
|
||||
return Cors.builder().allowedOrigins("*").auth().add(responseBuilder);
|
||||
}
|
||||
|
||||
throw new NotFoundException();
|
||||
|
@ -279,7 +279,7 @@ public class RealmsResource {
|
|||
if (!"https".equals(session.getContext().getUri().getBaseUri().getScheme())
|
||||
&& realm.getSslRequired().isRequired(session.getContext().getConnection())) {
|
||||
HttpRequest request = session.getContext().getHttpRequest();
|
||||
Cors cors = Cors.add(request).auth().allowedMethods(request.getHttpMethod()).auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
|
||||
Cors cors = Cors.builder().auth().allowedMethods(request.getHttpMethod()).auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
|
||||
throw new CorsErrorResponseException(cors.allowAllOrigins(), OAuthErrorException.INVALID_REQUEST, "HTTPS required",
|
||||
Response.Status.FORBIDDEN);
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ public class ThemeResource {
|
|||
@Path("/{realm}/{themeType}/{locale}")
|
||||
@OPTIONS
|
||||
public Response localizationTextPreflight() {
|
||||
return Cors.add(session.getContext().getHttpRequest(), Response.ok()).auth().preflight().build();
|
||||
return Cors.builder().auth().preflight().add(Response.ok());
|
||||
}
|
||||
|
||||
@GET
|
||||
|
@ -151,8 +151,7 @@ public class ThemeResource {
|
|||
new KeySource((String) e.getKey(), (String) e.getValue())).collect(toList());
|
||||
}
|
||||
|
||||
Response.ResponseBuilder responseBuilder = Response.ok(result);
|
||||
return Cors.add(session.getContext().getHttpRequest(), responseBuilder).allowedOrigins("*").auth().build();
|
||||
return Cors.builder().allowedOrigins("*").auth().add(Response.ok(result));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ public class AccountLoader {
|
|||
AccountResourceProvider accountResourceProvider = getAccountResourceProvider(theme);
|
||||
|
||||
if (request.getHttpMethod().equals(HttpMethod.OPTIONS)) {
|
||||
return new CorsPreflightService(request);
|
||||
return new CorsPreflightService();
|
||||
} else if ((accepts.contains(MediaType.APPLICATION_JSON_TYPE) || MediaType.APPLICATION_JSON_TYPE.equals(content)) && !uriInfo.getPath().endsWith("keycloak.json")) {
|
||||
return getAccountRestService(client, null);
|
||||
} else if (accountResourceProvider != null) {
|
||||
|
@ -100,7 +100,7 @@ public class AccountLoader {
|
|||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Object getVersionedAccountRestService(final @PathParam("version") String version) {
|
||||
if (request.getHttpMethod().equals(HttpMethod.OPTIONS)) {
|
||||
return new CorsPreflightService(request);
|
||||
return new CorsPreflightService();
|
||||
}
|
||||
return getAccountRestService(getAccountManagementClient(session.getContext().getRealm()), version);
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ public class AccountLoader {
|
|||
|
||||
Auth auth = new Auth(session.getContext().getRealm(), accessToken, authResult.getUser(), client, authResult.getSession(), false);
|
||||
|
||||
Cors.add(request).allowedOrigins(auth.getToken()).allowedMethods("GET", "PUT", "POST", "DELETE").auth().build(response);
|
||||
Cors.builder().allowedOrigins(auth.getToken()).allowedMethods("GET", "PUT", "POST", "DELETE").auth().add();
|
||||
|
||||
if (authResult.getUser().getServiceAccountClientLink() != null) {
|
||||
throw new NotAuthorizedException("Service accounts are not allowed to access this service");
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.keycloak.services.resources.account;
|
||||
|
||||
import org.keycloak.http.HttpRequest;
|
||||
import org.keycloak.services.cors.Cors;
|
||||
|
||||
import jakarta.ws.rs.OPTIONS;
|
||||
|
@ -12,12 +11,6 @@ import jakarta.ws.rs.core.Response;
|
|||
*/
|
||||
public class CorsPreflightService {
|
||||
|
||||
private final HttpRequest request;
|
||||
|
||||
public CorsPreflightService(HttpRequest request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
/**
|
||||
* CORS preflight
|
||||
*
|
||||
|
@ -26,8 +19,8 @@ public class CorsPreflightService {
|
|||
@Path("{any:.*}")
|
||||
@OPTIONS
|
||||
public Response preflight() {
|
||||
Cors cors = Cors.add(request, Response.ok()).auth().allowedMethods("GET", "POST", "DELETE", "PUT", "HEAD", "OPTIONS").preflight();
|
||||
return cors.build();
|
||||
return Cors.builder().auth().allowedMethods("GET", "POST", "DELETE", "PUT", "HEAD", "OPTIONS").preflight()
|
||||
.add(Response.ok());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ public class LinkedAccountsResource {
|
|||
public Response linkedAccounts() {
|
||||
auth.requireOneOf(AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_PROFILE);
|
||||
SortedSet<LinkedAccountRepresentation> linkedAccounts = getLinkedAccounts(this.session, this.realm, this.user);
|
||||
return Cors.add(request, Response.ok(linkedAccounts)).auth().allowedOrigins(auth.getToken()).build();
|
||||
return Cors.builder().auth().allowedOrigins(auth.getToken()).add(Response.ok(linkedAccounts));
|
||||
}
|
||||
|
||||
private Set<String> findSocialIds() {
|
||||
|
@ -183,7 +183,7 @@ public class LinkedAccountsResource {
|
|||
rep.setHash(hash);
|
||||
rep.setNonce(nonce);
|
||||
|
||||
return Cors.add(request, Response.ok(rep)).auth().allowedOrigins(auth.getToken()).build();
|
||||
return Cors.builder().auth().allowedOrigins(auth.getToken()).add(Response.ok(rep));
|
||||
} catch (Exception spe) {
|
||||
spe.printStackTrace();
|
||||
throw ErrorResponse.error(Messages.FAILED_TO_PROCESS_RESPONSE, Response.Status.INTERNAL_SERVER_ERROR);
|
||||
|
@ -221,7 +221,7 @@ public class LinkedAccountsResource {
|
|||
.detail(Details.IDENTITY_PROVIDER_USERNAME, link.getUserName())
|
||||
.success();
|
||||
|
||||
return Cors.add(request, Response.noContent()).auth().allowedOrigins(auth.getToken()).build();
|
||||
return Cors.builder().auth().allowedOrigins(auth.getToken()).add(Response.noContent());
|
||||
}
|
||||
|
||||
private String checkCommonPreconditions(String providerAlias) {
|
||||
|
|
|
@ -185,7 +185,7 @@ public class AdminConsole {
|
|||
@Path("whoami")
|
||||
@OPTIONS
|
||||
public Response whoAmIPreFlight() {
|
||||
return new AdminCorsPreflightService(request).preflight();
|
||||
return new AdminCorsPreflightService().preflight();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -239,10 +239,11 @@ public class AdminConsole {
|
|||
|
||||
Locale locale = session.getContext().resolveLocale(user);
|
||||
|
||||
Cors.add(request).allowedOrigins(authResult.getToken()).allowedMethods("GET").auth()
|
||||
.build(response);
|
||||
|
||||
return Response.ok(new WhoAmI(user.getId(), realm.getName(), displayName, createRealm, realmAccess, locale)).build();
|
||||
return Cors.builder()
|
||||
.allowedOrigins(authResult.getToken())
|
||||
.allowedMethods("GET")
|
||||
.auth()
|
||||
.add(Response.ok(new WhoAmI(user.getId(), realm.getName(), displayName, createRealm, realmAccess, locale)));
|
||||
}
|
||||
|
||||
private void addRealmAccess(RealmModel realm, UserModel user, Map<String, Set<String>> realmAdminAccess) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.keycloak.services.resources.admin;
|
||||
|
||||
import org.keycloak.http.HttpRequest;
|
||||
import org.keycloak.services.cors.Cors;
|
||||
|
||||
import jakarta.ws.rs.OPTIONS;
|
||||
|
@ -12,12 +11,6 @@ import jakarta.ws.rs.core.Response;
|
|||
*/
|
||||
public class AdminCorsPreflightService {
|
||||
|
||||
private HttpRequest request;
|
||||
|
||||
public AdminCorsPreflightService(HttpRequest request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
/**
|
||||
* CORS preflight
|
||||
*
|
||||
|
@ -26,7 +19,7 @@ public class AdminCorsPreflightService {
|
|||
@Path("{any:.*}")
|
||||
@OPTIONS
|
||||
public Response preflight() {
|
||||
return Cors.add(request, Response.ok()).preflight().allowedMethods("GET", "PUT", "POST", "DELETE").auth().build();
|
||||
return Cors.builder().preflight().allowedMethods("GET", "PUT", "POST", "DELETE").auth().add(Response.ok());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -224,10 +224,7 @@ public class AdminRoot {
|
|||
logger.debug("authenticated admin access for: " + auth.getUser().getUsername());
|
||||
}
|
||||
|
||||
HttpResponse response = getHttpResponse();
|
||||
|
||||
Cors.add(request).allowedOrigins(auth.getToken()).allowedMethods("GET", "PUT", "POST", "DELETE").exposedHeaders("Location").auth().build(
|
||||
response);
|
||||
Cors.builder().allowedOrigins(auth.getToken()).allowedMethods("GET", "PUT", "POST", "DELETE").exposedHeaders("Location").auth().add();
|
||||
|
||||
return new RealmsAdminResource(session, auth, tokenManager);
|
||||
}
|
||||
|
@ -236,13 +233,11 @@ public class AdminRoot {
|
|||
@OPTIONS
|
||||
@Operation(hidden = true)
|
||||
public Object preFlight() {
|
||||
HttpRequest request = getHttpRequest();
|
||||
|
||||
if (!isAdminApiEnabled()) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return new AdminCorsPreflightService(request);
|
||||
return new AdminCorsPreflightService();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -261,7 +256,7 @@ public class AdminRoot {
|
|||
HttpRequest request = getHttpRequest();
|
||||
|
||||
if (request.getHttpMethod().equals(HttpMethod.OPTIONS)) {
|
||||
return new AdminCorsPreflightService(request);
|
||||
return new AdminCorsPreflightService();
|
||||
}
|
||||
|
||||
AdminAuth auth = authenticateRealmAdminRequest(session.getContext().getRequestHeaders());
|
||||
|
@ -273,8 +268,7 @@ public class AdminRoot {
|
|||
logger.debug("authenticated admin access for: " + auth.getUser().getUsername());
|
||||
}
|
||||
|
||||
Cors.add(request).allowedOrigins(auth.getToken()).allowedMethods("GET", "PUT", "POST", "DELETE").auth().build(
|
||||
getHttpResponse());
|
||||
Cors.builder().allowedOrigins(auth.getToken()).allowedMethods("GET", "PUT", "POST", "DELETE").auth().add();
|
||||
|
||||
return new ServerInfoAdminResource(session);
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ public class RealmsAdminResourcePreflight extends RealmsAdminResource {
|
|||
@Path("{any:.*}")
|
||||
@OPTIONS
|
||||
public Response preFlight() {
|
||||
return Cors.add(request, Response.ok()).preflight().allowedMethods("GET", "PUT", "POST", "DELETE").auth().build();
|
||||
return Cors.builder().preflight().allowedMethods("GET", "PUT", "POST", "DELETE").auth().add(Response.ok());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -136,10 +136,10 @@ public class OAuth2Error {
|
|||
bearer.setErrorDescription(errorDescription);
|
||||
WWWAuthenticate wwwAuthenticate = new WWWAuthenticate(bearer);
|
||||
wwwAuthenticate.build(builder::header);
|
||||
cors.ifPresent(_cors -> _cors.addExposedHeaders(WWW_AUTHENTICATE));
|
||||
cors.ifPresent(_cors -> _cors.exposedHeaders(WWW_AUTHENTICATE));
|
||||
builder.entity("").type(MediaType.TEXT_PLAIN_UTF_8_TYPE);
|
||||
}
|
||||
cors.ifPresent(_cors -> { _cors.build(builder::header); });
|
||||
cors.ifPresent(Cors::add);
|
||||
|
||||
return constructor.newInstance(builder.build());
|
||||
} catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
package org.keycloak.testsuite.account;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assert;
|
||||
|
@ -62,6 +65,7 @@ import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
|||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
|
||||
import org.keycloak.representations.idm.RequiredActionProviderSimpleRepresentation;
|
||||
import org.keycloak.services.cors.Cors;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.account.AccountCredentialResource;
|
||||
import org.keycloak.services.util.ResolveRelative;
|
||||
|
@ -81,9 +85,11 @@ import jakarta.ws.rs.core.Response;
|
|||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
|
@ -592,6 +598,26 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCors() throws IOException {
|
||||
String accountUrl = getAccountUrl(null);
|
||||
SimpleHttp a = SimpleHttpDefault.doGet(accountUrl + "/linked-accounts", httpClient).auth(tokenUtil.getToken())
|
||||
.header("Origin", "http://localtest.me:8180")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
||||
|
||||
try (SimpleHttp.Response response = a.asResponse()) {
|
||||
Set<String> expected = new HashSet<>();
|
||||
Header[] actual = response.getAllHeaders();
|
||||
|
||||
for (Header header : actual) {
|
||||
assertTrue(expected.add(header.getName()));
|
||||
}
|
||||
|
||||
assertThat(expected, Matchers.hasItems(Cors.ACCESS_CONTROL_ALLOW_ORIGIN, Cors.ACCESS_CONTROL_ALLOW_CREDENTIALS));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected UserRepresentation getUser() throws IOException {
|
||||
return getUser(true);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue