[KEYCLOAK-12192] - Missing Input Validation in IDP Authorization URLs
This commit is contained in:
parent
0cf0955318
commit
2f489a41eb
41 changed files with 401 additions and 34 deletions
|
@ -18,10 +18,14 @@
|
||||||
package org.keycloak.common.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.net.URL;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.keycloak.common.enums.SslRequired;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
*/
|
*/
|
||||||
|
@ -84,4 +88,28 @@ public class UriUtils {
|
||||||
public static String stripQueryParam(String url, String name){
|
public static String stripQueryParam(String url, String name){
|
||||||
return url.replaceFirst("[\\?&]"+name+"=[^&]*$|"+name+"=[^&]*&", "");
|
return url.replaceFirst("[\\?&]"+name+"=[^&]*$|"+name+"=[^&]*&", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void checkUrl(SslRequired sslRequired, String url, String name) throws IllegalArgumentException{
|
||||||
|
if (url == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
URL parsed;
|
||||||
|
|
||||||
|
try {
|
||||||
|
parsed = new URL(url);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new IllegalArgumentException("The url [" + name + "] is malformed", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
String protocol = parsed.getProtocol().toLowerCase();
|
||||||
|
|
||||||
|
if (!("http".equalsIgnoreCase(protocol) || "https".equalsIgnoreCase(protocol))) {
|
||||||
|
throw new IllegalArgumentException("Invalid protocol/scheme for url [" + name + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!"https".equals(protocol) && sslRequired.isRequired(url)) {
|
||||||
|
throw new IllegalArgumentException("The url [" + name + "] requires secure connections");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,4 +54,14 @@ public interface IdentityProviderFactory<T extends IdentityProvider> extends Pro
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
Map<String, String> parseConfig(KeycloakSession session, InputStream inputStream);
|
Map<String, String> parseConfig(KeycloakSession session, InputStream inputStream);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a provider specific {@link IdentityProviderModel} instance.
|
||||||
|
*
|
||||||
|
* <p>Providers may want to implement their own {@link IdentityProviderModel} type so that validations
|
||||||
|
* can be performed when managing the provider configuration
|
||||||
|
*
|
||||||
|
* @return the provider specific instance
|
||||||
|
*/
|
||||||
|
<C extends IdentityProviderModel> C createConfig();
|
||||||
}
|
}
|
|
@ -49,6 +49,9 @@ import org.keycloak.authorization.store.ResourceServerStore;
|
||||||
import org.keycloak.authorization.store.ResourceStore;
|
import org.keycloak.authorization.store.ResourceStore;
|
||||||
import org.keycloak.authorization.store.ScopeStore;
|
import org.keycloak.authorization.store.ScopeStore;
|
||||||
import org.keycloak.authorization.store.StoreFactory;
|
import org.keycloak.authorization.store.StoreFactory;
|
||||||
|
import org.keycloak.broker.provider.IdentityProvider;
|
||||||
|
import org.keycloak.broker.provider.IdentityProviderFactory;
|
||||||
|
import org.keycloak.broker.social.SocialIdentityProvider;
|
||||||
import org.keycloak.common.enums.SslRequired;
|
import org.keycloak.common.enums.SslRequired;
|
||||||
import org.keycloak.common.util.MultivaluedHashMap;
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
import org.keycloak.common.util.UriUtils;
|
import org.keycloak.common.util.UriUtils;
|
||||||
|
@ -285,7 +288,7 @@ public class RepresentationToModel {
|
||||||
DefaultRequiredActions.addActions(newRealm);
|
DefaultRequiredActions.addActions(newRealm);
|
||||||
}
|
}
|
||||||
|
|
||||||
importIdentityProviders(rep, newRealm);
|
importIdentityProviders(rep, newRealm, session);
|
||||||
importIdentityProviderMappers(rep, newRealm);
|
importIdentityProviderMappers(rep, newRealm);
|
||||||
|
|
||||||
Map<String, ClientScopeModel> clientScopes = new HashMap<>();
|
Map<String, ClientScopeModel> clientScopes = new HashMap<>();
|
||||||
|
@ -1856,10 +1859,10 @@ public class RepresentationToModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void importIdentityProviders(RealmRepresentation rep, RealmModel newRealm) {
|
private static void importIdentityProviders(RealmRepresentation rep, RealmModel newRealm, KeycloakSession session) {
|
||||||
if (rep.getIdentityProviders() != null) {
|
if (rep.getIdentityProviders() != null) {
|
||||||
for (IdentityProviderRepresentation representation : rep.getIdentityProviders()) {
|
for (IdentityProviderRepresentation representation : rep.getIdentityProviders()) {
|
||||||
newRealm.addIdentityProvider(toModel(newRealm, representation));
|
newRealm.addIdentityProvider(toModel(newRealm, representation, session));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1872,8 +1875,20 @@ public class RepresentationToModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IdentityProviderModel toModel(RealmModel realm, IdentityProviderRepresentation representation) {
|
public static IdentityProviderModel toModel(RealmModel realm, IdentityProviderRepresentation representation, KeycloakSession session) {
|
||||||
IdentityProviderModel identityProviderModel = new IdentityProviderModel();
|
IdentityProviderFactory providerFactory = (IdentityProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(
|
||||||
|
IdentityProvider.class, representation.getProviderId());
|
||||||
|
|
||||||
|
if (providerFactory == null) {
|
||||||
|
providerFactory = (IdentityProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(
|
||||||
|
SocialIdentityProvider.class, representation.getProviderId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (providerFactory == null) {
|
||||||
|
throw new IllegalArgumentException("Invalid identity provider id [" + representation.getProviderId() + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityProviderModel identityProviderModel = providerFactory.createConfig();
|
||||||
|
|
||||||
identityProviderModel.setInternalId(representation.getInternalId());
|
identityProviderModel.setInternalId(representation.getInternalId());
|
||||||
identityProviderModel.setAlias(representation.getAlias());
|
identityProviderModel.setAlias(representation.getAlias());
|
||||||
|
@ -1909,6 +1924,8 @@ public class RepresentationToModel {
|
||||||
identityProviderModel.setPostBrokerLoginFlowId(flowModel.getId());
|
identityProviderModel.setPostBrokerLoginFlowId(flowModel.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
identityProviderModel.validate(realm);
|
||||||
|
|
||||||
return identityProviderModel;
|
return identityProviderModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -197,4 +197,14 @@ public class IdentityProviderModel implements Serializable {
|
||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Validates this configuration.
|
||||||
|
*
|
||||||
|
* <p>Sub-classes can override this method in order to enforce provider specific validations.
|
||||||
|
*
|
||||||
|
* @param realm the realm
|
||||||
|
*/
|
||||||
|
public void validate(RealmModel realm) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,10 @@ public class KeycloakOIDCIdentityProviderFactory extends AbstractIdentityProvide
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> parseConfig(KeycloakSession session, InputStream inputStream) {
|
public Map<String, String> parseConfig(KeycloakSession session, InputStream inputStream) {
|
||||||
return OIDCIdentityProviderFactory.parseOIDCConfig(session, inputStream);
|
return OIDCIdentityProviderFactory.parseOIDCConfig(session, inputStream);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OIDCIdentityProviderConfig createConfig() {
|
||||||
|
return new OIDCIdentityProviderConfig();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,12 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.broker.oidc;
|
package org.keycloak.broker.oidc;
|
||||||
|
|
||||||
|
import static org.keycloak.common.util.UriUtils.checkUrl;
|
||||||
|
|
||||||
|
import org.keycloak.common.enums.SslRequired;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,6 +33,10 @@ public class OAuth2IdentityProviderConfig extends IdentityProviderModel {
|
||||||
super(model);
|
super(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OAuth2IdentityProviderConfig() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
public String getAuthorizationUrl() {
|
public String getAuthorizationUrl() {
|
||||||
return getConfig().get("authorizationUrl");
|
return getConfig().get("authorizationUrl");
|
||||||
}
|
}
|
||||||
|
@ -123,4 +132,13 @@ public class OAuth2IdentityProviderConfig extends IdentityProviderModel {
|
||||||
public void setForwardParameters(String forwardParameters) {
|
public void setForwardParameters(String forwardParameters) {
|
||||||
getConfig().put("forwardParameters", forwardParameters);
|
getConfig().put("forwardParameters", forwardParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate(RealmModel realm) {
|
||||||
|
SslRequired sslRequired = realm.getSslRequired();
|
||||||
|
|
||||||
|
checkUrl(sslRequired, getAuthorizationUrl(), "authorization_url");
|
||||||
|
checkUrl(sslRequired, getTokenUrl(), "token_url");
|
||||||
|
checkUrl(sslRequired, getUserInfoUrl(), "userinfo_url");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,12 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.broker.oidc;
|
package org.keycloak.broker.oidc;
|
||||||
|
|
||||||
|
import static org.keycloak.common.util.UriUtils.checkUrl;
|
||||||
|
|
||||||
|
import org.keycloak.common.enums.SslRequired;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Pedro Igor
|
* @author Pedro Igor
|
||||||
|
@ -33,6 +38,10 @@ public class OIDCIdentityProviderConfig extends OAuth2IdentityProviderConfig {
|
||||||
super(identityProviderModel);
|
super(identityProviderModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OIDCIdentityProviderConfig() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
public String getPrompt() {
|
public String getPrompt() {
|
||||||
return getConfig().get("prompt");
|
return getConfig().get("prompt");
|
||||||
}
|
}
|
||||||
|
@ -122,4 +131,12 @@ public class OIDCIdentityProviderConfig extends OAuth2IdentityProviderConfig {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate(RealmModel realm) {
|
||||||
|
super.validate(realm);
|
||||||
|
SslRequired sslRequired = realm.getSslRequired();
|
||||||
|
checkUrl(sslRequired, getJwksUrl(), "jwks_url");
|
||||||
|
checkUrl(sslRequired, getLogoutUrl(), "logout_url");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,11 @@ public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory
|
||||||
return new OIDCIdentityProvider(session, new OIDCIdentityProviderConfig(model));
|
return new OIDCIdentityProvider(session, new OIDCIdentityProviderConfig(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OIDCIdentityProviderConfig createConfig() {
|
||||||
|
return new OIDCIdentityProviderConfig();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return PROVIDER_ID;
|
return PROVIDER_ID;
|
||||||
|
@ -60,7 +65,7 @@ public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("failed to load openid connect metadata", e);
|
throw new RuntimeException("failed to load openid connect metadata", e);
|
||||||
}
|
}
|
||||||
OIDCIdentityProviderConfig config = new OIDCIdentityProviderConfig(new IdentityProviderModel());
|
OIDCIdentityProviderConfig config = new OIDCIdentityProviderConfig();
|
||||||
config.setIssuer(rep.getIssuer());
|
config.setIssuer(rep.getIssuer());
|
||||||
config.setLogoutUrl(rep.getLogoutEndpoint());
|
config.setLogoutUrl(rep.getLogoutEndpoint());
|
||||||
config.setAuthorizationUrl(rep.getAuthorizationEndpoint());
|
config.setAuthorizationUrl(rep.getAuthorizationEndpoint());
|
||||||
|
|
|
@ -16,8 +16,13 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.broker.saml;
|
package org.keycloak.broker.saml;
|
||||||
|
|
||||||
|
import static org.keycloak.common.util.UriUtils.checkUrl;
|
||||||
|
|
||||||
|
import org.keycloak.common.enums.SslRequired;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
|
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.protocol.saml.SamlPrincipalType;
|
import org.keycloak.protocol.saml.SamlPrincipalType;
|
||||||
import org.keycloak.saml.common.util.XmlKeyInfoKeyNameTransformer;
|
import org.keycloak.saml.common.util.XmlKeyInfoKeyNameTransformer;
|
||||||
|
|
||||||
|
@ -276,4 +281,11 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
|
||||||
getConfig().put(PRINCIPAL_ATTRIBUTE, principalAttribute);
|
getConfig().put(PRINCIPAL_ATTRIBUTE, principalAttribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate(RealmModel realm) {
|
||||||
|
SslRequired sslRequired = realm.getSslRequired();
|
||||||
|
|
||||||
|
checkUrl(sslRequired, getSingleLogoutServiceUrl(), SINGLE_LOGOUT_SERVICE_URL);
|
||||||
|
checkUrl(sslRequired, getSingleSignOnServiceUrl(), SINGLE_SIGN_ON_SERVICE_URL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,11 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory
|
||||||
return new SAMLIdentityProvider(session, new SAMLIdentityProviderConfig(model), destinationValidator);
|
return new SAMLIdentityProvider(session, new SAMLIdentityProviderConfig(model), destinationValidator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SAMLIdentityProviderConfig createConfig() {
|
||||||
|
return new SAMLIdentityProviderConfig();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> parseConfig(KeycloakSession session, InputStream inputStream) {
|
public Map<String, String> parseConfig(KeycloakSession session, InputStream inputStream) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -72,7 +72,7 @@ public class IdentityProvidersPartialImport extends AbstractPartialImport<Identi
|
||||||
@Override
|
@Override
|
||||||
public void create(RealmModel realm, KeycloakSession session, IdentityProviderRepresentation idpRep) {
|
public void create(RealmModel realm, KeycloakSession session, IdentityProviderRepresentation idpRep) {
|
||||||
idpRep.setInternalId(KeycloakModelUtils.generateId());
|
idpRep.setInternalId(KeycloakModelUtils.generateId());
|
||||||
IdentityProviderModel identityProvider = RepresentationToModel.toModel(realm, idpRep);
|
IdentityProviderModel identityProvider = RepresentationToModel.toModel(realm, idpRep, session);
|
||||||
realm.addIdentityProvider(identityProvider);
|
realm.addIdentityProvider(identityProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.services.resources.admin;
|
package org.keycloak.services.resources.admin;
|
||||||
|
|
||||||
|
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||||
import javax.ws.rs.NotFoundException;
|
import javax.ws.rs.NotFoundException;
|
||||||
|
@ -162,6 +164,8 @@ public class IdentityProviderResource {
|
||||||
adminEvent.operation(OperationType.UPDATE).resourcePath(session.getContext().getUri()).representation(providerRep).success();
|
adminEvent.operation(OperationType.UPDATE).resourcePath(session.getContext().getUri()).representation(providerRep).success();
|
||||||
|
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return ErrorResponse.error("Invalid request", BAD_REQUEST);
|
||||||
} catch (ModelDuplicateException e) {
|
} catch (ModelDuplicateException e) {
|
||||||
return ErrorResponse.exists("Identity Provider " + providerRep.getAlias() + " already exists");
|
return ErrorResponse.exists("Identity Provider " + providerRep.getAlias() + " already exists");
|
||||||
}
|
}
|
||||||
|
@ -176,7 +180,7 @@ public class IdentityProviderResource {
|
||||||
lookUpProviderIdByAlias(realm, providerRep);
|
lookUpProviderIdByAlias(realm, providerRep);
|
||||||
}
|
}
|
||||||
|
|
||||||
IdentityProviderModel updated = RepresentationToModel.toModel(realm, providerRep);
|
IdentityProviderModel updated = RepresentationToModel.toModel(realm, providerRep, session);
|
||||||
|
|
||||||
if (updated.getConfig() != null && ComponentRepresentation.SECRET_VALUE.equals(updated.getConfig().get("clientSecret"))) {
|
if (updated.getConfig() != null && ComponentRepresentation.SECRET_VALUE.equals(updated.getConfig().get("clientSecret"))) {
|
||||||
updated.getConfig().put("clientSecret", identityProviderModel.getConfig() != null ? identityProviderModel.getConfig().get("clientSecret") : null);
|
updated.getConfig().put("clientSecret", identityProviderModel.getConfig() != null ? identityProviderModel.getConfig().get("clientSecret") : null);
|
||||||
|
|
|
@ -183,7 +183,7 @@ public class IdentityProvidersResource {
|
||||||
this.auth.realm().requireManageIdentityProviders();
|
this.auth.realm().requireManageIdentityProviders();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
IdentityProviderModel identityProvider = RepresentationToModel.toModel(realm, representation);
|
IdentityProviderModel identityProvider = RepresentationToModel.toModel(realm, representation, session);
|
||||||
this.realm.addIdentityProvider(identityProvider);
|
this.realm.addIdentityProvider(identityProvider);
|
||||||
|
|
||||||
representation.setInternalId(identityProvider.getInternalId());
|
representation.setInternalId(identityProvider.getInternalId());
|
||||||
|
@ -191,6 +191,8 @@ public class IdentityProvidersResource {
|
||||||
.representation(StripSecretsUtils.strip(representation)).success();
|
.representation(StripSecretsUtils.strip(representation)).success();
|
||||||
|
|
||||||
return Response.created(session.getContext().getUri().getAbsolutePathBuilder().path(representation.getAlias()).build()).build();
|
return Response.created(session.getContext().getUri().getAbsolutePathBuilder().path(representation.getAlias()).build()).build();
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return ErrorResponse.error("Invalid request", BAD_REQUEST);
|
||||||
} catch (ModelDuplicateException e) {
|
} catch (ModelDuplicateException e) {
|
||||||
return ErrorResponse.exists("Identity Provider " + representation.getAlias() + " already exists");
|
return ErrorResponse.exists("Identity Provider " + representation.getAlias() + " already exists");
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,11 @@ public class BitbucketIdentityProviderFactory extends AbstractIdentityProviderFa
|
||||||
return new BitbucketIdentityProvider(session, new OAuth2IdentityProviderConfig(model));
|
return new BitbucketIdentityProvider(session, new OAuth2IdentityProviderConfig(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OAuth2IdentityProviderConfig createConfig() {
|
||||||
|
return new OAuth2IdentityProviderConfig();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return PROVIDER_ID;
|
return PROVIDER_ID;
|
||||||
|
|
|
@ -39,6 +39,11 @@ public class FacebookIdentityProviderFactory extends AbstractIdentityProviderFac
|
||||||
return new FacebookIdentityProvider(session, new OAuth2IdentityProviderConfig(model));
|
return new FacebookIdentityProvider(session, new OAuth2IdentityProviderConfig(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OAuth2IdentityProviderConfig createConfig() {
|
||||||
|
return new OAuth2IdentityProviderConfig();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return PROVIDER_ID;
|
return PROVIDER_ID;
|
||||||
|
|
|
@ -39,6 +39,11 @@ public class GitHubIdentityProviderFactory extends AbstractIdentityProviderFacto
|
||||||
return new GitHubIdentityProvider(session, new OAuth2IdentityProviderConfig(model));
|
return new GitHubIdentityProvider(session, new OAuth2IdentityProviderConfig(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OAuth2IdentityProviderConfig createConfig() {
|
||||||
|
return new OAuth2IdentityProviderConfig();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return PROVIDER_ID;
|
return PROVIDER_ID;
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.social.gitlab;
|
package org.keycloak.social.gitlab;
|
||||||
|
|
||||||
import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
|
|
||||||
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
||||||
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
|
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
|
||||||
import org.keycloak.broker.social.SocialIdentityProviderFactory;
|
import org.keycloak.broker.social.SocialIdentityProviderFactory;
|
||||||
|
@ -40,6 +39,11 @@ public class GitLabIdentityProviderFactory extends AbstractIdentityProviderFacto
|
||||||
return new GitLabIdentityProvider(session, new OIDCIdentityProviderConfig(model));
|
return new GitLabIdentityProvider(session, new OIDCIdentityProviderConfig(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OIDCIdentityProviderConfig createConfig() {
|
||||||
|
return new OIDCIdentityProviderConfig();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return PROVIDER_ID;
|
return PROVIDER_ID;
|
||||||
|
|
|
@ -28,6 +28,10 @@ public class GoogleIdentityProviderConfig extends OIDCIdentityProviderConfig {
|
||||||
super(model);
|
super(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GoogleIdentityProviderConfig() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isUserIp() {
|
public boolean isUserIp() {
|
||||||
String userIp = getConfig().get("userIp");
|
String userIp = getConfig().get("userIp");
|
||||||
return userIp == null ? false : Boolean.valueOf(userIp);
|
return userIp == null ? false : Boolean.valueOf(userIp);
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.social.google;
|
package org.keycloak.social.google;
|
||||||
|
|
||||||
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
|
||||||
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
|
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.broker.social.SocialIdentityProviderFactory;
|
import org.keycloak.broker.social.SocialIdentityProviderFactory;
|
||||||
|
@ -39,6 +38,11 @@ public class GoogleIdentityProviderFactory extends AbstractIdentityProviderFacto
|
||||||
return new GoogleIdentityProvider(session, new GoogleIdentityProviderConfig(model));
|
return new GoogleIdentityProvider(session, new GoogleIdentityProviderConfig(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GoogleIdentityProviderConfig createConfig() {
|
||||||
|
return new GoogleIdentityProviderConfig();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return PROVIDER_ID;
|
return PROVIDER_ID;
|
||||||
|
|
|
@ -39,6 +39,11 @@ public class InstagramIdentityProviderFactory extends AbstractIdentityProviderFa
|
||||||
return new InstagramIdentityProvider(session, new OAuth2IdentityProviderConfig(model));
|
return new InstagramIdentityProvider(session, new OAuth2IdentityProviderConfig(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OAuth2IdentityProviderConfig createConfig() {
|
||||||
|
return new OAuth2IdentityProviderConfig();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return PROVIDER_ID;
|
return PROVIDER_ID;
|
||||||
|
|
|
@ -40,6 +40,11 @@ public class LinkedInIdentityProviderFactory extends AbstractIdentityProviderFac
|
||||||
return new LinkedInIdentityProvider(session, new OAuth2IdentityProviderConfig(model));
|
return new LinkedInIdentityProvider(session, new OAuth2IdentityProviderConfig(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OAuth2IdentityProviderConfig createConfig() {
|
||||||
|
return new OAuth2IdentityProviderConfig();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return PROVIDER_ID;
|
return PROVIDER_ID;
|
||||||
|
|
|
@ -39,6 +39,11 @@ public class MicrosoftIdentityProviderFactory extends AbstractIdentityProviderFa
|
||||||
return new MicrosoftIdentityProvider(session, new OAuth2IdentityProviderConfig(model));
|
return new MicrosoftIdentityProvider(session, new OAuth2IdentityProviderConfig(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OAuth2IdentityProviderConfig createConfig() {
|
||||||
|
return new OAuth2IdentityProviderConfig();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return PROVIDER_ID;
|
return PROVIDER_ID;
|
||||||
|
|
|
@ -10,6 +10,10 @@ public class OpenshiftV3IdentityProviderConfig extends OAuth2IdentityProviderCon
|
||||||
super(identityProviderModel);
|
super(identityProviderModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OpenshiftV3IdentityProviderConfig() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public String getBaseUrl() {
|
public String getBaseUrl() {
|
||||||
return getConfig().get(BASE_URL);
|
return getConfig().get(BASE_URL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,11 @@ public class OpenshiftV3IdentityProviderFactory extends AbstractIdentityProvider
|
||||||
return new OpenshiftV3IdentityProvider(keycloakSession, new OpenshiftV3IdentityProviderConfig(identityProviderModel));
|
return new OpenshiftV3IdentityProvider(keycloakSession, new OpenshiftV3IdentityProviderConfig(identityProviderModel));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OpenshiftV3IdentityProviderConfig createConfig() {
|
||||||
|
return new OpenshiftV3IdentityProviderConfig();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return PROVIDER_ID;
|
return PROVIDER_ID;
|
||||||
|
|
|
@ -19,6 +19,9 @@ public class OpenshiftV4IdentityProviderConfig extends OAuth2IdentityProviderCon
|
||||||
super(identityProviderModel);
|
super(identityProviderModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OpenshiftV4IdentityProviderConfig() {
|
||||||
|
}
|
||||||
|
|
||||||
private String trimTrailingSlash(String baseUrl) {
|
private String trimTrailingSlash(String baseUrl) {
|
||||||
if (baseUrl != null && baseUrl.endsWith("/")) {
|
if (baseUrl != null && baseUrl.endsWith("/")) {
|
||||||
baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
|
baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
|
||||||
|
|
|
@ -30,4 +30,8 @@ public class OpenshiftV4IdentityProviderFactory extends AbstractIdentityProvider
|
||||||
return PROVIDER_ID;
|
return PROVIDER_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OpenshiftV4IdentityProviderConfig createConfig() {
|
||||||
|
return new OpenshiftV4IdentityProviderConfig();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,10 @@ public class PayPalIdentityProviderConfig extends OAuth2IdentityProviderConfig {
|
||||||
super(model);
|
super(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PayPalIdentityProviderConfig() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public boolean targetSandbox() {
|
public boolean targetSandbox() {
|
||||||
String sandbox = getConfig().get("sandbox");
|
String sandbox = getConfig().get("sandbox");
|
||||||
return sandbox == null ? false : Boolean.valueOf(sandbox);
|
return sandbox == null ? false : Boolean.valueOf(sandbox);
|
||||||
|
|
|
@ -39,6 +39,11 @@ public class PayPalIdentityProviderFactory extends AbstractIdentityProviderFacto
|
||||||
return new PayPalIdentityProvider(session, new PayPalIdentityProviderConfig(model));
|
return new PayPalIdentityProvider(session, new PayPalIdentityProviderConfig(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PayPalIdentityProviderConfig createConfig() {
|
||||||
|
return new PayPalIdentityProviderConfig();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return PROVIDER_ID;
|
return PROVIDER_ID;
|
||||||
|
|
|
@ -28,6 +28,10 @@ public class StackOverflowIdentityProviderConfig extends OAuth2IdentityProviderC
|
||||||
super(model);
|
super(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StackOverflowIdentityProviderConfig() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public String getKey() {
|
public String getKey() {
|
||||||
return getConfig().get("key");
|
return getConfig().get("key");
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,11 @@ public class StackoverflowIdentityProviderFactory extends
|
||||||
return new StackoverflowIdentityProvider(session, new StackOverflowIdentityProviderConfig(model));
|
return new StackoverflowIdentityProvider(session, new StackOverflowIdentityProviderConfig(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StackOverflowIdentityProviderConfig createConfig() {
|
||||||
|
return new StackOverflowIdentityProviderConfig();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return PROVIDER_ID;
|
return PROVIDER_ID;
|
||||||
|
|
|
@ -39,6 +39,11 @@ public class TwitterIdentityProviderFactory extends AbstractIdentityProviderFact
|
||||||
return new TwitterIdentityProvider(session, new OAuth2IdentityProviderConfig(model));
|
return new TwitterIdentityProvider(session, new OAuth2IdentityProviderConfig(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OAuth2IdentityProviderConfig createConfig() {
|
||||||
|
return new OAuth2IdentityProviderConfig();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return PROVIDER_ID;
|
return PROVIDER_ID;
|
||||||
|
|
|
@ -17,9 +17,13 @@
|
||||||
|
|
||||||
package org.keycloak.testsuite.admin;
|
package org.keycloak.testsuite.admin;
|
||||||
|
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
|
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.ExpectedException;
|
||||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||||
|
import org.keycloak.common.enums.SslRequired;
|
||||||
import org.keycloak.dom.saml.v2.metadata.EndpointType;
|
import org.keycloak.dom.saml.v2.metadata.EndpointType;
|
||||||
import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
|
import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
|
||||||
import org.keycloak.dom.saml.v2.metadata.IndexedEndpointType;
|
import org.keycloak.dom.saml.v2.metadata.IndexedEndpointType;
|
||||||
|
@ -35,12 +39,15 @@ import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderMapperTypeRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderMapperTypeRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.saml.common.exceptions.ParsingException;
|
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||||
import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
|
import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
|
||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
|
import org.keycloak.testsuite.broker.OIDCIdentityProviderConfigRep;
|
||||||
import org.keycloak.testsuite.util.AdminEventPaths;
|
import org.keycloak.testsuite.util.AdminEventPaths;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
|
import javax.ws.rs.ClientErrorException;
|
||||||
import javax.ws.rs.NotFoundException;
|
import javax.ws.rs.NotFoundException;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
@ -59,6 +66,7 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
@ -75,6 +83,7 @@ import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.keycloak.testsuite.arquillian.AuthServerTestEnricher.AUTH_SERVER_SSL_REQUIRED;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
@ -102,6 +111,9 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
||||||
+ "vOU8TyqfZF5jpv0IcrviLl/DoFrbjByeHR+pu/vClcAOjL/u7oQELuuTfNsBI4tpexUj5G8q/YbEz0gk7idf"
|
+ "vOU8TyqfZF5jpv0IcrviLl/DoFrbjByeHR+pu/vClcAOjL/u7oQELuuTfNsBI4tpexUj5G8q/YbEz0gk7idf"
|
||||||
+ "LXrAUVcsR73oTngrhRfwUSmPrjjK0kjcRb6HL9V/+wh3R/6mEd59U08ExT8N38rhmn0CI3ehMdebReprP7U8=";
|
+ "LXrAUVcsR73oTngrhRfwUSmPrjjK0kjcRb6HL9V/+wh3R/6mEd59U08ExT8N38rhmn0CI3ehMdebReprP7U8=";
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public ExpectedException expectedException = ExpectedException.none();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFindAll() {
|
public void testFindAll() {
|
||||||
create(createRep("google", "google"));
|
create(createRep("google", "google"));
|
||||||
|
@ -143,6 +155,71 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
||||||
assertEquals(ComponentRepresentation.SECRET_VALUE, rep.getConfig().get("clientSecret"));
|
assertEquals(ComponentRepresentation.SECRET_VALUE, rep.getConfig().get("clientSecret"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failCreateInvalidUrl() {
|
||||||
|
RealmRepresentation realmRep = realm.toRepresentation();
|
||||||
|
|
||||||
|
realmRep.setSslRequired(SslRequired.ALL.name());
|
||||||
|
|
||||||
|
try {
|
||||||
|
realm.update(realmRep);
|
||||||
|
|
||||||
|
IdentityProviderRepresentation newIdentityProvider = createRep("new-identity-provider", "oidc");
|
||||||
|
|
||||||
|
newIdentityProvider.getConfig().put("clientId", "clientId");
|
||||||
|
newIdentityProvider.getConfig().put("clientSecret", "some secret value");
|
||||||
|
|
||||||
|
OIDCIdentityProviderConfigRep oidcConfig = new OIDCIdentityProviderConfigRep(newIdentityProvider);
|
||||||
|
|
||||||
|
oidcConfig.setAuthorizationUrl("invalid://test");
|
||||||
|
|
||||||
|
try (Response response = this.realm.identityProviders().create(newIdentityProvider)) {
|
||||||
|
assertEquals(AUTH_SERVER_SSL_REQUIRED ? Response.Status.BAD_REQUEST.getStatusCode() :
|
||||||
|
Response.Status.CREATED.getStatusCode(), response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
oidcConfig.setAuthorizationUrl(null);
|
||||||
|
oidcConfig.setTokenUrl("http://test");
|
||||||
|
|
||||||
|
try (Response response = this.realm.identityProviders().create(newIdentityProvider)) {
|
||||||
|
assertEquals(AUTH_SERVER_SSL_REQUIRED ? Response.Status.BAD_REQUEST.getStatusCode() :
|
||||||
|
Response.Status.CREATED.getStatusCode(), response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
oidcConfig.setAuthorizationUrl(null);
|
||||||
|
oidcConfig.setTokenUrl(null);
|
||||||
|
oidcConfig.setJwksUrl("http://test");
|
||||||
|
|
||||||
|
try (Response response = this.realm.identityProviders().create(newIdentityProvider)) {
|
||||||
|
assertEquals(AUTH_SERVER_SSL_REQUIRED ? Response.Status.BAD_REQUEST.getStatusCode() :
|
||||||
|
Response.Status.CREATED.getStatusCode(), response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
oidcConfig.setAuthorizationUrl(null);
|
||||||
|
oidcConfig.setTokenUrl(null);
|
||||||
|
oidcConfig.setJwksUrl(null);
|
||||||
|
oidcConfig.setLogoutUrl("http://test");
|
||||||
|
|
||||||
|
try (Response response = this.realm.identityProviders().create(newIdentityProvider)) {
|
||||||
|
assertEquals(AUTH_SERVER_SSL_REQUIRED ? Response.Status.BAD_REQUEST.getStatusCode() :
|
||||||
|
Response.Status.CREATED.getStatusCode(), response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
oidcConfig.setAuthorizationUrl(null);
|
||||||
|
oidcConfig.setTokenUrl(null);
|
||||||
|
oidcConfig.setJwksUrl(null);
|
||||||
|
oidcConfig.setLogoutUrl(null);
|
||||||
|
oidcConfig.setUserInfoUrl("http://test");
|
||||||
|
|
||||||
|
try (Response response = this.realm.identityProviders().create(newIdentityProvider)) {
|
||||||
|
assertEquals(AUTH_SERVER_SSL_REQUIRED ? Response.Status.BAD_REQUEST.getStatusCode() :
|
||||||
|
Response.Status.CREATED.getStatusCode(), response.getStatus());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
realmRep.setSslRequired(SslRequired.NONE.name());
|
||||||
|
realm.update(realmRep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateWithBasicAuth() {
|
public void testCreateWithBasicAuth() {
|
||||||
|
@ -258,6 +335,84 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
||||||
assertEquals("${vault.key}", testingClient.testing("admin-client-test").getIdentityProviderConfig("changed-alias").get("clientSecret"));
|
assertEquals("${vault.key}", testingClient.testing("admin-client-test").getIdentityProviderConfig("changed-alias").get("clientSecret"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void failUpdateInvalidUrl() {
|
||||||
|
RealmRepresentation realmRep = realm.toRepresentation();
|
||||||
|
|
||||||
|
realmRep.setSslRequired(SslRequired.ALL.name());
|
||||||
|
|
||||||
|
try {
|
||||||
|
realm.update(realmRep);
|
||||||
|
|
||||||
|
IdentityProviderRepresentation representation = createRep(UUID.randomUUID().toString(), "oidc");
|
||||||
|
|
||||||
|
representation.getConfig().put("clientId", "clientId");
|
||||||
|
representation.getConfig().put("clientSecret", "some secret value");
|
||||||
|
|
||||||
|
try (Response response = realm.identityProviders().create(representation)) {
|
||||||
|
assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityProviderResource resource = this.realm.identityProviders().get(representation.getAlias());
|
||||||
|
representation = resource.toRepresentation();
|
||||||
|
|
||||||
|
OIDCIdentityProviderConfigRep oidcConfig = new OIDCIdentityProviderConfigRep(representation);
|
||||||
|
|
||||||
|
oidcConfig.setAuthorizationUrl("invalid://test");
|
||||||
|
|
||||||
|
this.expectedException.expect(
|
||||||
|
Matchers.allOf(Matchers.instanceOf(ClientErrorException.class), Matchers.hasProperty("response",
|
||||||
|
Matchers.hasProperty("status", Matchers.is(
|
||||||
|
Response.Status.BAD_REQUEST.getStatusCode())))));
|
||||||
|
resource.update(representation);
|
||||||
|
|
||||||
|
oidcConfig.setAuthorizationUrl(null);
|
||||||
|
oidcConfig.setTokenUrl("http://test");
|
||||||
|
|
||||||
|
this.expectedException.expect(
|
||||||
|
Matchers.allOf(Matchers.instanceOf(ClientErrorException.class), Matchers.hasProperty("response",
|
||||||
|
Matchers.hasProperty("status", Matchers.is(
|
||||||
|
Response.Status.BAD_REQUEST.getStatusCode())))));
|
||||||
|
resource.update(representation);
|
||||||
|
|
||||||
|
oidcConfig.setAuthorizationUrl(null);
|
||||||
|
oidcConfig.setTokenUrl(null);
|
||||||
|
oidcConfig.setJwksUrl("http://test");
|
||||||
|
|
||||||
|
this.expectedException.expect(
|
||||||
|
Matchers.allOf(Matchers.instanceOf(ClientErrorException.class), Matchers.hasProperty("response",
|
||||||
|
Matchers.hasProperty("status", Matchers.is(
|
||||||
|
Response.Status.BAD_REQUEST.getStatusCode())))));
|
||||||
|
resource.update(representation);
|
||||||
|
|
||||||
|
oidcConfig.setAuthorizationUrl(null);
|
||||||
|
oidcConfig.setTokenUrl(null);
|
||||||
|
oidcConfig.setJwksUrl(null);
|
||||||
|
oidcConfig.setLogoutUrl("http://test");
|
||||||
|
|
||||||
|
this.expectedException.expect(
|
||||||
|
Matchers.allOf(Matchers.instanceOf(ClientErrorException.class), Matchers.hasProperty("response",
|
||||||
|
Matchers.hasProperty("status", Matchers.is(
|
||||||
|
Response.Status.BAD_REQUEST.getStatusCode())))));
|
||||||
|
resource.update(representation);
|
||||||
|
|
||||||
|
oidcConfig.setAuthorizationUrl(null);
|
||||||
|
oidcConfig.setTokenUrl(null);
|
||||||
|
oidcConfig.setJwksUrl(null);
|
||||||
|
oidcConfig.setLogoutUrl(null);
|
||||||
|
oidcConfig.setUserInfoUrl("http://test");
|
||||||
|
|
||||||
|
this.expectedException.expect(
|
||||||
|
Matchers.allOf(Matchers.instanceOf(ClientErrorException.class), Matchers.hasProperty("response",
|
||||||
|
Matchers.hasProperty("status", Matchers.is(
|
||||||
|
Response.Status.BAD_REQUEST.getStatusCode())))));
|
||||||
|
resource.update(representation);
|
||||||
|
} finally {
|
||||||
|
realmRep.setSslRequired(SslRequired.NONE.name());
|
||||||
|
realm.update(realmRep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRemove() {
|
public void testRemove() {
|
||||||
IdentityProviderRepresentation newIdentityProvider = createRep("remove-identity-provider", "saml");
|
IdentityProviderRepresentation newIdentityProvider = createRep("remove-identity-provider", "saml");
|
||||||
|
|
|
@ -1658,8 +1658,8 @@ public class PermissionsTest extends AbstractKeycloakTest {
|
||||||
}, Resource.IDENTITY_PROVIDER, false);
|
}, Resource.IDENTITY_PROVIDER, false);
|
||||||
invoke(new InvocationWithResponse() {
|
invoke(new InvocationWithResponse() {
|
||||||
public void invoke(RealmResource realm, AtomicReference<Response> response) {
|
public void invoke(RealmResource realm, AtomicReference<Response> response) {
|
||||||
response.set(realm.identityProviders().create(IdentityProviderBuilder.create().providerId("nosuch")
|
response.set(realm.identityProviders().create(IdentityProviderBuilder.create().providerId("oidc")
|
||||||
.displayName("nosuch-foo").alias("foo").build()));
|
.displayName("nosuch-foo").alias("foo").setAttribute("clientId", "foo").setAttribute("clientSecret", "foo").build()));
|
||||||
}
|
}
|
||||||
}, Resource.IDENTITY_PROVIDER, true);
|
}, Resource.IDENTITY_PROVIDER, true);
|
||||||
|
|
||||||
|
|
|
@ -708,7 +708,7 @@ public class UserTest extends AbstractAdminTest {
|
||||||
|
|
||||||
IdentityProviderRepresentation rep = new IdentityProviderRepresentation();
|
IdentityProviderRepresentation rep = new IdentityProviderRepresentation();
|
||||||
rep.setAlias("social-provider-id");
|
rep.setAlias("social-provider-id");
|
||||||
rep.setProviderId("social-provider-type");
|
rep.setProviderId("oidc");
|
||||||
|
|
||||||
realm.identityProviders().create(rep);
|
realm.identityProviders().create(rep);
|
||||||
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.identityProviderPath(rep.getAlias()), rep, ResourceType.IDENTITY_PROVIDER);
|
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.identityProviderPath(rep.getAlias()), rep, ResourceType.IDENTITY_PROVIDER);
|
||||||
|
|
|
@ -279,7 +279,7 @@ public class KcOIDCBrokerWithSignatureTest extends AbstractBaseBrokerTest {
|
||||||
|
|
||||||
// Update identityProvider to some bad JWKS_URL
|
// Update identityProvider to some bad JWKS_URL
|
||||||
OIDCIdentityProviderConfigRep cfg = new OIDCIdentityProviderConfigRep(idpRep);
|
OIDCIdentityProviderConfigRep cfg = new OIDCIdentityProviderConfigRep(idpRep);
|
||||||
cfg.setJwksUrl("http://localhost:43214/non-existent");
|
cfg.setJwksUrl("https://localhost:43214/non-existent");
|
||||||
updateIdentityProvider(idpRep);
|
updateIdentityProvider(idpRep);
|
||||||
|
|
||||||
// Check that key is not cached anymore
|
// Check that key is not cached anymore
|
||||||
|
|
|
@ -27,7 +27,7 @@ import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
class OIDCIdentityProviderConfigRep extends OIDCIdentityProviderConfig {
|
public class OIDCIdentityProviderConfigRep extends OIDCIdentityProviderConfig {
|
||||||
|
|
||||||
private final IdentityProviderRepresentation rep;
|
private final IdentityProviderRepresentation rep;
|
||||||
|
|
||||||
|
|
|
@ -43,8 +43,8 @@ public class KcAdmUpdateTest extends AbstractAdmCliTest {
|
||||||
.providerId(SAMLIdentityProviderFactory.PROVIDER_ID)
|
.providerId(SAMLIdentityProviderFactory.PROVIDER_ID)
|
||||||
.alias("idpAlias")
|
.alias("idpAlias")
|
||||||
.displayName("SAML")
|
.displayName("SAML")
|
||||||
.setAttribute(SAMLIdentityProviderConfig.SINGLE_SIGN_ON_SERVICE_URL, "http://saml.idp/saml")
|
.setAttribute(SAMLIdentityProviderConfig.SINGLE_SIGN_ON_SERVICE_URL, "https://saml.idp/saml")
|
||||||
.setAttribute(SAMLIdentityProviderConfig.SINGLE_LOGOUT_SERVICE_URL, "http://saml.idp/saml")
|
.setAttribute(SAMLIdentityProviderConfig.SINGLE_LOGOUT_SERVICE_URL, "https://saml.idp/saml")
|
||||||
.setAttribute(SAMLIdentityProviderConfig.NAME_ID_POLICY_FORMAT, "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")
|
.setAttribute(SAMLIdentityProviderConfig.NAME_ID_POLICY_FORMAT, "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")
|
||||||
.setAttribute(SAMLIdentityProviderConfig.POST_BINDING_RESPONSE, "false")
|
.setAttribute(SAMLIdentityProviderConfig.POST_BINDING_RESPONSE, "false")
|
||||||
.setAttribute(SAMLIdentityProviderConfig.POST_BINDING_AUTHN_REQUEST, "false")
|
.setAttribute(SAMLIdentityProviderConfig.POST_BINDING_AUTHN_REQUEST, "false")
|
||||||
|
|
|
@ -91,7 +91,7 @@ public class BrokerTest extends AbstractSamlTest {
|
||||||
final ResponseType res = new SAML2LoginResponseBuilder()
|
final ResponseType res = new SAML2LoginResponseBuilder()
|
||||||
.requestID(req.getID())
|
.requestID(req.getID())
|
||||||
.destination(req.getAssertionConsumerServiceURL().toString())
|
.destination(req.getAssertionConsumerServiceURL().toString())
|
||||||
.issuer("http://saml.idp/saml")
|
.issuer("https://saml.idp/saml")
|
||||||
.assertionExpiration(1000000)
|
.assertionExpiration(1000000)
|
||||||
.subjectExpiration(1000000)
|
.subjectExpiration(1000000)
|
||||||
.requestIssuer(getAuthServerRealmBase(REALM_NAME).toString())
|
.requestIssuer(getAuthServerRealmBase(REALM_NAME).toString())
|
||||||
|
@ -118,7 +118,7 @@ public class BrokerTest extends AbstractSamlTest {
|
||||||
|
|
||||||
AuthenticationExecutionInfoRepresentation reviewProfileAuthenticator = null;
|
AuthenticationExecutionInfoRepresentation reviewProfileAuthenticator = null;
|
||||||
String firstBrokerLoginFlowAlias = null;
|
String firstBrokerLoginFlowAlias = null;
|
||||||
try (IdentityProviderCreator idp = new IdentityProviderCreator(realm, addIdentityProvider("http://saml.idp/saml"))) {
|
try (IdentityProviderCreator idp = new IdentityProviderCreator(realm, addIdentityProvider("https://saml.idp/saml"))) {
|
||||||
IdentityProviderRepresentation idpRepresentation = idp.identityProvider().toRepresentation();
|
IdentityProviderRepresentation idpRepresentation = idp.identityProvider().toRepresentation();
|
||||||
firstBrokerLoginFlowAlias = idpRepresentation.getFirstBrokerLoginFlowAlias();
|
firstBrokerLoginFlowAlias = idpRepresentation.getFirstBrokerLoginFlowAlias();
|
||||||
List<AuthenticationExecutionInfoRepresentation> executions = realm.flows().getExecutions(firstBrokerLoginFlowAlias);
|
List<AuthenticationExecutionInfoRepresentation> executions = realm.flows().getExecutions(firstBrokerLoginFlowAlias);
|
||||||
|
@ -168,7 +168,7 @@ public class BrokerTest extends AbstractSamlTest {
|
||||||
public void testRedirectQueryParametersPreserved() throws IOException {
|
public void testRedirectQueryParametersPreserved() throws IOException {
|
||||||
final RealmResource realm = adminClient.realm(REALM_NAME);
|
final RealmResource realm = adminClient.realm(REALM_NAME);
|
||||||
|
|
||||||
try (IdentityProviderCreator idp = new IdentityProviderCreator(realm, addIdentityProvider("http://saml.idp/?service=name&serviceType=prod"))) {
|
try (IdentityProviderCreator idp = new IdentityProviderCreator(realm, addIdentityProvider("https://saml.idp/?service=name&serviceType=prod"))) {
|
||||||
SAMLDocumentHolder samlResponse = new SamlClientBuilder()
|
SAMLDocumentHolder samlResponse = new SamlClientBuilder()
|
||||||
.authnRequest(getAuthServerSamlEndpoint(REALM_NAME), SAML_CLIENT_ID_SALES_POST, SAML_ASSERTION_CONSUMER_URL_SALES_POST, POST).build()
|
.authnRequest(getAuthServerSamlEndpoint(REALM_NAME), SAML_CLIENT_ID_SALES_POST, SAML_ASSERTION_CONSUMER_URL_SALES_POST, POST).build()
|
||||||
.login().idp(SAML_BROKER_ALIAS).build()
|
.login().idp(SAML_BROKER_ALIAS).build()
|
||||||
|
@ -178,7 +178,7 @@ public class BrokerTest extends AbstractSamlTest {
|
||||||
|
|
||||||
assertThat(samlResponse.getSamlObject(), Matchers.instanceOf(AuthnRequestType.class));
|
assertThat(samlResponse.getSamlObject(), Matchers.instanceOf(AuthnRequestType.class));
|
||||||
AuthnRequestType ar = (AuthnRequestType) samlResponse.getSamlObject();
|
AuthnRequestType ar = (AuthnRequestType) samlResponse.getSamlObject();
|
||||||
assertThat(ar.getDestination(), Matchers.equalTo(URI.create("http://saml.idp/?service=name&serviceType=prod")));
|
assertThat(ar.getDestination(), Matchers.equalTo(URI.create("https://saml.idp/?service=name&serviceType=prod")));
|
||||||
|
|
||||||
Header[] headers = new SamlClientBuilder()
|
Header[] headers = new SamlClientBuilder()
|
||||||
.authnRequest(getAuthServerSamlEndpoint(REALM_NAME), SAML_CLIENT_ID_SALES_POST, SAML_ASSERTION_CONSUMER_URL_SALES_POST, POST).build()
|
.authnRequest(getAuthServerSamlEndpoint(REALM_NAME), SAML_CLIENT_ID_SALES_POST, SAML_ASSERTION_CONSUMER_URL_SALES_POST, POST).build()
|
||||||
|
@ -187,7 +187,7 @@ public class BrokerTest extends AbstractSamlTest {
|
||||||
.executeAndTransform(resp -> resp.getHeaders(HttpHeaders.LOCATION));
|
.executeAndTransform(resp -> resp.getHeaders(HttpHeaders.LOCATION));
|
||||||
|
|
||||||
assertThat(headers.length, Matchers.is(1));
|
assertThat(headers.length, Matchers.is(1));
|
||||||
assertThat(headers[0].getValue(), Matchers.containsString("http://saml.idp/?service=name&serviceType=prod"));
|
assertThat(headers[0].getValue(), Matchers.containsString("https://saml.idp/?service=name&serviceType=prod"));
|
||||||
assertThat(headers[0].getValue(), Matchers.containsString("SAMLRequest"));
|
assertThat(headers[0].getValue(), Matchers.containsString("SAMLRequest"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,7 +228,7 @@ public class BrokerTest extends AbstractSamlTest {
|
||||||
private void assertExpired(XMLGregorianCalendar notBefore, XMLGregorianCalendar notOnOrAfter, boolean shouldPass) throws Exception {
|
private void assertExpired(XMLGregorianCalendar notBefore, XMLGregorianCalendar notOnOrAfter, boolean shouldPass) throws Exception {
|
||||||
Status expectedStatus = shouldPass ? Status.OK : Status.BAD_REQUEST;
|
Status expectedStatus = shouldPass ? Status.OK : Status.BAD_REQUEST;
|
||||||
final RealmResource realm = adminClient.realm(REALM_NAME);
|
final RealmResource realm = adminClient.realm(REALM_NAME);
|
||||||
try (IdentityProviderCreator idp = new IdentityProviderCreator(realm, addIdentityProvider("http://saml.idp/"))) {
|
try (IdentityProviderCreator idp = new IdentityProviderCreator(realm, addIdentityProvider("https://saml.idp/"))) {
|
||||||
new SamlClientBuilder()
|
new SamlClientBuilder()
|
||||||
.authnRequest(getAuthServerSamlEndpoint(REALM_NAME), SAML_CLIENT_ID_SALES_POST, SAML_ASSERTION_CONSUMER_URL_SALES_POST, POST).build()
|
.authnRequest(getAuthServerSamlEndpoint(REALM_NAME), SAML_CLIENT_ID_SALES_POST, SAML_ASSERTION_CONSUMER_URL_SALES_POST, POST).build()
|
||||||
.login().idp(SAML_BROKER_ALIAS).build()
|
.login().idp(SAML_BROKER_ALIAS).build()
|
||||||
|
|
|
@ -210,7 +210,7 @@ public class IdpInitiatedLoginTest extends AbstractSamlTest {
|
||||||
IdentityProviderBuilder.create()
|
IdentityProviderBuilder.create()
|
||||||
.alias("saml-idp")
|
.alias("saml-idp")
|
||||||
.providerId("saml")
|
.providerId("saml")
|
||||||
.setAttribute(SAMLIdentityProviderConfig.SINGLE_SIGN_ON_SERVICE_URL, "http://saml-idp-sso-service/")
|
.setAttribute(SAMLIdentityProviderConfig.SINGLE_SIGN_ON_SERVICE_URL, "https://saml-idp-sso-service/")
|
||||||
.setAttribute(SAMLIdentityProviderConfig.POST_BINDING_AUTHN_REQUEST, "true")
|
.setAttribute(SAMLIdentityProviderConfig.POST_BINDING_AUTHN_REQUEST, "true")
|
||||||
.build())) {
|
.build())) {
|
||||||
new SamlClientBuilder()
|
new SamlClientBuilder()
|
||||||
|
|
|
@ -75,9 +75,9 @@ public class LogoutTest extends AbstractSamlTest {
|
||||||
private static final String SP_NAME_QUALIFIER = "spNameQualifier";
|
private static final String SP_NAME_QUALIFIER = "spNameQualifier";
|
||||||
private static final String NAME_QUALIFIER = "nameQualifier";
|
private static final String NAME_QUALIFIER = "nameQualifier";
|
||||||
|
|
||||||
private static final String BROKER_SIGN_ON_SERVICE_URL = "http://saml.idp/saml";
|
private static final String BROKER_SIGN_ON_SERVICE_URL = "https://saml.idp/saml";
|
||||||
private static final String BROKER_LOGOUT_SERVICE_URL = "http://saml.idp/SLO/saml";
|
private static final String BROKER_LOGOUT_SERVICE_URL = "https://saml.idp/SLO/saml";
|
||||||
private static final String BROKER_SERVICE_ID = "http://saml.idp/saml";
|
private static final String BROKER_SERVICE_ID = "https://saml.idp/saml";
|
||||||
|
|
||||||
private ClientRepresentation salesRep;
|
private ClientRepresentation salesRep;
|
||||||
private ClientRepresentation sales2Rep;
|
private ClientRepresentation sales2Rep;
|
||||||
|
|
|
@ -13,9 +13,9 @@
|
||||||
"config" : {
|
"config" : {
|
||||||
"nameIDPolicyFormat" : "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
|
"nameIDPolicyFormat" : "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
|
||||||
"postBindingResponse" : "false",
|
"postBindingResponse" : "false",
|
||||||
"singleLogoutServiceUrl" : "http://saml.idp/saml",
|
"singleLogoutServiceUrl" : "https://saml.idp/saml",
|
||||||
"postBindingAuthnRequest" : "false",
|
"postBindingAuthnRequest" : "false",
|
||||||
"singleSignOnServiceUrl" : "http://saml.idp/saml",
|
"singleSignOnServiceUrl" : "https://saml.idp/saml",
|
||||||
"backchannelSupported" : "false"
|
"backchannelSupported" : "false"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue