KEYCLOAK-4501, KEYCLOAK-4511, KEYCLOAK-4513

This commit is contained in:
Bill Burke 2017-03-03 09:48:52 -05:00
parent 0765b01189
commit 3bb29e033b
30 changed files with 643 additions and 113 deletions

View file

@ -53,6 +53,7 @@ public class IdentityProviderRepresentation {
protected boolean storeToken; protected boolean storeToken;
protected boolean addReadTokenRoleOnCreate; protected boolean addReadTokenRoleOnCreate;
protected boolean authenticateByDefault; protected boolean authenticateByDefault;
protected boolean linkOnly;
protected String firstBrokerLoginFlowAlias; protected String firstBrokerLoginFlowAlias;
protected String postBrokerLoginFlowAlias; protected String postBrokerLoginFlowAlias;
protected Map<String, String> config = new HashMap<String, String>(); protected Map<String, String> config = new HashMap<String, String>();
@ -97,6 +98,14 @@ public class IdentityProviderRepresentation {
this.enabled = enabled; this.enabled = enabled;
} }
public boolean isLinkOnly() {
return linkOnly;
}
public void setLinkOnly(boolean linkOnly) {
this.linkOnly = linkOnly;
}
/** /**
* *
* Deprecated because replaced by {@link #updateProfileFirstLoginMode}. Kept here to allow import of old realms. * Deprecated because replaced by {@link #updateProfileFirstLoginMode}. Kept here to allow import of old realms.

View file

@ -1008,6 +1008,7 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
copy.putAll(config); copy.putAll(config);
identityProviderModel.setConfig(copy); identityProviderModel.setConfig(copy);
identityProviderModel.setEnabled(entity.isEnabled()); identityProviderModel.setEnabled(entity.isEnabled());
identityProviderModel.setLinkOnly(entity.isLinkOnly());
identityProviderModel.setTrustEmail(entity.isTrustEmail()); identityProviderModel.setTrustEmail(entity.isTrustEmail());
identityProviderModel.setAuthenticateByDefault(entity.isAuthenticateByDefault()); identityProviderModel.setAuthenticateByDefault(entity.isAuthenticateByDefault());
identityProviderModel.setFirstBrokerLoginFlowId(entity.getFirstBrokerLoginFlowId()); identityProviderModel.setFirstBrokerLoginFlowId(entity.getFirstBrokerLoginFlowId());
@ -1044,6 +1045,7 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
entity.setFirstBrokerLoginFlowId(identityProvider.getFirstBrokerLoginFlowId()); entity.setFirstBrokerLoginFlowId(identityProvider.getFirstBrokerLoginFlowId());
entity.setPostBrokerLoginFlowId(identityProvider.getPostBrokerLoginFlowId()); entity.setPostBrokerLoginFlowId(identityProvider.getPostBrokerLoginFlowId());
entity.setConfig(identityProvider.getConfig()); entity.setConfig(identityProvider.getConfig());
entity.setLinkOnly(identityProvider.isLinkOnly());
realm.addIdentityProvider(entity); realm.addIdentityProvider(entity);
@ -1098,6 +1100,7 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
entity.setAddReadTokenRoleOnCreate(identityProvider.isAddReadTokenRoleOnCreate()); entity.setAddReadTokenRoleOnCreate(identityProvider.isAddReadTokenRoleOnCreate());
entity.setStoreToken(identityProvider.isStoreToken()); entity.setStoreToken(identityProvider.isStoreToken());
entity.setConfig(identityProvider.getConfig()); entity.setConfig(identityProvider.getConfig());
entity.setLinkOnly(identityProvider.isLinkOnly());
} }
} }

View file

@ -70,6 +70,9 @@ public class IdentityProviderEntity {
@Column(name="STORE_TOKEN") @Column(name="STORE_TOKEN")
private boolean storeToken; private boolean storeToken;
@Column(name="LINK_ONLY")
private boolean linkOnly;
@Column(name="ADD_TOKEN_ROLE") @Column(name="ADD_TOKEN_ROLE")
protected boolean addReadTokenRoleOnCreate; protected boolean addReadTokenRoleOnCreate;
@ -144,6 +147,14 @@ public class IdentityProviderEntity {
this.authenticateByDefault = authenticateByDefault; this.authenticateByDefault = authenticateByDefault;
} }
public boolean isLinkOnly() {
return linkOnly;
}
public void setLinkOnly(boolean linkOnly) {
this.linkOnly = linkOnly;
}
public String getFirstBrokerLoginFlowId() { public String getFirstBrokerLoginFlowId() {
return firstBrokerLoginFlowId; return firstBrokerLoginFlowId;
} }

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
~ Copyright 2016 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.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="bburke@redhat.com" id="3.0.0">
<addColumn tableName="IDENTITY_PROVIDER">
<column name="LINK_ONLY" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

View file

@ -46,4 +46,5 @@
<include file="META-INF/jpa-changelog-2.4.0.xml"/> <include file="META-INF/jpa-changelog-2.4.0.xml"/>
<include file="META-INF/jpa-changelog-2.5.0.xml"/> <include file="META-INF/jpa-changelog-2.5.0.xml"/>
<include file="META-INF/jpa-changelog-2.5.1.xml"/> <include file="META-INF/jpa-changelog-2.5.1.xml"/>
<include file="META-INF/jpa-changelog-3.0.0.xml"/>
</databaseChangeLog> </databaseChangeLog>

View file

@ -32,6 +32,7 @@ import org.keycloak.migration.migrators.MigrateTo2_1_0;
import org.keycloak.migration.migrators.MigrateTo2_2_0; import org.keycloak.migration.migrators.MigrateTo2_2_0;
import org.keycloak.migration.migrators.MigrateTo2_3_0; import org.keycloak.migration.migrators.MigrateTo2_3_0;
import org.keycloak.migration.migrators.MigrateTo2_5_0; import org.keycloak.migration.migrators.MigrateTo2_5_0;
import org.keycloak.migration.migrators.MigrateTo3_0_0;
import org.keycloak.migration.migrators.Migration; import org.keycloak.migration.migrators.Migration;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
@ -56,7 +57,8 @@ public class MigrationModelManager {
new MigrateTo2_1_0(), new MigrateTo2_1_0(),
new MigrateTo2_2_0(), new MigrateTo2_2_0(),
new MigrateTo2_3_0(), new MigrateTo2_3_0(),
new MigrateTo2_5_0() new MigrateTo2_5_0(),
new MigrateTo3_0_0()
}; };
public static void migrate(KeycloakSession session) { public static void migrate(KeycloakSession session) {

View file

@ -0,0 +1,66 @@
/*
* Copyright 2016 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.migration.migrators;
import org.keycloak.migration.ModelVersion;
import org.keycloak.models.AccountRoles;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.utils.DefaultKeyProviders;
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT;
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT_LINKS;
import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_CLIENT_ID;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class MigrateTo3_0_0 implements Migration {
public static final ModelVersion VERSION = new ModelVersion("2.5.0");
@Override
public void migrate(KeycloakSession session) {
session.realms().getRealms().stream().forEach(
r -> DefaultKeyProviders.createSecretProvider(r)
);
for (RealmModel realm : session.realms().getRealms()) {
ClientModel client = realm.getClientByClientId(ACCOUNT_MANAGEMENT_CLIENT_ID);
if (client == null) continue;
RoleModel linkRole = client.getRole(MANAGE_ACCOUNT_LINKS);
if (linkRole == null) {
client.addRole(MANAGE_ACCOUNT_LINKS);
}
RoleModel manageAccount = client.getRole(AccountRoles.MANAGE_ACCOUNT);
if (manageAccount == null) continue;
RoleModel manageAccountLinks = client.getRole(AccountRoles.MANAGE_ACCOUNT_LINKS);
manageAccount.addCompositeRole(manageAccountLinks);
}
}
@Override
public ModelVersion getVersion() {
return VERSION;
}
}

View file

@ -24,6 +24,7 @@ public interface AccountRoles {
String VIEW_PROFILE = "view-profile"; String VIEW_PROFILE = "view-profile";
String MANAGE_ACCOUNT = "manage-account"; String MANAGE_ACCOUNT = "manage-account";
String MANAGE_ACCOUNT_LINKS = "manage-account-links";
String[] ALL = {VIEW_PROFILE, MANAGE_ACCOUNT}; String[] ALL = {VIEW_PROFILE, MANAGE_ACCOUNT};

View file

@ -575,6 +575,7 @@ public class ModelToRepresentation {
providerRep.setAlias(identityProviderModel.getAlias()); providerRep.setAlias(identityProviderModel.getAlias());
providerRep.setDisplayName(identityProviderModel.getDisplayName()); providerRep.setDisplayName(identityProviderModel.getDisplayName());
providerRep.setEnabled(identityProviderModel.isEnabled()); providerRep.setEnabled(identityProviderModel.isEnabled());
providerRep.setLinkOnly(identityProviderModel.isLinkOnly());
providerRep.setStoreToken(identityProviderModel.isStoreToken()); providerRep.setStoreToken(identityProviderModel.isStoreToken());
providerRep.setTrustEmail(identityProviderModel.isTrustEmail()); providerRep.setTrustEmail(identityProviderModel.isTrustEmail());
providerRep.setAuthenticateByDefault(identityProviderModel.isAuthenticateByDefault()); providerRep.setAuthenticateByDefault(identityProviderModel.isAuthenticateByDefault());

View file

@ -118,6 +118,7 @@ import java.util.stream.Collectors;
public class RepresentationToModel { public class RepresentationToModel {
private static Logger logger = Logger.getLogger(RepresentationToModel.class); private static Logger logger = Logger.getLogger(RepresentationToModel.class);
public static OTPPolicy toPolicy(RealmRepresentation rep) { public static OTPPolicy toPolicy(RealmRepresentation rep) {
OTPPolicy policy = new OTPPolicy(); OTPPolicy policy = new OTPPolicy();
if (rep.getOtpPolicyType() != null) policy.setType(rep.getOtpPolicyType()); if (rep.getOtpPolicyType() != null) policy.setType(rep.getOtpPolicyType());
@ -129,6 +130,7 @@ public class RepresentationToModel {
return policy; return policy;
} }
public static void importRealm(KeycloakSession session, RealmRepresentation rep, RealmModel newRealm, boolean skipUserDependent) { public static void importRealm(KeycloakSession session, RealmRepresentation rep, RealmModel newRealm, boolean skipUserDependent) {
convertDeprecatedSocialProviders(rep); convertDeprecatedSocialProviders(rep);
convertDeprecatedApplications(session, rep); convertDeprecatedApplications(session, rep);
@ -139,16 +141,19 @@ public class RepresentationToModel {
if (rep.isEnabled() != null) newRealm.setEnabled(rep.isEnabled()); if (rep.isEnabled() != null) newRealm.setEnabled(rep.isEnabled());
if (rep.isBruteForceProtected() != null) newRealm.setBruteForceProtected(rep.isBruteForceProtected()); if (rep.isBruteForceProtected() != null) newRealm.setBruteForceProtected(rep.isBruteForceProtected());
if (rep.getMaxFailureWaitSeconds() != null) newRealm.setMaxFailureWaitSeconds(rep.getMaxFailureWaitSeconds()); if (rep.getMaxFailureWaitSeconds() != null) newRealm.setMaxFailureWaitSeconds(rep.getMaxFailureWaitSeconds());
if (rep.getMinimumQuickLoginWaitSeconds() != null) newRealm.setMinimumQuickLoginWaitSeconds(rep.getMinimumQuickLoginWaitSeconds()); if (rep.getMinimumQuickLoginWaitSeconds() != null)
newRealm.setMinimumQuickLoginWaitSeconds(rep.getMinimumQuickLoginWaitSeconds());
if (rep.getWaitIncrementSeconds() != null) newRealm.setWaitIncrementSeconds(rep.getWaitIncrementSeconds()); if (rep.getWaitIncrementSeconds() != null) newRealm.setWaitIncrementSeconds(rep.getWaitIncrementSeconds());
if (rep.getQuickLoginCheckMilliSeconds() != null) newRealm.setQuickLoginCheckMilliSeconds(rep.getQuickLoginCheckMilliSeconds()); if (rep.getQuickLoginCheckMilliSeconds() != null)
newRealm.setQuickLoginCheckMilliSeconds(rep.getQuickLoginCheckMilliSeconds());
if (rep.getMaxDeltaTimeSeconds() != null) newRealm.setMaxDeltaTimeSeconds(rep.getMaxDeltaTimeSeconds()); if (rep.getMaxDeltaTimeSeconds() != null) newRealm.setMaxDeltaTimeSeconds(rep.getMaxDeltaTimeSeconds());
if (rep.getFailureFactor() != null) newRealm.setFailureFactor(rep.getFailureFactor()); if (rep.getFailureFactor() != null) newRealm.setFailureFactor(rep.getFailureFactor());
if (rep.isEventsEnabled() != null) newRealm.setEventsEnabled(rep.isEventsEnabled()); if (rep.isEventsEnabled() != null) newRealm.setEventsEnabled(rep.isEventsEnabled());
if (rep.getEventsExpiration() != null) newRealm.setEventsExpiration(rep.getEventsExpiration()); if (rep.getEventsExpiration() != null) newRealm.setEventsExpiration(rep.getEventsExpiration());
if (rep.getEventsListeners() != null) newRealm.setEventsListeners(new HashSet<>(rep.getEventsListeners())); if (rep.getEventsListeners() != null) newRealm.setEventsListeners(new HashSet<>(rep.getEventsListeners()));
if (rep.isAdminEventsEnabled() != null) newRealm.setAdminEventsEnabled(rep.isAdminEventsEnabled()); if (rep.isAdminEventsEnabled() != null) newRealm.setAdminEventsEnabled(rep.isAdminEventsEnabled());
if (rep.isAdminEventsDetailsEnabled() != null) newRealm.setAdminEventsDetailsEnabled(rep.isAdminEventsDetailsEnabled()); if (rep.isAdminEventsDetailsEnabled() != null)
newRealm.setAdminEventsDetailsEnabled(rep.isAdminEventsDetailsEnabled());
if (rep.getNotBefore() != null) newRealm.setNotBefore(rep.getNotBefore()); if (rep.getNotBefore() != null) newRealm.setNotBefore(rep.getNotBefore());
@ -158,14 +163,17 @@ public class RepresentationToModel {
if (rep.getAccessTokenLifespan() != null) newRealm.setAccessTokenLifespan(rep.getAccessTokenLifespan()); if (rep.getAccessTokenLifespan() != null) newRealm.setAccessTokenLifespan(rep.getAccessTokenLifespan());
else newRealm.setAccessTokenLifespan(300); else newRealm.setAccessTokenLifespan(300);
if (rep.getAccessTokenLifespanForImplicitFlow() != null) newRealm.setAccessTokenLifespanForImplicitFlow(rep.getAccessTokenLifespanForImplicitFlow()); if (rep.getAccessTokenLifespanForImplicitFlow() != null)
else newRealm.setAccessTokenLifespanForImplicitFlow(Constants.DEFAULT_ACCESS_TOKEN_LIFESPAN_FOR_IMPLICIT_FLOW_TIMEOUT); newRealm.setAccessTokenLifespanForImplicitFlow(rep.getAccessTokenLifespanForImplicitFlow());
else
newRealm.setAccessTokenLifespanForImplicitFlow(Constants.DEFAULT_ACCESS_TOKEN_LIFESPAN_FOR_IMPLICIT_FLOW_TIMEOUT);
if (rep.getSsoSessionIdleTimeout() != null) newRealm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout()); if (rep.getSsoSessionIdleTimeout() != null) newRealm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout());
else newRealm.setSsoSessionIdleTimeout(1800); else newRealm.setSsoSessionIdleTimeout(1800);
if (rep.getSsoSessionMaxLifespan() != null) newRealm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan()); if (rep.getSsoSessionMaxLifespan() != null) newRealm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan());
else newRealm.setSsoSessionMaxLifespan(36000); else newRealm.setSsoSessionMaxLifespan(36000);
if (rep.getOfflineSessionIdleTimeout() != null) newRealm.setOfflineSessionIdleTimeout(rep.getOfflineSessionIdleTimeout()); if (rep.getOfflineSessionIdleTimeout() != null)
newRealm.setOfflineSessionIdleTimeout(rep.getOfflineSessionIdleTimeout());
else newRealm.setOfflineSessionIdleTimeout(Constants.DEFAULT_OFFLINE_SESSION_IDLE_TIMEOUT); else newRealm.setOfflineSessionIdleTimeout(Constants.DEFAULT_OFFLINE_SESSION_IDLE_TIMEOUT);
if (rep.getAccessCodeLifespan() != null) newRealm.setAccessCodeLifespan(rep.getAccessCodeLifespan()); if (rep.getAccessCodeLifespan() != null) newRealm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
@ -179,7 +187,8 @@ public class RepresentationToModel {
newRealm.setAccessCodeLifespanLogin(rep.getAccessCodeLifespanLogin()); newRealm.setAccessCodeLifespanLogin(rep.getAccessCodeLifespanLogin());
else newRealm.setAccessCodeLifespanLogin(1800); else newRealm.setAccessCodeLifespanLogin(1800);
if (rep.getSslRequired() != null) newRealm.setSslRequired(SslRequired.valueOf(rep.getSslRequired().toUpperCase())); if (rep.getSslRequired() != null)
newRealm.setSslRequired(SslRequired.valueOf(rep.getSslRequired().toUpperCase()));
if (rep.isRegistrationAllowed() != null) newRealm.setRegistrationAllowed(rep.isRegistrationAllowed()); if (rep.isRegistrationAllowed() != null) newRealm.setRegistrationAllowed(rep.isRegistrationAllowed());
if (rep.isRegistrationEmailAsUsername() != null) if (rep.isRegistrationEmailAsUsername() != null)
newRealm.setRegistrationEmailAsUsername(rep.isRegistrationEmailAsUsername()); newRealm.setRegistrationEmailAsUsername(rep.isRegistrationEmailAsUsername());
@ -203,7 +212,8 @@ public class RepresentationToModel {
newRealm.addRequiredCredential(CredentialRepresentation.PASSWORD); newRealm.addRequiredCredential(CredentialRepresentation.PASSWORD);
} }
if (rep.getPasswordPolicy() != null) newRealm.setPasswordPolicy(PasswordPolicy.parse(session, rep.getPasswordPolicy())); if (rep.getPasswordPolicy() != null)
newRealm.setPasswordPolicy(PasswordPolicy.parse(session, rep.getPasswordPolicy()));
if (rep.getOtpPolicyType() != null) newRealm.setOTPPolicy(toPolicy(rep)); if (rep.getOtpPolicyType() != null) newRealm.setOTPPolicy(toPolicy(rep));
else newRealm.setOTPPolicy(OTPPolicy.DEFAULT_POLICY); else newRealm.setOTPPolicy(OTPPolicy.DEFAULT_POLICY);
@ -328,16 +338,16 @@ public class RepresentationToModel {
importRealmAuthorizationSettings(rep, newRealm, session); importRealmAuthorizationSettings(rep, newRealm, session);
} }
if(rep.isInternationalizationEnabled() != null){ if (rep.isInternationalizationEnabled() != null) {
newRealm.setInternationalizationEnabled(rep.isInternationalizationEnabled()); newRealm.setInternationalizationEnabled(rep.isInternationalizationEnabled());
} }
if(rep.getSupportedLocales() != null){ if (rep.getSupportedLocales() != null) {
newRealm.setSupportedLocales(new HashSet<String>(rep.getSupportedLocales())); newRealm.setSupportedLocales(new HashSet<String>(rep.getSupportedLocales()));
} }
if(rep.getDefaultLocale() != null){ if (rep.getDefaultLocale() != null) {
newRealm.setDefaultLocale(rep.getDefaultLocale()); newRealm.setDefaultLocale(rep.getDefaultLocale());
} }
// import attributes // import attributes
if (rep.getAttributes() != null) { if (rep.getAttributes() != null) {
@ -434,9 +444,9 @@ public class RepresentationToModel {
} }
for (RoleRepresentation roleRep : entry.getValue()) { for (RoleRepresentation roleRep : entry.getValue()) {
// Application role may already exists (for example if it is defaultRole) // Application role may already exists (for example if it is defaultRole)
RoleModel role = roleRep.getId()!=null ? client.addRole(roleRep.getId(), roleRep.getName()) : client.addRole(roleRep.getName()); RoleModel role = roleRep.getId() != null ? client.addRole(roleRep.getId(), roleRep.getName()) : client.addRole(roleRep.getName());
role.setDescription(roleRep.getDescription()); role.setDescription(roleRep.getDescription());
boolean scopeParamRequired = roleRep.isScopeParamRequired()==null ? false : roleRep.isScopeParamRequired(); boolean scopeParamRequired = roleRep.isScopeParamRequired() == null ? false : roleRep.isScopeParamRequired();
role.setScopeParamRequired(scopeParamRequired); role.setScopeParamRequired(scopeParamRequired);
} }
} }
@ -612,6 +622,7 @@ public class RepresentationToModel {
identityProvider.setAlias(providerId); identityProvider.setAlias(providerId);
identityProvider.setProviderId(providerId); identityProvider.setProviderId(providerId);
identityProvider.setEnabled(true); identityProvider.setEnabled(true);
identityProvider.setLinkOnly(false);
identityProvider.setUpdateProfileFirstLogin(updateProfileFirstLogin); identityProvider.setUpdateProfileFirstLogin(updateProfileFirstLogin);
Map<String, String> config = new HashMap<>(); Map<String, String> config = new HashMap<>();
@ -776,13 +787,16 @@ public class RepresentationToModel {
if (rep.isEnabled() != null) realm.setEnabled(rep.isEnabled()); if (rep.isEnabled() != null) realm.setEnabled(rep.isEnabled());
if (rep.isBruteForceProtected() != null) realm.setBruteForceProtected(rep.isBruteForceProtected()); if (rep.isBruteForceProtected() != null) realm.setBruteForceProtected(rep.isBruteForceProtected());
if (rep.getMaxFailureWaitSeconds() != null) realm.setMaxFailureWaitSeconds(rep.getMaxFailureWaitSeconds()); if (rep.getMaxFailureWaitSeconds() != null) realm.setMaxFailureWaitSeconds(rep.getMaxFailureWaitSeconds());
if (rep.getMinimumQuickLoginWaitSeconds() != null) realm.setMinimumQuickLoginWaitSeconds(rep.getMinimumQuickLoginWaitSeconds()); if (rep.getMinimumQuickLoginWaitSeconds() != null)
realm.setMinimumQuickLoginWaitSeconds(rep.getMinimumQuickLoginWaitSeconds());
if (rep.getWaitIncrementSeconds() != null) realm.setWaitIncrementSeconds(rep.getWaitIncrementSeconds()); if (rep.getWaitIncrementSeconds() != null) realm.setWaitIncrementSeconds(rep.getWaitIncrementSeconds());
if (rep.getQuickLoginCheckMilliSeconds() != null) realm.setQuickLoginCheckMilliSeconds(rep.getQuickLoginCheckMilliSeconds()); if (rep.getQuickLoginCheckMilliSeconds() != null)
realm.setQuickLoginCheckMilliSeconds(rep.getQuickLoginCheckMilliSeconds());
if (rep.getMaxDeltaTimeSeconds() != null) realm.setMaxDeltaTimeSeconds(rep.getMaxDeltaTimeSeconds()); if (rep.getMaxDeltaTimeSeconds() != null) realm.setMaxDeltaTimeSeconds(rep.getMaxDeltaTimeSeconds());
if (rep.getFailureFactor() != null) realm.setFailureFactor(rep.getFailureFactor()); if (rep.getFailureFactor() != null) realm.setFailureFactor(rep.getFailureFactor());
if (rep.isRegistrationAllowed() != null) realm.setRegistrationAllowed(rep.isRegistrationAllowed()); if (rep.isRegistrationAllowed() != null) realm.setRegistrationAllowed(rep.isRegistrationAllowed());
if (rep.isRegistrationEmailAsUsername() != null) realm.setRegistrationEmailAsUsername(rep.isRegistrationEmailAsUsername()); if (rep.isRegistrationEmailAsUsername() != null)
realm.setRegistrationEmailAsUsername(rep.isRegistrationEmailAsUsername());
if (rep.isRememberMe() != null) realm.setRememberMe(rep.isRememberMe()); if (rep.isRememberMe() != null) realm.setRememberMe(rep.isRememberMe());
if (rep.isVerifyEmail() != null) realm.setVerifyEmail(rep.isVerifyEmail()); if (rep.isVerifyEmail() != null) realm.setVerifyEmail(rep.isVerifyEmail());
if (rep.isLoginWithEmailAllowed() != null) realm.setLoginWithEmailAllowed(rep.isLoginWithEmailAllowed()); if (rep.isLoginWithEmailAllowed() != null) realm.setLoginWithEmailAllowed(rep.isLoginWithEmailAllowed());
@ -791,15 +805,19 @@ public class RepresentationToModel {
if (rep.isEditUsernameAllowed() != null) realm.setEditUsernameAllowed(rep.isEditUsernameAllowed()); if (rep.isEditUsernameAllowed() != null) realm.setEditUsernameAllowed(rep.isEditUsernameAllowed());
if (rep.getSslRequired() != null) realm.setSslRequired(SslRequired.valueOf(rep.getSslRequired().toUpperCase())); if (rep.getSslRequired() != null) realm.setSslRequired(SslRequired.valueOf(rep.getSslRequired().toUpperCase()));
if (rep.getAccessCodeLifespan() != null) realm.setAccessCodeLifespan(rep.getAccessCodeLifespan()); if (rep.getAccessCodeLifespan() != null) realm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
if (rep.getAccessCodeLifespanUserAction() != null) realm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction()); if (rep.getAccessCodeLifespanUserAction() != null)
if (rep.getAccessCodeLifespanLogin() != null) realm.setAccessCodeLifespanLogin(rep.getAccessCodeLifespanLogin()); realm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction());
if (rep.getAccessCodeLifespanLogin() != null)
realm.setAccessCodeLifespanLogin(rep.getAccessCodeLifespanLogin());
if (rep.getNotBefore() != null) realm.setNotBefore(rep.getNotBefore()); if (rep.getNotBefore() != null) realm.setNotBefore(rep.getNotBefore());
if (rep.getRevokeRefreshToken() != null) realm.setRevokeRefreshToken(rep.getRevokeRefreshToken()); if (rep.getRevokeRefreshToken() != null) realm.setRevokeRefreshToken(rep.getRevokeRefreshToken());
if (rep.getAccessTokenLifespan() != null) realm.setAccessTokenLifespan(rep.getAccessTokenLifespan()); if (rep.getAccessTokenLifespan() != null) realm.setAccessTokenLifespan(rep.getAccessTokenLifespan());
if (rep.getAccessTokenLifespanForImplicitFlow() != null) realm.setAccessTokenLifespanForImplicitFlow(rep.getAccessTokenLifespanForImplicitFlow()); if (rep.getAccessTokenLifespanForImplicitFlow() != null)
realm.setAccessTokenLifespanForImplicitFlow(rep.getAccessTokenLifespanForImplicitFlow());
if (rep.getSsoSessionIdleTimeout() != null) realm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout()); if (rep.getSsoSessionIdleTimeout() != null) realm.setSsoSessionIdleTimeout(rep.getSsoSessionIdleTimeout());
if (rep.getSsoSessionMaxLifespan() != null) realm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan()); if (rep.getSsoSessionMaxLifespan() != null) realm.setSsoSessionMaxLifespan(rep.getSsoSessionMaxLifespan());
if (rep.getOfflineSessionIdleTimeout() != null) realm.setOfflineSessionIdleTimeout(rep.getOfflineSessionIdleTimeout()); if (rep.getOfflineSessionIdleTimeout() != null)
realm.setOfflineSessionIdleTimeout(rep.getOfflineSessionIdleTimeout());
if (rep.getRequiredCredentials() != null) { if (rep.getRequiredCredentials() != null) {
realm.updateRequiredCredentials(rep.getRequiredCredentials()); realm.updateRequiredCredentials(rep.getRequiredCredentials());
} }
@ -814,10 +832,12 @@ public class RepresentationToModel {
if (rep.getEnabledEventTypes() != null) realm.setEnabledEventTypes(new HashSet<>(rep.getEnabledEventTypes())); if (rep.getEnabledEventTypes() != null) realm.setEnabledEventTypes(new HashSet<>(rep.getEnabledEventTypes()));
if (rep.isAdminEventsEnabled() != null) realm.setAdminEventsEnabled(rep.isAdminEventsEnabled()); if (rep.isAdminEventsEnabled() != null) realm.setAdminEventsEnabled(rep.isAdminEventsEnabled());
if (rep.isAdminEventsDetailsEnabled() != null) realm.setAdminEventsDetailsEnabled(rep.isAdminEventsDetailsEnabled()); if (rep.isAdminEventsDetailsEnabled() != null)
realm.setAdminEventsDetailsEnabled(rep.isAdminEventsDetailsEnabled());
if (rep.getPasswordPolicy() != null) realm.setPasswordPolicy(PasswordPolicy.parse(session, rep.getPasswordPolicy())); if (rep.getPasswordPolicy() != null)
realm.setPasswordPolicy(PasswordPolicy.parse(session, rep.getPasswordPolicy()));
if (rep.getOtpPolicyType() != null) realm.setOTPPolicy(toPolicy(rep)); if (rep.getOtpPolicyType() != null) realm.setOTPPolicy(toPolicy(rep));
if (rep.getDefaultRoles() != null) { if (rep.getDefaultRoles() != null) {
@ -837,13 +857,13 @@ public class RepresentationToModel {
realm.setBrowserSecurityHeaders(rep.getBrowserSecurityHeaders()); realm.setBrowserSecurityHeaders(rep.getBrowserSecurityHeaders());
} }
if(rep.isInternationalizationEnabled() != null){ if (rep.isInternationalizationEnabled() != null) {
realm.setInternationalizationEnabled(rep.isInternationalizationEnabled()); realm.setInternationalizationEnabled(rep.isInternationalizationEnabled());
} }
if(rep.getSupportedLocales() != null){ if (rep.getSupportedLocales() != null) {
realm.setSupportedLocales(new HashSet<String>(rep.getSupportedLocales())); realm.setSupportedLocales(new HashSet<String>(rep.getSupportedLocales()));
} }
if(rep.getDefaultLocale() != null){ if (rep.getDefaultLocale() != null) {
realm.setDefaultLocale(rep.getDefaultLocale()); realm.setDefaultLocale(rep.getDefaultLocale());
} }
if (rep.getBrowserFlow() != null) { if (rep.getBrowserFlow() != null) {
@ -904,7 +924,7 @@ public class RepresentationToModel {
// Roles // Roles
public static void createRole(RealmModel newRealm, RoleRepresentation roleRep) { public static void createRole(RealmModel newRealm, RoleRepresentation roleRep) {
RoleModel role = roleRep.getId()!=null ? newRealm.addRole(roleRep.getId(), roleRep.getName()) : newRealm.addRole(roleRep.getName()); RoleModel role = roleRep.getId() != null ? newRealm.addRole(roleRep.getId(), roleRep.getName()) : newRealm.addRole(roleRep.getName());
if (roleRep.getDescription() != null) role.setDescription(roleRep.getDescription()); if (roleRep.getDescription() != null) role.setDescription(roleRep.getDescription());
boolean scopeParamRequired = roleRep.isScopeParamRequired() == null ? false : roleRep.isScopeParamRequired(); boolean scopeParamRequired = roleRep.isScopeParamRequired() == null ? false : roleRep.isScopeParamRequired();
role.setScopeParamRequired(scopeParamRequired); role.setScopeParamRequired(scopeParamRequired);
@ -927,7 +947,8 @@ public class RepresentationToModel {
} }
for (String roleStr : entry.getValue()) { for (String roleStr : entry.getValue()) {
RoleModel clientRole = client.getRole(roleStr); RoleModel clientRole = client.getRole(roleStr);
if (clientRole == null) throw new RuntimeException("Unable to find composite client role: " + roleStr); if (clientRole == null)
throw new RuntimeException("Unable to find composite client role: " + roleStr);
role.addCompositeRole(clientRole); role.addCompositeRole(clientRole);
} }
} }
@ -957,9 +978,9 @@ public class RepresentationToModel {
public static ClientModel createClient(KeycloakSession session, RealmModel realm, ClientRepresentation resourceRep, boolean addDefaultRoles) { public static ClientModel createClient(KeycloakSession session, RealmModel realm, ClientRepresentation resourceRep, boolean addDefaultRoles) {
logger.debug("Create client: {0}" + resourceRep.getClientId()); logger.debug("Create client: {0}" + resourceRep.getClientId());
ClientModel client = resourceRep.getId()!=null ? realm.addClient(resourceRep.getId(), resourceRep.getClientId()) : realm.addClient(resourceRep.getClientId()); ClientModel client = resourceRep.getId() != null ? realm.addClient(resourceRep.getId(), resourceRep.getClientId()) : realm.addClient(resourceRep.getClientId());
if (resourceRep.getName() != null) client.setName(resourceRep.getName()); if (resourceRep.getName() != null) client.setName(resourceRep.getName());
if(resourceRep.getDescription() != null) client.setDescription(resourceRep.getDescription()); if (resourceRep.getDescription() != null) client.setDescription(resourceRep.getDescription());
if (resourceRep.isEnabled() != null) client.setEnabled(resourceRep.isEnabled()); if (resourceRep.isEnabled() != null) client.setEnabled(resourceRep.isEnabled());
client.setManagementUrl(resourceRep.getAdminUrl()); client.setManagementUrl(resourceRep.getAdminUrl());
if (resourceRep.isSurrogateAuthRequired() != null) if (resourceRep.isSurrogateAuthRequired() != null)
@ -976,13 +997,18 @@ public class RepresentationToModel {
client.setDirectAccessGrantsEnabled(resourceRep.isDirectGrantsOnly()); client.setDirectAccessGrantsEnabled(resourceRep.isDirectGrantsOnly());
} }
if (resourceRep.isStandardFlowEnabled() != null) client.setStandardFlowEnabled(resourceRep.isStandardFlowEnabled()); if (resourceRep.isStandardFlowEnabled() != null)
if (resourceRep.isImplicitFlowEnabled() != null) client.setImplicitFlowEnabled(resourceRep.isImplicitFlowEnabled()); client.setStandardFlowEnabled(resourceRep.isStandardFlowEnabled());
if (resourceRep.isDirectAccessGrantsEnabled() != null) client.setDirectAccessGrantsEnabled(resourceRep.isDirectAccessGrantsEnabled()); if (resourceRep.isImplicitFlowEnabled() != null)
if (resourceRep.isServiceAccountsEnabled() != null) client.setServiceAccountsEnabled(resourceRep.isServiceAccountsEnabled()); client.setImplicitFlowEnabled(resourceRep.isImplicitFlowEnabled());
if (resourceRep.isDirectAccessGrantsEnabled() != null)
client.setDirectAccessGrantsEnabled(resourceRep.isDirectAccessGrantsEnabled());
if (resourceRep.isServiceAccountsEnabled() != null)
client.setServiceAccountsEnabled(resourceRep.isServiceAccountsEnabled());
if (resourceRep.isPublicClient() != null) client.setPublicClient(resourceRep.isPublicClient()); if (resourceRep.isPublicClient() != null) client.setPublicClient(resourceRep.isPublicClient());
if (resourceRep.isFrontchannelLogout() != null) client.setFrontchannelLogout(resourceRep.isFrontchannelLogout()); if (resourceRep.isFrontchannelLogout() != null)
client.setFrontchannelLogout(resourceRep.isFrontchannelLogout());
if (resourceRep.getProtocol() != null) client.setProtocol(resourceRep.getProtocol()); if (resourceRep.getProtocol() != null) client.setProtocol(resourceRep.getProtocol());
if (resourceRep.getNodeReRegistrationTimeout() != null) { if (resourceRep.getNodeReRegistrationTimeout() != null) {
client.setNodeReRegistrationTimeout(resourceRep.getNodeReRegistrationTimeout()); client.setNodeReRegistrationTimeout(resourceRep.getNodeReRegistrationTimeout());
@ -1030,7 +1056,7 @@ public class RepresentationToModel {
logger.debugv("add redirect-uri to origin: {0}", redirectUri); logger.debugv("add redirect-uri to origin: {0}", redirectUri);
if (redirectUri.startsWith("http")) { if (redirectUri.startsWith("http")) {
String origin = UriUtils.getOrigin(redirectUri); String origin = UriUtils.getOrigin(redirectUri);
logger.debugv("adding default client origin: {0}" , origin); logger.debugv("adding default client origin: {0}", origin);
origins.add(origin); origins.add(origin);
} }
} }
@ -1051,7 +1077,6 @@ public class RepresentationToModel {
} }
if (resourceRep.getProtocolMappers() != null) { if (resourceRep.getProtocolMappers() != null) {
// first, remove all default/built in mappers // first, remove all default/built in mappers
Set<ProtocolMapperModel> mappers = client.getProtocolMappers(); Set<ProtocolMapperModel> mappers = client.getProtocolMappers();
@ -1091,7 +1116,8 @@ public class RepresentationToModel {
if (resourceRep.isUseTemplateScope() != null) client.setUseTemplateScope(resourceRep.isUseTemplateScope()); if (resourceRep.isUseTemplateScope() != null) client.setUseTemplateScope(resourceRep.isUseTemplateScope());
else client.setUseTemplateScope(resourceRep.getClientTemplate() != null); else client.setUseTemplateScope(resourceRep.getClientTemplate() != null);
if (resourceRep.isUseTemplateMappers() != null) client.setUseTemplateMappers(resourceRep.isUseTemplateMappers()); if (resourceRep.isUseTemplateMappers() != null)
client.setUseTemplateMappers(resourceRep.isUseTemplateMappers());
else client.setUseTemplateMappers(resourceRep.getClientTemplate() != null); else client.setUseTemplateMappers(resourceRep.getClientTemplate() != null);
client.updateClient(); client.updateClient();
@ -1108,7 +1134,8 @@ public class RepresentationToModel {
if (rep.isConsentRequired() != null) resource.setConsentRequired(rep.isConsentRequired()); if (rep.isConsentRequired() != null) resource.setConsentRequired(rep.isConsentRequired());
if (rep.isStandardFlowEnabled() != null) resource.setStandardFlowEnabled(rep.isStandardFlowEnabled()); if (rep.isStandardFlowEnabled() != null) resource.setStandardFlowEnabled(rep.isStandardFlowEnabled());
if (rep.isImplicitFlowEnabled() != null) resource.setImplicitFlowEnabled(rep.isImplicitFlowEnabled()); if (rep.isImplicitFlowEnabled() != null) resource.setImplicitFlowEnabled(rep.isImplicitFlowEnabled());
if (rep.isDirectAccessGrantsEnabled() != null) resource.setDirectAccessGrantsEnabled(rep.isDirectAccessGrantsEnabled()); if (rep.isDirectAccessGrantsEnabled() != null)
resource.setDirectAccessGrantsEnabled(rep.isDirectAccessGrantsEnabled());
if (rep.isServiceAccountsEnabled() != null) resource.setServiceAccountsEnabled(rep.isServiceAccountsEnabled()); if (rep.isServiceAccountsEnabled() != null) resource.setServiceAccountsEnabled(rep.isServiceAccountsEnabled());
if (rep.isPublicClient() != null) resource.setPublicClient(rep.isPublicClient()); if (rep.isPublicClient() != null) resource.setPublicClient(rep.isPublicClient());
if (rep.isFullScopeAllowed() != null) resource.setFullScopeAllowed(rep.isFullScopeAllowed()); if (rep.isFullScopeAllowed() != null) resource.setFullScopeAllowed(rep.isFullScopeAllowed());
@ -1117,8 +1144,10 @@ public class RepresentationToModel {
if (rep.getAdminUrl() != null) resource.setManagementUrl(rep.getAdminUrl()); if (rep.getAdminUrl() != null) resource.setManagementUrl(rep.getAdminUrl());
if (rep.getBaseUrl() != null) resource.setBaseUrl(rep.getBaseUrl()); if (rep.getBaseUrl() != null) resource.setBaseUrl(rep.getBaseUrl());
if (rep.isSurrogateAuthRequired() != null) resource.setSurrogateAuthRequired(rep.isSurrogateAuthRequired()); if (rep.isSurrogateAuthRequired() != null) resource.setSurrogateAuthRequired(rep.isSurrogateAuthRequired());
if (rep.getNodeReRegistrationTimeout() != null) resource.setNodeReRegistrationTimeout(rep.getNodeReRegistrationTimeout()); if (rep.getNodeReRegistrationTimeout() != null)
if (rep.getClientAuthenticatorType() != null) resource.setClientAuthenticatorType(rep.getClientAuthenticatorType()); resource.setNodeReRegistrationTimeout(rep.getNodeReRegistrationTimeout());
if (rep.getClientAuthenticatorType() != null)
resource.setClientAuthenticatorType(rep.getClientAuthenticatorType());
if (rep.getProtocol() != null) resource.setProtocol(rep.getProtocol()); if (rep.getProtocol() != null) resource.setProtocol(rep.getProtocol());
if (rep.getAttributes() != null) { if (rep.getAttributes() != null) {
@ -1191,9 +1220,9 @@ public class RepresentationToModel {
public static ClientTemplateModel createClientTemplate(KeycloakSession session, RealmModel realm, ClientTemplateRepresentation resourceRep) { public static ClientTemplateModel createClientTemplate(KeycloakSession session, RealmModel realm, ClientTemplateRepresentation resourceRep) {
logger.debug("Create client template: {0}" + resourceRep.getName()); logger.debug("Create client template: {0}" + resourceRep.getName());
ClientTemplateModel client = resourceRep.getId()!=null ? realm.addClientTemplate(resourceRep.getId(), resourceRep.getName()) : realm.addClientTemplate(resourceRep.getName()); ClientTemplateModel client = resourceRep.getId() != null ? realm.addClientTemplate(resourceRep.getId(), resourceRep.getName()) : realm.addClientTemplate(resourceRep.getName());
if (resourceRep.getName() != null) client.setName(resourceRep.getName()); if (resourceRep.getName() != null) client.setName(resourceRep.getName());
if(resourceRep.getDescription() != null) client.setDescription(resourceRep.getDescription()); if (resourceRep.getDescription() != null) client.setDescription(resourceRep.getDescription());
if (resourceRep.getProtocol() != null) client.setProtocol(resourceRep.getProtocol()); if (resourceRep.getProtocol() != null) client.setProtocol(resourceRep.getProtocol());
if (resourceRep.isFullScopeAllowed() != null) client.setFullScopeAllowed(resourceRep.isFullScopeAllowed()); if (resourceRep.isFullScopeAllowed() != null) client.setFullScopeAllowed(resourceRep.isFullScopeAllowed());
if (resourceRep.getProtocolMappers() != null) { if (resourceRep.getProtocolMappers() != null) {
@ -1208,13 +1237,18 @@ public class RepresentationToModel {
if (resourceRep.isBearerOnly() != null) client.setBearerOnly(resourceRep.isBearerOnly()); if (resourceRep.isBearerOnly() != null) client.setBearerOnly(resourceRep.isBearerOnly());
if (resourceRep.isConsentRequired() != null) client.setConsentRequired(resourceRep.isConsentRequired()); if (resourceRep.isConsentRequired() != null) client.setConsentRequired(resourceRep.isConsentRequired());
if (resourceRep.isStandardFlowEnabled() != null) client.setStandardFlowEnabled(resourceRep.isStandardFlowEnabled()); if (resourceRep.isStandardFlowEnabled() != null)
if (resourceRep.isImplicitFlowEnabled() != null) client.setImplicitFlowEnabled(resourceRep.isImplicitFlowEnabled()); client.setStandardFlowEnabled(resourceRep.isStandardFlowEnabled());
if (resourceRep.isDirectAccessGrantsEnabled() != null) client.setDirectAccessGrantsEnabled(resourceRep.isDirectAccessGrantsEnabled()); if (resourceRep.isImplicitFlowEnabled() != null)
if (resourceRep.isServiceAccountsEnabled() != null) client.setServiceAccountsEnabled(resourceRep.isServiceAccountsEnabled()); client.setImplicitFlowEnabled(resourceRep.isImplicitFlowEnabled());
if (resourceRep.isDirectAccessGrantsEnabled() != null)
client.setDirectAccessGrantsEnabled(resourceRep.isDirectAccessGrantsEnabled());
if (resourceRep.isServiceAccountsEnabled() != null)
client.setServiceAccountsEnabled(resourceRep.isServiceAccountsEnabled());
if (resourceRep.isPublicClient() != null) client.setPublicClient(resourceRep.isPublicClient()); if (resourceRep.isPublicClient() != null) client.setPublicClient(resourceRep.isPublicClient());
if (resourceRep.isFrontchannelLogout() != null) client.setFrontchannelLogout(resourceRep.isFrontchannelLogout()); if (resourceRep.isFrontchannelLogout() != null)
client.setFrontchannelLogout(resourceRep.isFrontchannelLogout());
if (resourceRep.getAttributes() != null) { if (resourceRep.getAttributes() != null) {
for (Map.Entry<String, String> entry : resourceRep.getAttributes().entrySet()) { for (Map.Entry<String, String> entry : resourceRep.getAttributes().entrySet()) {
@ -1240,7 +1274,8 @@ public class RepresentationToModel {
if (rep.isConsentRequired() != null) resource.setConsentRequired(rep.isConsentRequired()); if (rep.isConsentRequired() != null) resource.setConsentRequired(rep.isConsentRequired());
if (rep.isStandardFlowEnabled() != null) resource.setStandardFlowEnabled(rep.isStandardFlowEnabled()); if (rep.isStandardFlowEnabled() != null) resource.setStandardFlowEnabled(rep.isStandardFlowEnabled());
if (rep.isImplicitFlowEnabled() != null) resource.setImplicitFlowEnabled(rep.isImplicitFlowEnabled()); if (rep.isImplicitFlowEnabled() != null) resource.setImplicitFlowEnabled(rep.isImplicitFlowEnabled());
if (rep.isDirectAccessGrantsEnabled() != null) resource.setDirectAccessGrantsEnabled(rep.isDirectAccessGrantsEnabled()); if (rep.isDirectAccessGrantsEnabled() != null)
resource.setDirectAccessGrantsEnabled(rep.isDirectAccessGrantsEnabled());
if (rep.isServiceAccountsEnabled() != null) resource.setServiceAccountsEnabled(rep.isServiceAccountsEnabled()); if (rep.isServiceAccountsEnabled() != null) resource.setServiceAccountsEnabled(rep.isServiceAccountsEnabled());
if (rep.isPublicClient() != null) resource.setPublicClient(rep.isPublicClient()); if (rep.isPublicClient() != null) resource.setPublicClient(rep.isPublicClient());
if (rep.isFullScopeAllowed() != null) resource.setFullScopeAllowed(rep.isFullScopeAllowed()); if (rep.isFullScopeAllowed() != null) resource.setFullScopeAllowed(rep.isFullScopeAllowed());
@ -1391,7 +1426,8 @@ public class RepresentationToModel {
if (client == null) { if (client == null) {
throw new RuntimeException("Unable to find client specified for service account link. Client: " + clientId); throw new RuntimeException("Unable to find client specified for service account link. Client: " + clientId);
} }
user.setServiceAccountClientLink(client.getId());; user.setServiceAccountClientLink(client.getId());
;
} }
if (userRep.getGroups() != null) { if (userRep.getGroups() != null) {
for (String path : userRep.getGroups()) { for (String path : userRep.getGroups()) {
@ -1406,7 +1442,7 @@ public class RepresentationToModel {
return user; return user;
} }
public static void createCredentials(UserRepresentation userRep, KeycloakSession session, RealmModel realm,UserModel user) { public static void createCredentials(UserRepresentation userRep, KeycloakSession session, RealmModel realm, UserModel user) {
if (userRep.getCredentials() != null) { if (userRep.getCredentials() != null) {
for (CredentialRepresentation cred : userRep.getCredentials()) { for (CredentialRepresentation cred : userRep.getCredentials()) {
updateCredential(session, realm, user, cred); updateCredential(session, realm, user, cred);
@ -1541,6 +1577,7 @@ public class RepresentationToModel {
} }
} }
} }
private static void importIdentityProviderMappers(RealmRepresentation rep, RealmModel newRealm) { private static void importIdentityProviderMappers(RealmRepresentation rep, RealmModel newRealm) {
if (rep.getIdentityProviderMappers() != null) { if (rep.getIdentityProviderMappers() != null) {
for (IdentityProviderMapperRepresentation representation : rep.getIdentityProviderMappers()) { for (IdentityProviderMapperRepresentation representation : rep.getIdentityProviderMappers()) {
@ -1548,7 +1585,8 @@ public class RepresentationToModel {
} }
} }
} }
public static IdentityProviderModel toModel(RealmModel realm, IdentityProviderRepresentation representation) {
public static IdentityProviderModel toModel(RealmModel realm, IdentityProviderRepresentation representation) {
IdentityProviderModel identityProviderModel = new IdentityProviderModel(); IdentityProviderModel identityProviderModel = new IdentityProviderModel();
identityProviderModel.setInternalId(representation.getInternalId()); identityProviderModel.setInternalId(representation.getInternalId());
@ -1556,6 +1594,7 @@ public class RepresentationToModel {
identityProviderModel.setDisplayName(representation.getDisplayName()); identityProviderModel.setDisplayName(representation.getDisplayName());
identityProviderModel.setProviderId(representation.getProviderId()); identityProviderModel.setProviderId(representation.getProviderId());
identityProviderModel.setEnabled(representation.isEnabled()); identityProviderModel.setEnabled(representation.isEnabled());
identityProviderModel.setLinkOnly(representation.isLinkOnly());
identityProviderModel.setTrustEmail(representation.isTrustEmail()); identityProviderModel.setTrustEmail(representation.isTrustEmail());
identityProviderModel.setAuthenticateByDefault(representation.isAuthenticateByDefault()); identityProviderModel.setAuthenticateByDefault(representation.isAuthenticateByDefault());
identityProviderModel.setStoreToken(representation.isStoreToken()); identityProviderModel.setStoreToken(representation.isStoreToken());
@ -1567,24 +1606,24 @@ public class RepresentationToModel {
flowAlias = DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW; flowAlias = DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW;
} }
AuthenticationFlowModel flowModel = realm.getFlowByAlias(flowAlias); AuthenticationFlowModel flowModel = realm.getFlowByAlias(flowAlias);
if (flowModel == null) { if (flowModel == null) {
throw new ModelException("No available authentication flow with alias: " + flowAlias); throw new ModelException("No available authentication flow with alias: " + flowAlias);
} }
identityProviderModel.setFirstBrokerLoginFlowId(flowModel.getId()); identityProviderModel.setFirstBrokerLoginFlowId(flowModel.getId());
flowAlias = representation.getPostBrokerLoginFlowAlias(); flowAlias = representation.getPostBrokerLoginFlowAlias();
if (flowAlias == null || flowAlias.trim().length() == 0) { if (flowAlias == null || flowAlias.trim().length() == 0) {
identityProviderModel.setPostBrokerLoginFlowId(null); identityProviderModel.setPostBrokerLoginFlowId(null);
} else { } else {
flowModel = realm.getFlowByAlias(flowAlias); flowModel = realm.getFlowByAlias(flowAlias);
if (flowModel == null) { if (flowModel == null) {
throw new ModelException("No available authentication flow with alias: " + flowAlias); throw new ModelException("No available authentication flow with alias: " + flowAlias);
} }
identityProviderModel.setPostBrokerLoginFlowId(flowModel.getId()); identityProviderModel.setPostBrokerLoginFlowId(flowModel.getId());
} }
return identityProviderModel; return identityProviderModel;
} }
public static ProtocolMapperModel toModel(ProtocolMapperRepresentation rep) { public static ProtocolMapperModel toModel(ProtocolMapperRepresentation rep) {
@ -1906,7 +1945,7 @@ public class RepresentationToModel {
if (roles != null && !roles.isEmpty()) { if (roles != null && !roles.isEmpty()) {
try { try {
List<Map> rolesMap = (List<Map>)JsonSerialization.readValue(roles, List.class); List<Map> rolesMap = (List<Map>) JsonSerialization.readValue(roles, List.class);
config.put("roles", JsonSerialization.writeValueAsString(rolesMap.stream().map(roleConfig -> { config.put("roles", JsonSerialization.writeValueAsString(rolesMap.stream().map(roleConfig -> {
String roleName = roleConfig.get("id").toString(); String roleName = roleConfig.get("id").toString();
String clientId = null; String clientId = null;
@ -2210,7 +2249,8 @@ public class RepresentationToModel {
} }
} }
if (!hasPolicy) { if (!hasPolicy) {
policy.removeAssociatedPolicy(policyModel);; policy.removeAssociatedPolicy(policyModel);
;
} }
} }
@ -2284,7 +2324,7 @@ public class RepresentationToModel {
existing.setIconUri(resource.getIconUri()); existing.setIconUri(resource.getIconUri());
existing.updateScopes(resource.getScopes().stream() existing.updateScopes(resource.getScopes().stream()
.map((ScopeRepresentation scope) -> toModel(scope, resourceServer, authorization)) .map((ScopeRepresentation scope) -> toModel(scope, resourceServer, authorization))
.collect(Collectors.toSet())); .collect(Collectors.toSet()));
return existing; return existing;
} }
@ -2353,7 +2393,7 @@ public class RepresentationToModel {
} }
} }
if (userRep.getRequiredActions() != null) { if (userRep.getRequiredActions() != null) {
for (String action: userRep.getRequiredActions()) { for (String action : userRep.getRequiredActions()) {
federatedStorage.addRequiredAction(newRealm, userRep.getId(), action); federatedStorage.addRequiredAction(newRealm, userRep.getId(), action);
} }
} }

View file

@ -48,6 +48,9 @@ public class IdentityProviderModel implements Serializable {
private boolean storeToken; private boolean storeToken;
protected boolean addReadTokenRoleOnCreate; protected boolean addReadTokenRoleOnCreate;
protected boolean linkOnly;
/** /**
* Specifies if particular provider should be used by default for authentication even before displaying login screen * Specifies if particular provider should be used by default for authentication even before displaying login screen
*/ */
@ -78,6 +81,7 @@ public class IdentityProviderModel implements Serializable {
this.enabled = model.isEnabled(); this.enabled = model.isEnabled();
this.trustEmail = model.isTrustEmail(); this.trustEmail = model.isTrustEmail();
this.storeToken = model.isStoreToken(); this.storeToken = model.isStoreToken();
this.linkOnly = model.isLinkOnly();
this.authenticateByDefault = model.isAuthenticateByDefault(); this.authenticateByDefault = model.isAuthenticateByDefault();
this.addReadTokenRoleOnCreate = model.addReadTokenRoleOnCreate; this.addReadTokenRoleOnCreate = model.addReadTokenRoleOnCreate;
this.firstBrokerLoginFlowId = model.getFirstBrokerLoginFlowId(); this.firstBrokerLoginFlowId = model.getFirstBrokerLoginFlowId();
@ -125,6 +129,14 @@ public class IdentityProviderModel implements Serializable {
this.storeToken = storeToken; this.storeToken = storeToken;
} }
public boolean isLinkOnly() {
return linkOnly;
}
public void setLinkOnly(boolean linkOnly) {
this.linkOnly = linkOnly;
}
@Deprecated @Deprecated
public boolean isAuthenticateByDefault() { public boolean isAuthenticateByDefault() {
return authenticateByDefault; return authenticateByDefault;

View file

@ -49,7 +49,7 @@ public class IdentityProviderBean {
if (!identityProviders.isEmpty()) { if (!identityProviders.isEmpty()) {
Set<IdentityProvider> orderedSet = new TreeSet<>(IdentityProviderComparator.INSTANCE); Set<IdentityProvider> orderedSet = new TreeSet<>(IdentityProviderComparator.INSTANCE);
for (IdentityProviderModel identityProvider : identityProviders) { for (IdentityProviderModel identityProvider : identityProviders) {
if (identityProvider.isEnabled()) { if (identityProvider.isEnabled() && !identityProvider.isLinkOnly()) {
addIdentityProvider(orderedSet, realm, baseURI, identityProvider); addIdentityProvider(orderedSet, realm, baseURI, identityProvider);
} }
} }

View file

@ -79,6 +79,14 @@ public class Urls {
return uriBuilder.build(realmName, providerId); return uriBuilder.build(realmName, providerId);
} }
public static URI identityProviderLinkRequest(URI baseUri, String providerId, String realmName) {
UriBuilder uriBuilder = realmBase(baseUri).path(RealmsResource.class, "getBrokerService")
.replaceQuery(null)
.path(IdentityBrokerService.class, "clientInitiatedAccountLinking");
return uriBuilder.build(realmName, providerId);
}
public static URI identityProviderRetrieveToken(URI baseUri, String providerId, String realmName) { public static URI identityProviderRetrieveToken(URI baseUri, String providerId, String realmName) {
return realmBase(baseUri).path(RealmsResource.class, "getBrokerService") return realmBase(baseUri).path(RealmsResource.class, "getBrokerService")
.path(IdentityBrokerService.class, "retrieveToken") .path(IdentityBrokerService.class, "retrieveToken")

View file

@ -396,6 +396,11 @@ public class RealmManager {
roleModel.setDescription("${role_" + role + "}"); roleModel.setDescription("${role_" + role + "}");
roleModel.setScopeParamRequired(false); roleModel.setScopeParamRequired(false);
} }
RoleModel manageAccountLinks = client.addRole(AccountRoles.MANAGE_ACCOUNT_LINKS);
manageAccountLinks.setDescription("${role_" + AccountRoles.MANAGE_ACCOUNT_LINKS + "}");
manageAccountLinks.setScopeParamRequired(false);
RoleModel manageAccount = client.getRole(AccountRoles.MANAGE_ACCOUNT);
manageAccount.addCompositeRole(manageAccountLinks);
} }
} }

View file

@ -17,6 +17,7 @@
package org.keycloak.services.resources; package org.keycloak.services.resources;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.UriUtils; import org.keycloak.common.util.UriUtils;
import org.keycloak.credential.CredentialModel; import org.keycloak.credential.CredentialModel;
import org.keycloak.events.Details; import org.keycloak.events.Details;
@ -70,6 +71,9 @@ import javax.ws.rs.core.Variant;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.URI; import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -771,14 +775,19 @@ public class AccountService extends AbstractSecuredLocalService {
String redirectUri = UriBuilder.fromUri(Urls.accountFederatedIdentityPage(uriInfo.getBaseUri(), realm.getName())).build().toString(); String redirectUri = UriBuilder.fromUri(Urls.accountFederatedIdentityPage(uriInfo.getBaseUri(), realm.getName())).build().toString();
try { try {
ClientSessionModel clientSession = auth.getClientSession(); String nonce = UUID.randomUUID().toString();
ClientSessionCode clientSessionCode = new ClientSessionCode(session, realm, clientSession); MessageDigest md = MessageDigest.getInstance("SHA-256");
clientSessionCode.setAction(ClientSessionModel.Action.AUTHENTICATE.name()); String input = nonce + auth.getSession().getId() + auth.getClientSession().getId() + providerId;
clientSession.setRedirectUri(redirectUri); byte[] check = md.digest(input.getBytes(StandardCharsets.UTF_8));
clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, UUID.randomUUID().toString()); String hash = Base64Url.encode(check);
URI linkUrl = Urls.identityProviderLinkRequest(this.uriInfo.getBaseUri(), providerId, realm.getName());
return Response.seeOther( linkUrl = UriBuilder.fromUri(linkUrl)
Urls.identityProviderAuthnRequest(this.uriInfo.getBaseUri(), providerId, realm.getName(), clientSessionCode.getCode())) .queryParam("nonce", nonce)
.queryParam("hash", hash)
.queryParam("client_id", client.getClientId())
.queryParam("redirect_uri", redirectUri)
.build();
return Response.seeOther(linkUrl)
.build(); .build();
} catch (Exception spe) { } catch (Exception spe) {
setReferrerOnPage(); setReferrerOnPage();

View file

@ -107,6 +107,7 @@ import java.util.Set;
import java.util.UUID; import java.util.UUID;
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT; import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT;
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT_LINKS;
import static org.keycloak.models.ClientSessionModel.Action.AUTHENTICATE; import static org.keycloak.models.ClientSessionModel.Action.AUTHENTICATE;
import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_CLIENT_ID; import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_CLIENT_ID;
@ -230,10 +231,11 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
} }
AuthResult cookieResult = authenticationManager.authenticateIdentityCookie(session, realmModel, true); AuthResult cookieResult = authenticationManager.authenticateIdentityCookie(session, realmModel, true);
String errorParam = "link_error";
if (cookieResult == null) { if (cookieResult == null) {
event.error(Errors.NOT_LOGGED_IN); event.error(Errors.NOT_LOGGED_IN);
UriBuilder builder = UriBuilder.fromUri(redirectUri) UriBuilder builder = UriBuilder.fromUri(redirectUri)
.queryParam("error", Errors.NOT_LOGGED_IN) .queryParam(errorParam, Errors.NOT_LOGGED_IN)
.queryParam("nonce", nonce); .queryParam("nonce", nonce);
return Response.status(302).location(builder.build()).build(); return Response.status(302).location(builder.build()).build();
@ -264,11 +266,30 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
throw new ErrorPageException(session, Messages.INVALID_REQUEST); throw new ErrorPageException(session, Messages.INVALID_REQUEST);
} }
ClientModel accountService = this.realmModel.getClientByClientId(ACCOUNT_MANAGEMENT_CLIENT_ID);
if (!accountService.getId().equals(client.getId())) {
RoleModel manageAccountRole = accountService.getRole(MANAGE_ACCOUNT);
if (!clientSession.getRoles().contains(manageAccountRole.getId())) {
RoleModel linkRole = accountService.getRole(MANAGE_ACCOUNT_LINKS);
if (!clientSession.getRoles().contains(linkRole.getId())) {
event.error(Errors.NOT_ALLOWED);
UriBuilder builder = UriBuilder.fromUri(redirectUri)
.queryParam(errorParam, Errors.NOT_ALLOWED)
.queryParam("nonce", nonce);
return Response.status(302).location(builder.build()).build();
}
}
}
IdentityProviderModel identityProviderModel = realmModel.getIdentityProviderByAlias(providerId); IdentityProviderModel identityProviderModel = realmModel.getIdentityProviderByAlias(providerId);
if (identityProviderModel == null) { if (identityProviderModel == null) {
event.error(Errors.UNKNOWN_IDENTITY_PROVIDER); event.error(Errors.UNKNOWN_IDENTITY_PROVIDER);
UriBuilder builder = UriBuilder.fromUri(redirectUri) UriBuilder builder = UriBuilder.fromUri(redirectUri)
.queryParam("error", Errors.UNKNOWN_IDENTITY_PROVIDER) .queryParam(errorParam, Errors.UNKNOWN_IDENTITY_PROVIDER)
.queryParam("nonce", nonce); .queryParam("nonce", nonce);
return Response.status(302).location(builder.build()).build(); return Response.status(302).location(builder.build()).build();
@ -329,7 +350,18 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
} }
ClientSessionCode clientSessionCode = parsedCode.clientSessionCode; ClientSessionCode clientSessionCode = parsedCode.clientSessionCode;
IdentityProvider identityProvider = getIdentityProvider(session, realmModel, providerId); IdentityProviderModel identityProviderModel = realmModel.getIdentityProviderByAlias(providerId);
if (identityProviderModel == null) {
throw new IdentityBrokerException("Identity Provider [" + providerId + "] not found.");
}
if (identityProviderModel.isLinkOnly()) {
throw new IdentityBrokerException("Identity Provider [" + providerId + "] is not allowed to perform a login.");
}
IdentityProviderFactory providerFactory = getIdentityProviderFactory(session, identityProviderModel);
IdentityProvider identityProvider = providerFactory.create(session, identityProviderModel);
Response response = identityProvider.performLogin(createAuthenticationRequest(providerId, clientSessionCode)); Response response = identityProvider.performLogin(createAuthenticationRequest(providerId, clientSessionCode));
if (response != null) { if (response != null) {
@ -786,23 +818,28 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
private Response performAccountLinking(ClientSessionModel clientSession, BrokeredIdentityContext context, FederatedIdentityModel federatedIdentityModel, UserModel federatedUser) { private Response performAccountLinking(ClientSessionModel clientSession, BrokeredIdentityContext context, FederatedIdentityModel federatedIdentityModel, UserModel federatedUser) {
this.event.event(EventType.FEDERATED_IDENTITY_LINK); this.event.event(EventType.FEDERATED_IDENTITY_LINK);
UserModel authenticatedUser = clientSession.getUserSession().getUser();
if (federatedUser != null) { if (federatedUser != null) {
// refresh the token if (authenticatedUser.getId().equals(federatedUser.getId())) {
if (context.getIdpConfig().isStoreToken()) { // refresh the token
federatedIdentityModel = this.session.users().getFederatedIdentity(federatedUser, context.getIdpConfig().getAlias(), this.realmModel); if (context.getIdpConfig().isStoreToken()) {
if (!ObjectUtil.isEqualOrBothNull(context.getToken(), federatedIdentityModel.getToken())) { federatedIdentityModel = this.session.users().getFederatedIdentity(federatedUser, context.getIdpConfig().getAlias(), this.realmModel);
if (!ObjectUtil.isEqualOrBothNull(context.getToken(), federatedIdentityModel.getToken())) {
this.session.users().updateFederatedIdentity(this.realmModel, federatedUser, federatedIdentityModel); this.session.users().updateFederatedIdentity(this.realmModel, federatedUser, federatedIdentityModel);
if (isDebugEnabled()) { if (isDebugEnabled()) {
logger.debugf("Identity [%s] update with response from identity provider [%s].", federatedUser, context.getIdpConfig().getAlias()); logger.debugf("Identity [%s] update with response from identity provider [%s].", federatedUser, context.getIdpConfig().getAlias());
}
} }
} }
return Response.status(302).location(UriBuilder.fromUri(clientSession.getRedirectUri()).build()).build();
} else {
return redirectToAccountErrorPage(clientSession, Messages.IDENTITY_PROVIDER_ALREADY_LINKED, context.getIdpConfig().getAlias());
} }
return Response.status(302).location(UriBuilder.fromUri(clientSession.getRedirectUri()).build()).build();
} }
UserModel authenticatedUser = clientSession.getUserSession().getUser();
if (isDebugEnabled()) { if (isDebugEnabled()) {
logger.debugf("Linking account [%s] from identity provider [%s] to user [%s].", federatedIdentityModel, context.getIdpConfig().getAlias(), authenticatedUser); logger.debugf("Linking account [%s] from identity provider [%s] to user [%s].", federatedIdentityModel, context.getIdpConfig().getAlias(), authenticatedUser);

View file

@ -39,6 +39,7 @@ import java.util.UUID;
public class ClientInitiatedAccountLinkServlet extends HttpServlet { public class ClientInitiatedAccountLinkServlet extends HttpServlet {
@Override @Override
protected void doGet(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException { protected void doGet(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("Cache-Control", "no-cache");
if (request.getRequestURI().endsWith("/link") && request.getParameter("response") == null) { if (request.getRequestURI().endsWith("/link") && request.getParameter("response") == null) {
String provider = request.getParameter("provider"); String provider = request.getParameter("provider");
String realm = request.getParameter("realm"); String realm = request.getParameter("realm");
@ -68,13 +69,11 @@ public class ClientInitiatedAccountLinkServlet extends HttpServlet {
resp.setStatus(302); resp.setStatus(302);
resp.setHeader("Location", accountLinkUrl); resp.setHeader("Location", accountLinkUrl);
} else if (request.getRequestURI().endsWith("/link") && request.getParameter("response") != null) { } else if (request.getRequestURI().endsWith("/link") && request.getParameter("response") != null) {
String hash = request.getSession().getAttribute("hash").toString();
String hashParam = request.getParameter("hash");
resp.setStatus(200); resp.setStatus(200);
resp.setContentType("text/html"); resp.setContentType("text/html");
PrintWriter pw = resp.getWriter(); PrintWriter pw = resp.getWriter();
pw.printf("<html><head><title>%s</title></head><body>", "Client Linking"); pw.printf("<html><head><title>%s</title></head><body>", "Client Linking");
String error = request.getParameter("error"); String error = request.getParameter("link_error");
if (error != null) { if (error != null) {
pw.println("Link error: " + error); pw.println("Link error: " + error);
} else { } else {

View file

@ -877,7 +877,8 @@ public class AccountTest extends AbstractTestRealmKeycloakTest {
Assert.assertThat(apps.keySet(), containsInAnyOrder("Account", "test-app", "test-app-scope", "third-party", "test-app-authz", "My Named Test App", "Test App Named - ${client_account}")); Assert.assertThat(apps.keySet(), containsInAnyOrder("Account", "test-app", "test-app-scope", "third-party", "test-app-authz", "My Named Test App", "Test App Named - ${client_account}"));
AccountApplicationsPage.AppEntry accountEntry = apps.get("Account"); AccountApplicationsPage.AppEntry accountEntry = apps.get("Account");
Assert.assertEquals(2, accountEntry.getRolesAvailable().size()); Assert.assertEquals(3, accountEntry.getRolesAvailable().size());
Assert.assertTrue(accountEntry.getRolesAvailable().contains("Manage account links in Account"));
Assert.assertTrue(accountEntry.getRolesAvailable().contains("Manage account in Account")); Assert.assertTrue(accountEntry.getRolesAvailable().contains("Manage account in Account"));
Assert.assertTrue(accountEntry.getRolesAvailable().contains("View profile in Account")); Assert.assertTrue(accountEntry.getRolesAvailable().contains("View profile in Account"));
Assert.assertEquals(1, accountEntry.getRolesGranted().size()); Assert.assertEquals(1, accountEntry.getRolesGranted().size());

View file

@ -414,7 +414,8 @@ public class ClientTest extends AbstractAdminTest {
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAll(), AccountRoles.VIEW_PROFILE); Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAll(), AccountRoles.VIEW_PROFILE);
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listEffective(), AccountRoles.VIEW_PROFILE); Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listEffective(), AccountRoles.VIEW_PROFILE);
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAvailable(), AccountRoles.MANAGE_ACCOUNT);
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAvailable(), AccountRoles.MANAGE_ACCOUNT, AccountRoles.MANAGE_ACCOUNT_LINKS);
Assert.assertNames(scopesResource.getAll().getRealmMappings(), "role1"); Assert.assertNames(scopesResource.getAll().getRealmMappings(), "role1");
Assert.assertNames(scopesResource.getAll().getClientMappings().get(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).getMappings(), AccountRoles.VIEW_PROFILE); Assert.assertNames(scopesResource.getAll().getClientMappings().get(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).getMappings(), AccountRoles.VIEW_PROFILE);
@ -429,7 +430,7 @@ public class ClientTest extends AbstractAdminTest {
Assert.assertNames(scopesResource.realmLevel().listEffective()); Assert.assertNames(scopesResource.realmLevel().listEffective());
Assert.assertNames(scopesResource.realmLevel().listAvailable(), "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, "role1", "role2"); Assert.assertNames(scopesResource.realmLevel().listAvailable(), "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, "role1", "role2");
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAll()); Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAll());
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAvailable(), AccountRoles.VIEW_PROFILE, AccountRoles.MANAGE_ACCOUNT); Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAvailable(), AccountRoles.VIEW_PROFILE, AccountRoles.MANAGE_ACCOUNT, AccountRoles.MANAGE_ACCOUNT_LINKS);
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listEffective()); Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listEffective());
} }

View file

@ -38,7 +38,7 @@ public class BrokerTestTools {
IdentityProviderRepresentation identityProviderRepresentation = new IdentityProviderRepresentation(); IdentityProviderRepresentation identityProviderRepresentation = new IdentityProviderRepresentation();
identityProviderRepresentation.setAlias(alias); identityProviderRepresentation.setAlias(alias);
identityProviderRepresentation.setDisplayName(providerId); identityProviderRepresentation.setDisplayName(alias);
identityProviderRepresentation.setProviderId(providerId); identityProviderRepresentation.setProviderId(providerId);
identityProviderRepresentation.setEnabled(true); identityProviderRepresentation.setEnabled(true);
@ -84,7 +84,16 @@ public class BrokerTestTools {
* @param suiteContext * @param suiteContext
*/ */
public static void createKcOidcBroker(Keycloak adminClient, String childRealm, String idpRealm, SuiteContext suiteContext) { public static void createKcOidcBroker(Keycloak adminClient, String childRealm, String idpRealm, SuiteContext suiteContext) {
IdentityProviderRepresentation idp = createIdentityProvider(idpRealm, IDP_OIDC_PROVIDER_ID); createKcOidcBroker(adminClient, childRealm, idpRealm, suiteContext, idpRealm, false);
}
public static void createKcOidcBroker(Keycloak adminClient, String childRealm, String idpRealm, SuiteContext suiteContext, String alias, boolean linkOnly) {
IdentityProviderRepresentation idp = createIdentityProvider(alias, IDP_OIDC_PROVIDER_ID);
idp.setLinkOnly(linkOnly);
Map<String, String> config = idp.getConfig(); Map<String, String> config = idp.getConfig();
config.put("clientId", childRealm); config.put("clientId", childRealm);
@ -109,8 +118,5 @@ public class BrokerTestTools {
client.setAdminUrl(getAuthRoot(suiteContext) + client.setAdminUrl(getAuthRoot(suiteContext) +
"/auth/realms/" + childRealm + "/broker/" + idpRealm + "/endpoint"); "/auth/realms/" + childRealm + "/broker/" + idpRealm + "/endpoint");
adminClient.realm(idpRealm).clients().create(client); adminClient.realm(idpRealm).clients().create(client);
} }
} }

View file

@ -25,11 +25,15 @@ import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.common.util.Base64Url; import org.keycloak.common.util.Base64Url;
import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.FederatedIdentityRepresentation; import org.keycloak.representations.idm.FederatedIdentityRepresentation;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserRepresentation;
@ -50,8 +54,13 @@ import java.net.URL;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT;
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT_LINKS;
import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_CLIENT_ID;
import static org.keycloak.testsuite.admin.ApiUtil.createUserAndResetPasswordWithAdminClient; import static org.keycloak.testsuite.admin.ApiUtil.createUserAndResetPasswordWithAdminClient;
/** /**
@ -153,7 +162,7 @@ public class ClientInitiatedAccountLinkTest extends AbstractKeycloakTest {
user.setEnabled(true); user.setEnabled(true);
childUserId = createUserAndResetPasswordWithAdminClient(realm, user, "password"); childUserId = createUserAndResetPasswordWithAdminClient(realm, user, "password");
// have to add a role as stupid undertow auth manager doesn't like "*" // have to add a role as undertow default auth manager doesn't like "*". todo we can remove this eventually as undertow fixes this in later versions
realm.roles().create(new RoleRepresentation("user", null, false)); realm.roles().create(new RoleRepresentation("user", null, false));
RoleRepresentation role = realm.roles().get("user").toRepresentation(); RoleRepresentation role = realm.roles().get("user").toRepresentation();
List<RoleRepresentation> roles = new LinkedList<>(); List<RoleRepresentation> roles = new LinkedList<>();
@ -171,12 +180,21 @@ public class ClientInitiatedAccountLinkTest extends AbstractKeycloakTest {
BrokerTestTools.createKcOidcBroker(adminClient, CHILD_IDP, PARENT_IDP, suiteContext); BrokerTestTools.createKcOidcBroker(adminClient, CHILD_IDP, PARENT_IDP, suiteContext);
} }
//@Test
public void testUi() throws Exception {
Thread.sleep(1000000000);
}
@Test @Test
public void testErrorConditions() { public void testErrorConditions() throws Exception {
RealmResource realm = adminClient.realms().realm(CHILD_IDP); RealmResource realm = adminClient.realms().realm(CHILD_IDP);
List<FederatedIdentityRepresentation> links = realm.users().get(childUserId).getFederatedIdentity(); List<FederatedIdentityRepresentation> links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty()); Assert.assertTrue(links.isEmpty());
ClientRepresentation client = adminClient.realms().realm(CHILD_IDP).clients().findByClientId("client-linking").get(0);
UriBuilder redirectUri = UriBuilder.fromUri(appPage.getInjectedUrl().toString()) UriBuilder redirectUri = UriBuilder.fromUri(appPage.getInjectedUrl().toString())
.path("link") .path("link")
.queryParam("response", "true"); .queryParam("response", "true");
@ -190,19 +208,24 @@ public class ClientInitiatedAccountLinkTest extends AbstractKeycloakTest {
String linkUrl = directLinking String linkUrl = directLinking
.build(PARENT_IDP).toString(); .build(PARENT_IDP).toString();
// test not logged in // test not logged in
driver.navigate().to(linkUrl); driver.navigate().to(linkUrl);
Assert.assertTrue(loginPage.isCurrent(CHILD_IDP));
loginPage.login("child", "password");
Assert.assertTrue(driver.getCurrentUrl().contains("error=not_logged_in")); Assert.assertTrue(driver.getCurrentUrl().contains("link_error=not_logged_in"));
logoutAll();
// now log in // now log in
driver.navigate().to( appPage.getInjectedUrl() + "/hello"); driver.navigate().to( appPage.getInjectedUrl() + "/hello");
Assert.assertTrue(loginPage.isCurrent(CHILD_IDP));
loginPage.login("child", "password"); loginPage.login("child", "password");
Assert.assertTrue(driver.getCurrentUrl().startsWith(appPage.getInjectedUrl() + "/hello")); Assert.assertTrue(driver.getCurrentUrl().startsWith(appPage.getInjectedUrl() + "/hello"));
Assert.assertTrue(driver.getPageSource().contains("Unknown request:"));
// now test CSRF with bad hash. // now test CSRF with bad hash.
@ -210,12 +233,140 @@ public class ClientInitiatedAccountLinkTest extends AbstractKeycloakTest {
Assert.assertTrue(driver.getPageSource().contains("We're sorry...")); Assert.assertTrue(driver.getPageSource().contains("We're sorry..."));
logoutAll();
// now log in again with client that does not have scope
String accountId = adminClient.realms().realm(CHILD_IDP).clients().findByClientId(ACCOUNT_MANAGEMENT_CLIENT_ID).get(0).getId();
RoleRepresentation manageAccount = adminClient.realms().realm(CHILD_IDP).clients().get(accountId).roles().get(MANAGE_ACCOUNT).toRepresentation();
RoleRepresentation manageLinks = adminClient.realms().realm(CHILD_IDP).clients().get(accountId).roles().get(MANAGE_ACCOUNT_LINKS).toRepresentation();
RoleRepresentation userRole = adminClient.realms().realm(CHILD_IDP).roles().get("user").toRepresentation();
client.setFullScopeAllowed(false);
ClientResource clientResource = adminClient.realms().realm(CHILD_IDP).clients().get(client.getId());
clientResource.update(client);
List<RoleRepresentation> roles = new LinkedList<>();
roles.add(userRole);
clientResource.getScopeMappings().realmLevel().add(roles);
driver.navigate().to( appPage.getInjectedUrl() + "/hello");
Assert.assertTrue(loginPage.isCurrent(CHILD_IDP));
loginPage.login("child", "password");
Assert.assertTrue(driver.getCurrentUrl().startsWith(appPage.getInjectedUrl() + "/hello"));
Assert.assertTrue(driver.getPageSource().contains("Unknown request:"));
UriBuilder linkBuilder = UriBuilder.fromUri(appPage.getInjectedUrl().toString())
.path("link");
String clientLinkUrl = linkBuilder.clone()
.queryParam("realm", CHILD_IDP)
.queryParam("provider", PARENT_IDP).build().toString();
driver.navigate().to(clientLinkUrl);
Assert.assertTrue(driver.getCurrentUrl().contains("error=not_allowed"));
logoutAll();
// add MANAGE_ACCOUNT_LINKS scope should pass.
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
roles = new LinkedList<>();
roles.add(manageLinks);
clientResource.getScopeMappings().clientLevel(accountId).add(roles);
driver.navigate().to(clientLinkUrl);
Assert.assertTrue(loginPage.isCurrent(CHILD_IDP));
loginPage.login("child", "password");
Assert.assertTrue(loginPage.isCurrent(PARENT_IDP));
loginPage.login(PARENT_USERNAME, "password");
Assert.assertTrue(driver.getCurrentUrl().startsWith(linkBuilder.toTemplate()));
Assert.assertTrue(driver.getPageSource().contains("Account Linked"));
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertFalse(links.isEmpty());
realm.users().get(childUserId).removeFederatedIdentity(PARENT_IDP);
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
clientResource.getScopeMappings().clientLevel(accountId).remove(roles);
logoutAll();
driver.navigate().to(clientLinkUrl);
Assert.assertTrue(loginPage.isCurrent(CHILD_IDP));
loginPage.login("child", "password");
Assert.assertTrue(driver.getCurrentUrl().contains("link_error=not_allowed"));
logoutAll();
// add MANAGE_ACCOUNT scope should pass
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
roles = new LinkedList<>();
roles.add(manageAccount);
clientResource.getScopeMappings().clientLevel(accountId).add(roles);
driver.navigate().to(clientLinkUrl);
Assert.assertTrue(loginPage.isCurrent(CHILD_IDP));
loginPage.login("child", "password");
Assert.assertTrue(loginPage.isCurrent(PARENT_IDP));
loginPage.login(PARENT_USERNAME, "password");
Assert.assertTrue(driver.getCurrentUrl().startsWith(linkBuilder.toTemplate()));
Assert.assertTrue(driver.getPageSource().contains("Account Linked"));
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertFalse(links.isEmpty());
realm.users().get(childUserId).removeFederatedIdentity(PARENT_IDP);
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
clientResource.getScopeMappings().clientLevel(accountId).remove(roles);
logoutAll();
driver.navigate().to(clientLinkUrl);
Assert.assertTrue(loginPage.isCurrent(CHILD_IDP));
loginPage.login("child", "password");
Assert.assertTrue(driver.getCurrentUrl().contains("link_error=not_allowed"));
logoutAll();
// undo fullScopeAllowed
client = adminClient.realms().realm(CHILD_IDP).clients().findByClientId("client-linking").get(0);
client.setFullScopeAllowed(true);
clientResource.update(client);
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
logoutAll();
} }
@Test @Test
public void testAccountLink() { public void testAccountLink() throws Exception {
RealmResource realm = adminClient.realms().realm(CHILD_IDP); RealmResource realm = adminClient.realms().realm(CHILD_IDP);
List<FederatedIdentityRepresentation> links = realm.users().get(childUserId).getFederatedIdentity(); List<FederatedIdentityRepresentation> links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty()); Assert.assertTrue(links.isEmpty());
@ -227,6 +378,7 @@ public class ClientInitiatedAccountLinkTest extends AbstractKeycloakTest {
.queryParam("provider", PARENT_IDP).build().toString(); .queryParam("provider", PARENT_IDP).build().toString();
driver.navigate().to(linkUrl); driver.navigate().to(linkUrl);
Assert.assertTrue(loginPage.isCurrent(CHILD_IDP)); Assert.assertTrue(loginPage.isCurrent(CHILD_IDP));
Assert.assertTrue(driver.getPageSource().contains(PARENT_IDP));
loginPage.login("child", "password"); loginPage.login("child", "password");
Assert.assertTrue(loginPage.isCurrent(PARENT_IDP)); Assert.assertTrue(loginPage.isCurrent(PARENT_IDP));
loginPage.login(PARENT_USERNAME, "password"); loginPage.login(PARENT_USERNAME, "password");
@ -237,9 +389,117 @@ public class ClientInitiatedAccountLinkTest extends AbstractKeycloakTest {
links = realm.users().get(childUserId).getFederatedIdentity(); links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertFalse(links.isEmpty()); Assert.assertFalse(links.isEmpty());
realm.users().get(childUserId).removeFederatedIdentity(PARENT_IDP);
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
logoutAll();
}
public void logoutAll() {
String logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder()).build(CHILD_IDP).toString();
driver.navigate().to(logoutUri);
logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder()).build(PARENT_IDP).toString();
driver.navigate().to(logoutUri);
}
@Test
public void testLinkOnlyProvider() throws Exception {
RealmResource realm = adminClient.realms().realm(CHILD_IDP);
IdentityProviderRepresentation rep = realm.identityProviders().get(PARENT_IDP).toRepresentation();
rep.setLinkOnly(true);
realm.identityProviders().get(PARENT_IDP).update(rep);
try {
List<FederatedIdentityRepresentation> links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
UriBuilder linkBuilder = UriBuilder.fromUri(appPage.getInjectedUrl().toString())
.path("link");
String linkUrl = linkBuilder.clone()
.queryParam("realm", CHILD_IDP)
.queryParam("provider", PARENT_IDP).build().toString();
driver.navigate().to(linkUrl);
Assert.assertTrue(loginPage.isCurrent(CHILD_IDP));
// should not be on login page. This is what we are testing
Assert.assertFalse(driver.getPageSource().contains(PARENT_IDP));
// now test that we can still link.
loginPage.login("child", "password");
Assert.assertTrue(loginPage.isCurrent(PARENT_IDP));
loginPage.login(PARENT_USERNAME, "password");
System.out.println("After linking: " + driver.getCurrentUrl());
System.out.println(driver.getPageSource());
Assert.assertTrue(driver.getCurrentUrl().startsWith(linkBuilder.toTemplate()));
Assert.assertTrue(driver.getPageSource().contains("Account Linked"));
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertFalse(links.isEmpty());
realm.users().get(childUserId).removeFederatedIdentity(PARENT_IDP);
links = realm.users().get(childUserId).getFederatedIdentity();
Assert.assertTrue(links.isEmpty());
logoutAll();
System.out.println("testing link-only attack");
driver.navigate().to(linkUrl);
Assert.assertTrue(loginPage.isCurrent(CHILD_IDP));
System.out.println("login page uri is: " + driver.getCurrentUrl());
// ok, now scrape the code from page
String pageSource = driver.getPageSource();
Pattern p = Pattern.compile("action=\"(.+)\"");
Matcher m = p.matcher(pageSource);
String action = null;
if (m.find()) {
action = m.group(1);
}
System.out.println("action: " + action);
p = Pattern.compile("code=(.+)&");
m = p.matcher(action);
String code = null;
if (m.find()) {
code = m.group(1);
}
System.out.println("code: " + code);
// now try and use the code to login to remote link-only idp
String uri = "/auth/realms/child/broker/parent-idp/login";
uri = UriBuilder.fromUri(AuthServerTestEnricher.getAuthServerContextRoot())
.path(uri)
.queryParam("code", code)
.build().toString();
System.out.println("hack uri: " + uri);
driver.navigate().to(uri);
Assert.assertTrue(driver.getPageSource().contains("Could not send authentication request to identity provider."));
} finally {
rep.setLinkOnly(false);
realm.identityProviders().get(PARENT_IDP).update(rep);
}
} }
} }

View file

@ -3,6 +3,7 @@
"resource" : "client-linking", "resource" : "client-linking",
"auth-server-url" : "http://localhost:8180/auth", "auth-server-url" : "http://localhost:8180/auth",
"ssl-required" : "external", "ssl-required" : "external",
"min-time-between-jwks-requests" : 0,
"credentials" : { "credentials" : {
"secret": "password" "secret": "password"
} }

View file

@ -57,7 +57,6 @@
<module>tomcat8</module> <module>tomcat8</module>
<module>jetty</module> <module>jetty</module>
<module>integration-arquillian</module> <module>integration-arquillian</module>
<module>stress</module>
</modules> </modules>
</project> </project>

View file

@ -51,6 +51,7 @@ role_manage-clients=Manage clients
role_manage-events=Manage events role_manage-events=Manage events
role_view-profile=View profile role_view-profile=View profile
role_manage-account=Manage account role_manage-account=Manage account
role_manage-account-links=Manage account links
role_read-token=Read token role_read-token=Read token
role_offline-access=Offline access role_offline-access=Offline access
role_uma_authorization=Obtain permissions role_uma_authorization=Obtain permissions

View file

@ -3,6 +3,7 @@ consoleTitle=Keycloak Admin Console
# Common messages # Common messages
enabled=Enabled enabled=Enabled
hidden=Hidden hidden=Hidden
link-only-column=Link only
name=Name name=Name
displayName=Display name displayName=Display name
displayNameHtml=HTML Display name displayNameHtml=HTML Display name
@ -467,6 +468,8 @@ off=Off
update-profile-on-first-login.tooltip=Define conditions under which a user has to update their profile during first-time login. update-profile-on-first-login.tooltip=Define conditions under which a user has to update their profile during first-time login.
trust-email=Trust Email trust-email=Trust Email
trust-email.tooltip=If enabled then email provided by this provider is not verified even if verification is enabled for the realm. trust-email.tooltip=If enabled then email provided by this provider is not verified even if verification is enabled for the realm.
link-only=Account Linking Only
link-only.tooltip=If true, users cannot log in through this provider. They can only link to this provider. This is useful if you don't want to allow login from the provider, but want to integrate with a provider
hide-on-login-page=Hide on Login Page hide-on-login-page=Hide on Login Page
hide-on-login-page.tooltip=If hidden, then login with this provider is possible only if requested explicitly, e.g. using the 'kc_idp_hint' parameter. hide-on-login-page.tooltip=If hidden, then login with this provider is possible only if requested explicitly, e.g. using the 'kc_idp_hint' parameter.
gui-order.tooltip=Number defining order of the provider in GUI (eg. on Login page). gui-order.tooltip=Number defining order of the provider in GUI (eg. on Login page).

View file

@ -64,6 +64,13 @@
</div> </div>
<kc-tooltip>{{:: 'trust-email.tooltip' | translate}}</kc-tooltip> <kc-tooltip>{{:: 'trust-email.tooltip' | translate}}</kc-tooltip>
</div> </div>
<div class="form-group">
<label class="col-md-2 control-label" for="linkOnly">{{:: 'link-only' | translate}}</label>
<div class="col-md-6">
<input ng-model="identityProvider.linkOnly" name="identityProvider.trustEmail" id="linkOnly" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
<kc-tooltip>{{:: 'linkOnly.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group"> <div class="form-group">
<label class="col-md-2 control-label" for="hideOnLoginPage">{{:: 'hide-on-login-page' | translate}}</label> <label class="col-md-2 control-label" for="hideOnLoginPage">{{:: 'hide-on-login-page' | translate}}</label>
<div class="col-md-6"> <div class="col-md-6">

View file

@ -61,6 +61,13 @@
</div> </div>
<kc-tooltip>{{:: 'trust-email.tooltip' | translate}}</kc-tooltip> <kc-tooltip>{{:: 'trust-email.tooltip' | translate}}</kc-tooltip>
</div> </div>
<div class="form-group">
<label class="col-md-2 control-label" for="linkOnly">{{:: 'link-only' | translate}}</label>
<div class="col-md-6">
<input ng-model="identityProvider.linkOnly" name="identityProvider.trustEmail" id="linkOnly" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
<kc-tooltip>{{:: 'linkOnly.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group"> <div class="form-group">
<label class="col-md-2 control-label" for="hideOnLoginPage">{{:: 'hide-on-login-page' | translate}}</label> <label class="col-md-2 control-label" for="hideOnLoginPage">{{:: 'hide-on-login-page' | translate}}</label>
<div class="col-md-6"> <div class="col-md-6">

View file

@ -78,6 +78,13 @@
</div> </div>
<kc-tooltip>{{:: 'trust-email.tooltip' | translate}}</kc-tooltip> <kc-tooltip>{{:: 'trust-email.tooltip' | translate}}</kc-tooltip>
</div> </div>
<div class="form-group">
<label class="col-md-2 control-label" for="linkOnly">{{:: 'link-only' | translate}}</label>
<div class="col-md-6">
<input ng-model="identityProvider.linkOnly" name="identityProvider.trustEmail" id="linkOnly" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
<kc-tooltip>{{:: 'linkOnly.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group"> <div class="form-group">
<label class="col-md-2 control-label" for="hideOnLoginPage">{{:: 'hide-on-login-page' | translate}}</label> <label class="col-md-2 control-label" for="hideOnLoginPage">{{:: 'hide-on-login-page' | translate}}</label>
<div class="col-md-6"> <div class="col-md-6">

View file

@ -48,6 +48,7 @@
<th>{{:: 'provider' | translate}}</th> <th>{{:: 'provider' | translate}}</th>
<th>{{:: 'enabled' | translate}}</th> <th>{{:: 'enabled' | translate}}</th>
<th>{{:: 'hidden' | translate}}</th> <th>{{:: 'hidden' | translate}}</th>
<th>{{:: 'link-only-column' | translate}}</th>
<th width="15%">{{:: 'gui-order' | translate}}</th> <th width="15%">{{:: 'gui-order' | translate}}</th>
<th colspan="2">{{:: 'actions' | translate}}</th> <th colspan="2">{{:: 'actions' | translate}}</th>
</tr> </tr>
@ -64,6 +65,7 @@
<td>{{identityProvider.providerId}}</td> <td>{{identityProvider.providerId}}</td>
<td translate="{{identityProvider.enabled}}"></td> <td translate="{{identityProvider.enabled}}"></td>
<td translate="{{identityProvider.config.hideOnLoginPage == 'true'}}"></td> <td translate="{{identityProvider.config.hideOnLoginPage == 'true'}}"></td>
<td translate="{{identityProvider.linkOnly}}"></td>
<td>{{identityProvider.config.guiOrder}}</td> <td>{{identityProvider.config.guiOrder}}</td>
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">{{:: 'edit' | translate}}</td> <td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">{{:: 'edit' | translate}}</td>
<td class="kc-action-cell" data-ng-show="access.manageIdentityProviders" data-ng-click="removeIdentityProvider(identityProvider)">{{:: 'delete' | translate}}</td> <td class="kc-action-cell" data-ng-show="access.manageIdentityProviders" data-ng-click="removeIdentityProvider(identityProvider)">{{:: 'delete' | translate}}</td>

View file

@ -109,6 +109,7 @@ role_manage-clients=Manage clients
role_manage-events=Manage events role_manage-events=Manage events
role_view-profile=View profile role_view-profile=View profile
role_manage-account=Manage account role_manage-account=Manage account
role_manage-account-links=Manage account links
role_read-token=Read token role_read-token=Read token
role_offline-access=Offline access role_offline-access=Offline access
client_account=Account client_account=Account