Merge pull request #1182 from patriot1burke/master

more broker mappers
This commit is contained in:
Bill Burke 2015-04-26 11:20:15 -04:00
commit f17863041a
13 changed files with 381 additions and 185 deletions

View file

@ -0,0 +1,110 @@
package org.keycloak.broker.provider;
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.models.ClientModel;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderConfigProperty;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class HardcodedRoleMapper extends AbstractIdentityProviderMapper {
public static final String ROLE = "role";
protected static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty();
property.setName(ROLE);
property.setLabel("Role");
property.setHelpText("Role to grant to user. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
}
public static String[] parseRole(String role) {
int scopeIndex = role.indexOf('.');
if (scopeIndex > -1) {
String appName = role.substring(0, scopeIndex);
role = role.substring(scopeIndex + 1);
String[] rtn = {appName, role};
return rtn;
} else {
String[] rtn = {null, role};
return rtn;
}
}
public static RoleModel getRoleFromString(RealmModel realm, String roleName) {
String[] parsedRole = parseRole(roleName);
RoleModel role = null;
if (parsedRole[0] == null) {
role = realm.getRole(parsedRole[1]);
} else {
ClientModel client = realm.getClientByClientId(parsedRole[0]);
role = client.getRole(parsedRole[1]);
}
return role;
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getDisplayCategory() {
return "Role Importer";
}
@Override
public String getDisplayType() {
return "Hardcoded Role";
}
public static final String[] COMPATIBLE_PROVIDERS = {ANY_PROVIDER};
public static final String PROVIDER_ID = "oidc-hardcoded-role-idp-mapper";
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public String[] getCompatibleProviders() {
return COMPATIBLE_PROVIDERS;
}
@Override
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String roleName = mapperModel.getConfig().get(ROLE);
RoleModel role = getRoleFromString(realm, roleName);
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
user.grantRole(role);
}
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
}
@Override
public String getHelpText() {
return "When user is imported from provider, hardcode a role mapping for it.";
}
}

View file

@ -13,6 +13,8 @@ import org.keycloak.provider.ProviderFactory;
* @version $Revision: 1 $
*/
public interface IdentityProviderMapper extends Provider, ProviderFactory<IdentityProviderMapper>,ConfiguredProvider {
public static final String ANY_PROVIDER = "*";
String[] getCompatibleProviders();
String getDisplayCategory();
String getDisplayType();

View file

@ -0,0 +1 @@
org.keycloak.broker.provider.HardcodedRoleMapper

View file

@ -3,8 +3,8 @@ package org.keycloak.broker.oidc.mappers;
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProviderFactory;
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.HardcodedRoleMapper;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.models.ClientModel;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@ -19,14 +19,12 @@ import java.util.List;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RoleMapper extends AbstractClaimMapper {
public class ClaimToRoleMapper extends AbstractClaimMapper {
public static final String[] COMPATIBLE_PROVIDERS = {KeycloakOIDCIdentityProviderFactory.PROVIDER_ID, OIDCIdentityProviderFactory.PROVIDER_ID};
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
public static final String ROLE = "role";
static {
ProviderConfigProperty property;
ProviderConfigProperty property1;
@ -43,29 +41,15 @@ public class RoleMapper extends AbstractClaimMapper {
property1.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property1);
property = new ProviderConfigProperty();
property.setName(ROLE);
property.setName(HardcodedRoleMapper.ROLE);
property.setLabel("Role");
property.setHelpText("Role to grant to user. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
property.setHelpText("Role to grant to user if claim is present. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
}
public static final String PROVIDER_ID = "oidc-role-idp-mapper";
public static String[] parseRole(String role) {
int scopeIndex = role.indexOf('.');
if (scopeIndex > -1) {
String appName = role.substring(0, scopeIndex);
role = role.substring(scopeIndex + 1);
String[] rtn = {appName, role};
return rtn;
} else {
String[] rtn = {null, role};
return rtn;
}
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
@ -89,36 +73,24 @@ public class RoleMapper extends AbstractClaimMapper {
@Override
public String getDisplayType() {
return "Role Importer";
return "Claim to Role";
}
@Override
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String roleName = mapperModel.getConfig().get(ROLE);
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
if (hasClaimValue(mapperModel, context)) {
RoleModel role = getRoleFromString(realm, roleName);
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
user.grantRole(role);
}
}
protected RoleModel getRoleFromString(RealmModel realm, String roleName) {
String[] parsedRole = parseRole(roleName);
RoleModel role = null;
if (parsedRole[0] == null) {
role = realm.getRole(parsedRole[1]);
} else {
ClientModel client = realm.getClientByClientId(parsedRole[0]);
role = client.getRole(parsedRole[1]);
}
return role;
}
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String roleName = mapperModel.getConfig().get(ROLE);
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
if (!hasClaimValue(mapperModel, context)) {
RoleModel role = getRoleFromString(realm, roleName);
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
user.deleteRoleMapping(role);
}

View file

@ -0,0 +1,118 @@
package org.keycloak.broker.oidc.mappers;
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProvider;
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProviderFactory;
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.HardcodedRoleMapper;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.JsonWebToken;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ExternalKeycloakRoleToRoleMapper extends AbstractClaimMapper {
public static final String[] COMPATIBLE_PROVIDERS = {KeycloakOIDCIdentityProviderFactory.PROVIDER_ID};
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
private static final String EXTERNAL_ROLE = "external.role";
static {
ProviderConfigProperty property;
ProviderConfigProperty property1;
property1 = new ProviderConfigProperty();
property1.setName(EXTERNAL_ROLE);
property1.setLabel("External role");
property1.setHelpText("External role to check for. To reference an application role the syntax is appname.approle, i.e. myapp.myrole.");
property1.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property1);
property = new ProviderConfigProperty();
property.setName(HardcodedRoleMapper.ROLE);
property.setLabel("Role");
property.setHelpText("Role to grant to user if external role is present. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
}
public static final String PROVIDER_ID = "keycloak-oidc-role-to-role-idp-mapper";
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
}
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public String[] getCompatibleProviders() {
return COMPATIBLE_PROVIDERS;
}
@Override
public String getDisplayCategory() {
return "Role Importer";
}
@Override
public String getDisplayType() {
return "External Role to Role";
}
@Override
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
RoleModel role = hasRole(realm, mapperModel, context);
if (role != null) {
user.grantRole(role);
}
}
private RoleModel hasRole(RealmModel realm,IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
JsonWebToken token = (JsonWebToken)context.getContextData().get(KeycloakOIDCIdentityProvider.VALIDATED_ACCESS_TOKEN);
//if (token == null) return;
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
String[] parseRole = HardcodedRoleMapper.parseRole(mapperModel.getConfig().get(EXTERNAL_ROLE));
String externalRoleName = parseRole[1];
String claimName = null;
if (parseRole[0] == null) {
claimName = "realm_access.roles";
} else {
claimName = "resource_access." + parseRole[0] + ".roles";
}
Object claim = getClaimValue(token, claimName);
if (valueEquals(externalRoleName, claim)) {
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
return role;
}
return null;
}
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
RoleModel role = hasRole(realm, mapperModel, context);
if (role == null) {
user.deleteRoleMapping(role);
}
}
@Override
public String getHelpText() {
return "Looks for an external role in a keycloak access token. If external role exists, grant the user the specified realm or application role.";
}
}

View file

@ -1,2 +1,3 @@
org.keycloak.broker.oidc.mappers.RoleMapper
org.keycloak.broker.oidc.mappers.ClaimToRoleMapper
org.keycloak.broker.oidc.mappers.ExternalKeycloakRoleToRoleMapper
org.keycloak.broker.oidc.mappers.UserAttributeMapper

View file

@ -2,6 +2,7 @@ package org.keycloak.broker.saml.mappers;
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.HardcodedRoleMapper;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.broker.saml.SAMLEndpoint;
import org.keycloak.broker.saml.SAMLIdentityProviderFactory;
@ -25,13 +26,12 @@ import java.util.Map;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RoleMapper extends AbstractIdentityProviderMapper {
public class AttributeToRoleMapper extends AbstractIdentityProviderMapper {
public static final String[] COMPATIBLE_PROVIDERS = {SAMLIdentityProviderFactory.PROVIDER_ID};
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
public static final String ROLE = "role";
public static final String ATTRIBUTE_NAME = "attribute.name";
public static final String ATTRIBUTE_FRIENDLY_NAME = "attribute.friendly.name";
public static final String ATTRIBUTE_VALUE = "attribute.value";
@ -57,7 +57,7 @@ public class RoleMapper extends AbstractIdentityProviderMapper {
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
property = new ProviderConfigProperty();
property.setName(ROLE);
property.setName(HardcodedRoleMapper.ROLE);
property.setLabel("Role");
property.setHelpText("Role to grant to user. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
property.setType(ProviderConfigProperty.STRING_TYPE);
@ -66,20 +66,6 @@ public class RoleMapper extends AbstractIdentityProviderMapper {
public static final String PROVIDER_ID = "saml-role-idp-mapper";
public static String[] parseRole(String role) {
int scopeIndex = role.indexOf('.');
if (scopeIndex > -1) {
String appName = role.substring(0, scopeIndex);
role = role.substring(scopeIndex + 1);
String[] rtn = {appName, role};
return rtn;
} else {
String[] rtn = {null, role};
return rtn;
}
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
@ -102,31 +88,19 @@ public class RoleMapper extends AbstractIdentityProviderMapper {
@Override
public String getDisplayType() {
return "Role Mapper";
return "SAML Attribute to Role";
}
@Override
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String roleName = mapperModel.getConfig().get(ROLE);
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
if (isAttributePresent(mapperModel, context)) {
RoleModel role = getRoleFromString(realm, roleName);
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
user.grantRole(role);
}
}
protected RoleModel getRoleFromString(RealmModel realm, String roleName) {
String[] parsedRole = parseRole(roleName);
RoleModel role = null;
if (parsedRole[0] == null) {
role = realm.getRole(parsedRole[1]);
} else {
ClientModel client = realm.getClientByClientId(parsedRole[0]);
role = client.getRole(parsedRole[1]);
}
return role;
}
protected boolean isAttributePresent(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String name = mapperModel.getConfig().get(ATTRIBUTE_NAME);
if (name != null && name.trim().equals("")) name = null;
@ -149,9 +123,9 @@ public class RoleMapper extends AbstractIdentityProviderMapper {
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String roleName = mapperModel.getConfig().get(ROLE);
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
if (!isAttributePresent(mapperModel, context)) {
RoleModel role = getRoleFromString(realm, roleName);
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
user.deleteRoleMapping(role);
}

View file

@ -1,2 +1,2 @@
org.keycloak.broker.saml.mappers.RoleMapper
org.keycloak.broker.saml.mappers.AttributeToRoleMapper
org.keycloak.broker.saml.mappers.UserAttributeMapper

View file

@ -72,7 +72,9 @@
<para>
When using Keycloak as an identity broker, users are not forced to provide their credentials in order to
authenticate in a specific realm. Instead of that, they are presented with a list of identity providers from
where they can pick one and authenticate. The following diagram demonstrates the steps involved when using
where they can pick one and authenticate. You can also configure a hard-coded default broker. In this case
the user will not be given a choice, but instead be redirected directly the the parent broker.
The following diagram demonstrates the steps involved when using
Keycloak to broker an external identity provider:
</para>
@ -272,6 +274,25 @@
be used by any other means.
</entry>
</row>
<row>
<entry>
<literal>Store Tokens</literal>
</entry>
<entry>
Any external tokens provided by the parent IDP will be stored.
This options is useful if you are using social authentication and need to access the token in order to invoke the
API of a social provider on behalf of the user.
</entry>
</row>
<row>
<entry>
<literal>Stored Tokens Readable</literal>
</entry>
<entry>
Automatically assigns a <literal>broker.READ_TOKEN</literal> role that allows the user
to access any stored external tokens via the broker service.
</entry>
</row>
<row>
<entry>
<literal>Update Profile on First Login</literal>
@ -293,17 +314,6 @@
You can put number into this field, providers with lower numbers are shown first.
</entry>
</row>
<!--<row>-->
<!--<entry>-->
<!--<literal>Store Tokens</literal>-->
<!--</entry>-->
<!--<entry>-->
<!--Allows you to store tokens issued by an identity provider during the authentication of a specific user.-->
<!--Tokens are stored and can be retrieved later.-->
<!--This options is useful if you are using social authentication and need to access the token in order to invoke the-->
<!--API of a social provider on behalf of the user.-->
<!--</entry>-->
<!--</row>-->
</tbody>
</tgroup>
</table>
@ -1003,6 +1013,14 @@
</tbody>
</tgroup>
</table>
<para>
You can also import all this configuration data by providing a URL or XML file that points to the entity descriptor of the external
SAML IDP you want to connect to.
</para>
<para>
Once you create a SAML provider, there is an <literal>EXPORT</literal> button that appears when viewing that provider.
Clicking this button will export a SAML entity descriptor which you can use to
</para>
</section>
<section>
@ -1104,63 +1122,45 @@
</tbody>
</tgroup>
</table>
<para>
You can also import all this configuration data by providing a URL or file that points to OpenID Provider Metadata (see OIDC Discovery specification)
</para>
</section>
<!--<section>-->
<!--<title>Retrieving Tokens from Identity Providers</title>-->
<!--<para>-->
<!--Keycloak allows you to store tokens and responses from identity providers during the authentication process.-->
<!--For that, you can use the <literal>Store Token</literal> configuration option, as mentioned before.-->
<!--</para>-->
<!--<para>-->
<!--It also allows you to retrieve these tokens and responses once the user is authenticated in order to use their-->
<!--information or use them to invoke external resources protected by these tokens.-->
<!--The latter case is usually related with social providers,-->
<!--where you usually need to use their tokens to invoke methods on their APIs.-->
<!--</para>-->
<!--<para>-->
<!--To retrieve a token for a particular identity provider you need to send a request as follows:-->
<!--</para>-->
<!--<programlisting language="JAVA"><![CDATA[GET /auth/realms/{realm}/broker/{provider_alias}/token HTTP/1.1-->
<!--Host: localhost:8080-->
<!--Authorization: Bearer {keycloak_access_token}]]></programlisting>-->
<!--<para>-->
<!--In this case, given that you are accessing an protected service in Keycloak, you need to send the access token-->
<!--issued by Keycloak during the user authentication.-->
<!--</para>-->
<!--<para>-->
<!--By default, the Keycloak access token issued for the application can't be automatically used for retrieve thirdparty token. You will-->
<!--need to enable this in admin console first:-->
<!--<orderedlist>-->
<!--<listitem>-->
<!--<para>-->
<!--Click 'Applications' on the left side menu.-->
<!--</para>-->
<!--</listitem>-->
<!--<listitem>-->
<!--<para>-->
<!--Select an application from the list.-->
<!--</para>-->
<!--</listitem>-->
<!--<listitem>-->
<!--<para>-->
<!--Click the 'Identity Provider' tab.-->
<!--</para>-->
<!--</listitem>-->
<!--<listitem>-->
<!--<para>-->
<!--From this page you can configure if an application is allowed to retrieve tokens from an specific identity provider. For that,-->
<!--just click on the <emphasis>Can Retrieve Token</emphasis> button.-->
<!--</para>-->
<!--</listitem>-->
<!--</orderedlist>-->
<!--</para>-->
<!--<note>-->
<!--<para>-->
<!--If your application is not at the same origin as the authentication server, make sure you have properly configured CORS.-->
<!--</para>-->
<!--</note>-->
<!--</section>-->
<section>
<title>Retrieving Tokens from Identity Providers</title>
<para>
Keycloak allows you to store tokens and responses from identity providers during the authentication process.
For that, you can use the <literal>Store Token</literal> configuration option, as mentioned before.
</para>
<para>
It also allows you to retrieve these tokens and responses once the user is authenticated in order to use their
information or use them to invoke external resources protected by these tokens.
The latter case is usually related with social providers,
where you usually need to use their tokens to invoke methods on their APIs.
</para>
<para>
To retrieve a token for a particular identity provider you need to send a request as follows:
</para>
<programlisting language="JAVA"><![CDATA[GET /auth/realms/{realm}/broker/{provider_alias}/token HTTP/1.1
Host: localhost:8080
Authorization: Bearer {keycloak_access_token}]]></programlisting>
<para>
In this case, given that you are accessing an protected service in Keycloak, you need to send the access token
issued by Keycloak during the user authentication.
</para>
<para>
By default, the Keycloak access token issued for the application can't be automatically used for retrieve thirdparty token.
A user will have to have the <literal>broker.READ_TOKEN</literal> role. The client will also have to have that role
in its scope. In the broker configuration page you can automatically assign this role to newly imported users by
turning on the <literal>Stored Tokens Readable</literal> switch.
</para>
<note>
<para>
If your application is not at the same origin as the authentication server, make sure you have properly configured CORS.
</para>
</note>
</section>
<section>
<title>Automatically Select and Identity Provider</title>
@ -1188,6 +1188,19 @@ keycloak.createLoginUrl({
});]]></programlisting>
</section>
<section>
<title>Mapping/Importing SAML and OIDC Metadata</title>
<para>
You can import SAML assertion data, OpenID Connect ID Token claims, and Keycloak access token claims
into new users that are imported from a brokered IDP. After you configure a broker, you'll see a <literal>Mappers</literal>
button appear. Click on that and you'll get to the list of mappers that are assigned to this broker. There is a
<literal>Create</literal> button on this page. Clicking on this create button allows you to create a broker mapper.
Broker mappers can import SAML attributes or OIDC ID/Access token claims into user attributes. You can assign
a role mapping to a user if a claim or external role exists. There's a bunch of options here so just mouse over
the tool tips to see what each mapper can do for you.
</para>
</section>
<section>
<title>Examples</title>
<para>

View file

@ -283,9 +283,10 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
} catch (Exception e) {
return redirectToLoginPage(e, clientCode);
}
} else {
updateFederatedIdentity(context, federatedUser);
}
updateFederatedIdentity(context, federatedUser);
UserSessionModel userSession = this.session.sessions()
.createUserSession(this.realmModel, federatedUser, federatedUser.getUsername(), this.clientConnection.getRemoteAddr(), "broker", false, context.getBrokerSessionId(), context.getBrokerUserId());
@ -335,26 +336,26 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
return Response.status(302).location(UriBuilder.fromUri(clientSession.getRedirectUri()).build()).build();
}
private void updateFederatedIdentity(BrokeredIdentityContext updatedIdentity, UserModel federatedUser) {
FederatedIdentityModel federatedIdentityModel = this.session.users().getFederatedIdentity(federatedUser, updatedIdentity.getIdpConfig().getAlias(), this.realmModel);
private void updateFederatedIdentity(BrokeredIdentityContext context, UserModel federatedUser) {
FederatedIdentityModel federatedIdentityModel = this.session.users().getFederatedIdentity(federatedUser, context.getIdpConfig().getAlias(), this.realmModel);
// Skip DB write if tokens are null or equal
if (!ObjectUtil.isEqualOrNull(updatedIdentity.getToken(), federatedIdentityModel.getToken())) {
federatedIdentityModel.setToken(updatedIdentity.getToken());
if (context.getIdpConfig().isStoreToken() && !ObjectUtil.isEqualOrNull(context.getToken(), federatedIdentityModel.getToken())) {
federatedIdentityModel.setToken(context.getToken());
this.session.users().updateFederatedIdentity(this.realmModel, federatedUser, federatedIdentityModel);
if (isDebugEnabled()) {
LOGGER.debugf("Identity [%s] update with response from identity provider [%s].", federatedUser, updatedIdentity.getIdpConfig().getAlias());
LOGGER.debugf("Identity [%s] update with response from identity provider [%s].", federatedUser, context.getIdpConfig().getAlias());
}
}
updatedIdentity.getIdp().updateBrokeredUser(session, realmModel, federatedUser, updatedIdentity);
Set<IdentityProviderMapperModel> mappers = realmModel.getIdentityProviderMappersByAlias(updatedIdentity.getIdpConfig().getAlias());
context.getIdp().updateBrokeredUser(session, realmModel, federatedUser, context);
Set<IdentityProviderMapperModel> mappers = realmModel.getIdentityProviderMappersByAlias(context.getIdpConfig().getAlias());
if (mappers != null) {
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
for (IdentityProviderMapperModel mapper : mappers) {
IdentityProviderMapper target = (IdentityProviderMapper)sessionFactory.getProviderFactory(IdentityProviderMapper.class, mapper.getIdentityProviderMapper());
target.updateBrokeredUser(session, realmModel, federatedUser, mapper, updatedIdentity);
target.updateBrokeredUser(session, realmModel, federatedUser, mapper, context);
}
}
@ -484,14 +485,14 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
throw new IdentityBrokerException("Configuration for identity provider [" + providerId + "] not found.");
}
private UserModel createUser(BrokeredIdentityContext updatedIdentity) {
FederatedIdentityModel federatedIdentityModel = new FederatedIdentityModel(updatedIdentity.getIdpConfig().getAlias(), updatedIdentity.getId(),
updatedIdentity.getUsername(), updatedIdentity.getToken());
private UserModel createUser(BrokeredIdentityContext context) {
FederatedIdentityModel federatedIdentityModel = new FederatedIdentityModel(context.getIdpConfig().getAlias(), context.getId(),
context.getUsername(), context.getToken());
// Check if no user already exists with this username or email
UserModel existingUser = null;
if (updatedIdentity.getEmail() != null) {
existingUser = this.session.users().getUserByEmail(updatedIdentity.getEmail(), this.realmModel);
if (context.getEmail() != null) {
existingUser = this.session.users().getUserByEmail(context.getEmail(), this.realmModel);
}
if (existingUser != null) {
@ -499,13 +500,13 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
throw new IdentityBrokerException(Messages.FEDERATED_IDENTITY_EMAIL_EXISTS);
}
String username = updatedIdentity.getUsername();
if (this.realmModel.isRegistrationEmailAsUsername() && !Validation.isEmpty(updatedIdentity.getEmail())) {
username = updatedIdentity.getEmail();
String username = context.getUsername();
if (this.realmModel.isRegistrationEmailAsUsername() && !Validation.isEmpty(context.getEmail())) {
username = context.getEmail();
} else if (username == null) {
username = updatedIdentity.getIdpConfig().getAlias() + "." + updatedIdentity.getId();
username = context.getIdpConfig().getAlias() + "." + context.getId();
} else {
username = updatedIdentity.getIdpConfig().getAlias() + "." + updatedIdentity.getUsername();
username = context.getIdpConfig().getAlias() + "." + context.getUsername();
}
if (username != null) {
username = username.trim();
@ -529,33 +530,36 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
}
federatedUser.setEnabled(true);
federatedUser.setEmail(updatedIdentity.getEmail());
federatedUser.setFirstName(updatedIdentity.getFirstName());
federatedUser.setLastName(updatedIdentity.getLastName());
federatedUser.setEmail(context.getEmail());
federatedUser.setFirstName(context.getFirstName());
federatedUser.setLastName(context.getLastName());
if (updatedIdentity.getIdpConfig().isAddReadTokenRoleOnCreate()) {
if (context.getIdpConfig().isAddReadTokenRoleOnCreate()) {
RoleModel readTokenRole = realmModel.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(READ_TOKEN_ROLE);
federatedUser.grantRole(readTokenRole);
}
if (context.getIdpConfig().isStoreToken()) {
federatedIdentityModel.setToken(context.getToken());
}
this.session.users().addFederatedIdentity(this.realmModel, federatedUser, federatedIdentityModel);
updatedIdentity.getIdp().importNewUser(session, realmModel, federatedUser, updatedIdentity);
Set<IdentityProviderMapperModel> mappers = realmModel.getIdentityProviderMappersByAlias(updatedIdentity.getIdpConfig().getAlias());
context.getIdp().importNewUser(session, realmModel, federatedUser, context);
Set<IdentityProviderMapperModel> mappers = realmModel.getIdentityProviderMappersByAlias(context.getIdpConfig().getAlias());
if (mappers != null) {
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
for (IdentityProviderMapperModel mapper : mappers) {
IdentityProviderMapper target = (IdentityProviderMapper)sessionFactory.getProviderFactory(IdentityProviderMapper.class, mapper.getIdentityProviderMapper());
target.importNewUser(session, realmModel, federatedUser, mapper, updatedIdentity);
target.importNewUser(session, realmModel, federatedUser, mapper, context);
}
}
this.event.clone().user(federatedUser).event(EventType.REGISTER)
.detail(Details.IDENTITY_PROVIDER, federatedIdentityModel.getIdentityProvider())
.detail(Details.IDENTITY_PROVIDER_USERNAME, updatedIdentity.getUsername())
.detail(Details.IDENTITY_PROVIDER_USERNAME, context.getUsername())
.removeDetail("auth_method")
.success();

View file

@ -181,7 +181,7 @@ public class IdentityProviderResource {
for (ProviderFactory factory : factories) {
IdentityProviderMapper mapper = (IdentityProviderMapper)factory;
for (String type : mapper.getCompatibleProviders()) {
if (type.equals(identityProviderModel.getProviderId())) {
if (IdentityProviderMapper.ANY_PROVIDER.equals(type) || type.equals(identityProviderModel.getProviderId())) {
IdentityProviderMapperTypeRepresentation rep = new IdentityProviderMapperTypeRepresentation();
rep.setId(mapper.getId());
rep.setCategory(mapper.getDisplayCategory());
@ -198,7 +198,7 @@ public class IdentityProviderResource {
rep.getProperties().add(propRep);
}
types.put(rep.getId(), rep);
break;
}
}
}

View file

@ -13,11 +13,26 @@
<name>Keycloak Tomcat 7 Integration TestSuite</name>
<properties>
<!--<tomcat.version>8.0.14</tomcat.version>-->
<tomcat.version>7.0.54</tomcat.version>
<tomcat.version>7.0.59</tomcat.version>
</properties>
<description />
<dependencies>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>7.0.59</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-util</artifactId>
<version>7.0.59</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>7.0.59</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-dependencies-server-all</artifactId>
@ -199,21 +214,6 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-util</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
</dependency>
</dependencies>
<build>

View file

@ -22,6 +22,7 @@
package org.keycloak.testsuite;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.util.http.mapper.Mapper;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;