Closes #25446

Signed-off-by: Dmitry Telegin <demetrio@carretti.pro>
This commit is contained in:
Dmitry Telegin 2024-01-27 00:39:52 +00:00 committed by Pedro Igor
parent 509f618992
commit b0403e2268
44 changed files with 336 additions and 76 deletions

View file

@ -23,7 +23,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.quarkus.runtime.Environment;
import org.keycloak.quarkus.runtime.configuration.Configuration;
import org.keycloak.services.Urls;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.cors.Cors;
import org.keycloak.theme.FreeMarkerException;
import org.keycloak.theme.Theme;
import org.keycloak.theme.freemarker.FreeMarkerProvider;

View file

@ -23,6 +23,7 @@ import org.keycloak.events.EventBuilder;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.services.cors.Cors;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MultivaluedMap;
@ -40,7 +41,7 @@ public class TokenExchangeContext {
private final MultivaluedMap<String, String> formParams;
// TODO: resolve deps issue and use correct types
private final Object cors;
private final Cors cors;
private final Object tokenManager;
private final ClientModel client;
@ -55,7 +56,7 @@ public class TokenExchangeContext {
public TokenExchangeContext(KeycloakSession session,
MultivaluedMap<String, String> formParams,
Object cors,
Cors cors,
RealmModel realm,
EventBuilder event,
ClientModel client,
@ -83,7 +84,7 @@ public class TokenExchangeContext {
return formParams;
}
public Object getCors() {
public Cors getCors() {
return cors;
}

View file

@ -0,0 +1,92 @@
/*
* Copyright 2024 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.common.util.Resteasy;
import org.keycloak.http.HttpRequest;
import org.keycloak.http.HttpResponse;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.provider.Provider;
import org.keycloak.representations.AccessToken;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
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";
public static final String ORIGIN_HEADER = "Origin";
public static final 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";
public static final String ACCESS_CONTROL_ALLOW_ORIGIN_WILDCARD = "*";
public static final String INCLUDE_REDIRECTS = "+";
public static Cors add(HttpRequest request, ResponseBuilder response) {
KeycloakSession session = Resteasy.getContextData(KeycloakSession.class);
return session.getProvider(Cors.class).request(request).builder(response);
}
public static Cors add(HttpRequest request) {
KeycloakSession session = Resteasy.getContextData(KeycloakSession.class);
return session.getProvider(Cors.class).request(request);
}
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 Response build();
public boolean build(HttpResponse response);
public boolean build(BiConsumer<String, String> addHeader);
}

View file

@ -0,0 +1,27 @@
/*
* Copyright 2024 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.services.cors;
import org.keycloak.provider.ProviderFactory;
/**
* @author <a href="mailto:demetrio@carretti.pro">Dmitry Telegin</a>
*/
public interface CorsFactory extends ProviderFactory<Cors> {
}

View file

@ -0,0 +1,53 @@
/*
* Copyright 2024 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.keycloak.services.cors;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:demetrio@carretti.pro">Dmitry Telegin</a>
*/
public class CorsSpi implements Spi {
private static final String SPI_ID = "cors";
@Override
public boolean isInternal() {
return true;
}
@Override
public String getName() {
return SPI_ID;
}
@Override
public Class<? extends Provider> getProviderClass() {
return Cors.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return CorsFactory.class;
}
}

View file

@ -90,6 +90,7 @@ org.keycloak.headers.SecurityHeadersSpi
org.keycloak.services.clientpolicy.condition.ClientPolicyConditionSpi
org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorSpi
org.keycloak.services.clientpolicy.ClientPolicyManagerSpi
org.keycloak.services.cors.CorsSpi
org.keycloak.userprofile.UserProfileSpi
org.keycloak.device.DeviceRepresentationSpi
org.keycloak.health.LoadBalancerCheckSpi

View file

@ -91,10 +91,10 @@ import org.keycloak.representations.idm.authorization.PermissionTicketToken;
import org.keycloak.services.CorsErrorResponseException;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.Urls;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.AuthenticationSessionManager;
import org.keycloak.services.managers.UserSessionManager;
import org.keycloak.services.resources.Cors;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.keycloak.sessions.RootAuthenticationSessionModel;
import org.keycloak.util.JsonSerialization;

View file

@ -60,11 +60,11 @@ import org.keycloak.representations.JsonWebToken;
import org.keycloak.saml.common.constants.GeneralConstants;
import org.keycloak.services.CorsErrorResponseException;
import org.keycloak.services.Urls;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.AuthenticationSessionManager;
import org.keycloak.services.managers.BruteForceProtector;
import org.keycloak.services.managers.UserSessionManager;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.resources.IdentityBrokerService;
import org.keycloak.services.resources.admin.AdminAuth;
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
@ -120,7 +120,7 @@ public class DefaultTokenExchangeProvider implements TokenExchangeProvider {
public Response exchange(TokenExchangeContext context) {
this.formParams = context.getFormParams();
this.session = context.getSession();
this.cors = (Cors)context.getCors();
this.cors = context.getCors();
this.realm = context.getRealm();
this.client = context.getClient();
this.event = context.getEvent();

View file

@ -42,7 +42,7 @@ import org.keycloak.protocol.oidc.endpoints.TokenRevocationEndpoint;
import org.keycloak.protocol.oidc.endpoints.UserInfoEndpoint;
import org.keycloak.protocol.oidc.ext.OIDCExtProvider;
import org.keycloak.services.CorsErrorResponseException;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.resources.RealmsResource;
import org.keycloak.services.util.CacheControlUtil;

View file

@ -47,8 +47,8 @@ import org.keycloak.protocol.oidc.utils.RedirectUtils;
import org.keycloak.services.CorsErrorResponseException;
import org.keycloak.services.ErrorPageException;
import org.keycloak.services.ServicesLogger;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.Cors;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.keycloak.util.TokenUtil;
import org.keycloak.utils.StringUtil;

View file

@ -64,12 +64,12 @@ import org.keycloak.services.ErrorPage;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.context.LogoutRequestContext;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.AuthenticationSessionManager;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.UserSessionManager;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.resources.LogoutSessionCodeChecks;
import org.keycloak.services.resources.SessionCodeChecks;
import org.keycloak.services.util.LocaleUtil;

View file

@ -85,13 +85,13 @@ import org.keycloak.services.clientpolicy.context.TokenRefreshContext;
import org.keycloak.services.clientpolicy.context.TokenRefreshResponseContext;
import org.keycloak.services.clientpolicy.context.TokenRequestContext;
import org.keycloak.services.clientpolicy.context.TokenResponseContext;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.AuthenticationSessionManager;
import org.keycloak.services.managers.ClientManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.UserSessionManager;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.util.AuthorizationContextUtil;
import org.keycloak.services.util.DefaultClientSessionContext;
import org.keycloak.services.util.DPoPUtil;

View file

@ -51,10 +51,10 @@ import org.keycloak.services.CorsErrorResponseException;
import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.context.TokenRevokeContext;
import org.keycloak.services.clientpolicy.context.TokenRevokeResponseContext;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.managers.UserConsentManager;
import org.keycloak.services.managers.UserSessionCrossDCManager;
import org.keycloak.services.managers.UserSessionManager;
import org.keycloak.services.resources.Cors;
import org.keycloak.util.TokenUtil;
/**

View file

@ -56,8 +56,8 @@ import org.keycloak.representations.dpop.DPoP;
import org.keycloak.services.Urls;
import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.context.UserInfoRequestContext;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.util.DPoPUtil;
import org.keycloak.services.util.DefaultClientSessionContext;
import org.keycloak.services.util.MtlsHoKTokenUtil;

View file

@ -54,9 +54,9 @@ import org.keycloak.services.CorsErrorResponseException;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.Urls;
import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.UserConsentManager;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.util.DefaultClientSessionContext;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.keycloak.sessions.RootAuthenticationSessionModel;

View file

@ -29,7 +29,7 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.AccessToken;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.cors.Cors;
import org.keycloak.util.TokenUtil;
/**

View file

@ -49,9 +49,9 @@ import org.keycloak.protocol.oidc.utils.PkceUtils;
import org.keycloak.services.CorsErrorResponseException;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.UserSessionCrossDCManager;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.resources.RealmsResource;
import org.keycloak.services.util.DefaultClientSessionContext;
import org.keycloak.sessions.AuthenticationSessionModel;

View file

@ -50,9 +50,9 @@ import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.ServicesLogger;
import org.keycloak.services.Urls;
import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resource.RealmResourceProvider;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.util.CacheControlUtil;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.keycloak.util.JsonSerialization;

View file

@ -31,7 +31,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
import org.keycloak.services.CorsErrorResponseException;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.cors.Cors;
public abstract class AbstractParEndpoint {

View file

@ -32,7 +32,7 @@ import org.keycloak.protocol.oidc.par.ParResponse;
import org.keycloak.protocol.oidc.par.clientpolicy.context.PushedAuthorizationRequestContext;
import org.keycloak.protocol.oidc.par.endpoints.request.ParEndpointRequestParserProcessor;
import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.cors.Cors;
import org.keycloak.utils.ProfileHelper;
import jakarta.ws.rs.Consumes;

View file

@ -31,7 +31,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.services.CorsErrorResponseException;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.cors.Cors;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response;

View file

@ -9,7 +9,7 @@ import org.keycloak.events.Errors;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.services.CorsErrorResponseException;
import org.keycloak.events.EventBuilder;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.cors.Cors;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;

View file

@ -18,7 +18,7 @@
package org.keycloak.services;
import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.cors.Cors;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;

View file

@ -0,0 +1,53 @@
/*
* Copyright 2024 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.services.cors;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
/**
* @author <a href="mailto:demetrio@carretti.pro">Dmitry Telegin</a>
*/
public class DefaultCorsFactory implements CorsFactory {
private static final String PROVIDER_ID = "default";
@Override
public Cors create(KeycloakSession session) {
return new DefaultCorsImpl(session.getContext().getHttpRequest());
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return PROVIDER_ID;
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* Copyright 2024 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -14,16 +14,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.services.resources;
package org.keycloak.services.cors;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
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.jboss.logging.Logger;
import org.keycloak.http.HttpRequest;
import org.keycloak.common.util.CollectionUtil;
@ -36,26 +38,9 @@ import org.keycloak.representations.AccessToken;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class Cors {
public class DefaultCorsImpl implements Cors {
private static final Logger logger = Logger.getLogger(Cors.class);
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";
public static final String ORIGIN_HEADER = "Origin";
public static final 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";
public static final String ACCESS_CONTROL_ALLOW_ORIGIN_WILDCARD = "*";
public static final String INCLUDE_REDIRECTS = "+";
private static final Logger logger = Logger.getLogger(DefaultCorsImpl.class);
private HttpRequest request;
private ResponseBuilder builder;
@ -66,43 +51,41 @@ public class Cors {
private boolean preflight;
private boolean auth;
public Cors(HttpRequest request, ResponseBuilder response) {
this.request = request;
this.builder = response;
}
public Cors(HttpRequest request) {
DefaultCorsImpl(HttpRequest request) {
this.request = request;
}
public static Cors add(HttpRequest request, ResponseBuilder response) {
return new Cors(request, response);
}
public static Cors add(HttpRequest request) {
return new Cors(request);
@Override
public Cors request(HttpRequest request) {
this.request = request;
return this;
}
@Override
public Cors builder(ResponseBuilder builder) {
this.builder = builder;
return this;
}
@Override
public Cors preflight() {
preflight = true;
return this;
}
@Override
public Cors auth() {
auth = true;
return this;
}
@Override
public Cors allowAllOrigins() {
allowedOrigins = Collections.singleton(ACCESS_CONTROL_ALLOW_ORIGIN_WILDCARD);
return this;
}
@Override
public Cors allowedOrigins(KeycloakSession session, ClientModel client) {
if (client != null) {
allowedOrigins = WebOriginsUtils.resolveValidWebOrigins(session, client);
@ -110,6 +93,7 @@ public class Cors {
return this;
}
@Override
public Cors allowedOrigins(AccessToken token) {
if (token != null) {
allowedOrigins = token.getAllowedOrigins();
@ -117,6 +101,7 @@ public class Cors {
return this;
}
@Override
public Cors allowedOrigins(String... allowedOrigins) {
if (allowedOrigins != null && allowedOrigins.length > 0) {
this.allowedOrigins = new HashSet<>(Arrays.asList(allowedOrigins));
@ -124,39 +109,56 @@ public class Cors {
return this;
}
@Override
public Cors allowedMethods(String... allowedMethods) {
this.allowedMethods = new HashSet<>(Arrays.asList(allowedMethods));
return this;
}
@Override
public Cors exposedHeaders(String... exposedHeaders) {
this.exposedHeaders = new HashSet<>(Arrays.asList(exposedHeaders));
return this;
}
@Override
public Response build() {
build(builder::header);
if (builder == null) {
throw new IllegalStateException("builder is not set");
}
if (build(builder::header)) {
logger.debug("Added CORS headers to response");
}
return builder.build();
}
public void build(HttpResponse response) {
build(response::addHeader);
@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) {
if (request == null) {
throw new IllegalStateException("request is not set");
}
public void build(BiConsumer<String, String> addHeader) {
String origin = request.getHttpHeaders().getRequestHeaders().getFirst(ORIGIN_HEADER);
if (origin == null) {
logger.trace("No origin header ignoring");
return;
logger.trace("No Origin header, ignoring");
return false;
}
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;
return false;
}
addHeader.accept(ACCESS_CONTROL_ALLOW_ORIGIN, origin);
@ -186,6 +188,12 @@ public class Cors {
if (preflight) {
addHeader.accept(ACCESS_CONTROL_MAX_AGE, String.valueOf(DEFAULT_MAX_AGE));
}
return true;
}
@Override
public void close() {
}
}

View file

@ -82,6 +82,7 @@ import org.keycloak.services.managers.AuthenticationSessionManager;
import org.keycloak.services.managers.BruteForceProtector;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.resources.account.AccountConsole;
import org.keycloak.services.util.AuthenticationFlowURLHelper;
import org.keycloak.services.util.BrowserHistoryHelper;

View file

@ -21,6 +21,7 @@ import org.keycloak.common.Version;
import org.keycloak.encoding.ResourceEncodingHelper;
import org.keycloak.encoding.ResourceEncodingProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.util.CacheControlUtil;
import org.keycloak.utils.MediaType;

View file

@ -25,6 +25,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.representations.idm.PublishedRealmRepresentation;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.Urls;
import jakarta.ws.rs.GET;

View file

@ -31,6 +31,7 @@ import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.LoginProtocolFactory;
import org.keycloak.services.CorsErrorResponseException;
import org.keycloak.services.clientregistration.ClientRegistrationService;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resource.RealmResourceProvider;
import org.keycloak.services.resources.account.AccountLoader;

View file

@ -30,6 +30,7 @@ import org.keycloak.encoding.ResourceEncodingProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.services.ServicesLogger;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.util.CacheControlUtil;
import org.keycloak.services.util.LocaleUtil;
import org.keycloak.theme.Theme;

View file

@ -26,11 +26,11 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.Auth;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.resource.AccountResourceProvider;
import org.keycloak.services.resources.Cors;
import org.keycloak.theme.Theme;
import jakarta.ws.rs.HttpMethod;

View file

@ -1,7 +1,7 @@
package org.keycloak.services.resources.account;
import org.keycloak.http.HttpRequest;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.cors.Cors;
import jakarta.ws.rs.OPTIONS;
import jakarta.ws.rs.Path;

View file

@ -56,9 +56,9 @@ import org.keycloak.representations.account.AccountLinkUriRepresentation;
import org.keycloak.representations.account.LinkedAccountRepresentation;
import org.keycloak.services.ErrorResponse;
import org.keycloak.services.Urls;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.managers.Auth;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.validation.Validation;
import static org.keycloak.models.Constants.ACCOUNT_CONSOLE_CLIENT_ID;

View file

@ -36,11 +36,11 @@ import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.services.Urls;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.ClientManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.Cors;
import org.keycloak.theme.FreeMarkerException;
import org.keycloak.theme.Theme;
import org.keycloak.theme.freemarker.FreeMarkerProvider;

View file

@ -1,7 +1,7 @@
package org.keycloak.services.resources.admin;
import org.keycloak.http.HttpRequest;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.cors.Cors;
import jakarta.ws.rs.OPTIONS;
import jakarta.ws.rs.Path;

View file

@ -30,10 +30,10 @@ import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.AccessToken;
import org.keycloak.services.ForbiddenException;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.resources.admin.info.ServerInfoAdminResource;
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
import org.keycloak.theme.Theme;

View file

@ -23,7 +23,7 @@ import jakarta.ws.rs.core.Response;
import org.keycloak.http.HttpRequest;
import org.keycloak.models.KeycloakSession;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.cors.Cors;
public class RealmsAdminResourcePreflight extends RealmsAdminResource {

View file

@ -38,7 +38,7 @@ import jakarta.ws.rs.core.Response;
import org.keycloak.OAuthErrorException;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.cors.Cors;
import static jakarta.ws.rs.core.HttpHeaders.WWW_AUTHENTICATE;

View file

@ -0,0 +1,20 @@
#
# Copyright 2024 Red Hat, Inc. and/or its affiliates
# and other contributors as indicated by the @author tags.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
org.keycloak.services.cors.DefaultCorsFactory

View file

@ -7,7 +7,7 @@ import org.apache.http.impl.client.HttpClientBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.cors.Cors;
import java.io.IOException;

View file

@ -77,7 +77,7 @@ import org.keycloak.representations.oidc.TokenMetadataRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.condition.ClientAccessTypeConditionFactory;
import org.keycloak.services.clientpolicy.executor.DPoPBindEnforcerExecutorFactory;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.cors.Cors;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.AssertEvents;

View file

@ -41,7 +41,7 @@ import org.keycloak.representations.IDToken;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.clientregistration.ClientRegistrationService;
import org.keycloak.services.clientregistration.oidc.OIDCClientRegistrationProviderFactory;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.cors.Cors;
import org.keycloak.services.resources.RealmsResource;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.Assert;