keycloak-uncached

more broker mappers

4/26/2015 11:31:53 AM

Details

diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/HardcodedRoleMapper.java b/broker/core/src/main/java/org/keycloak/broker/provider/HardcodedRoleMapper.java
new file mode 100755
index 0000000..31684a0
--- /dev/null
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/HardcodedRoleMapper.java
@@ -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.";
+    }
+}
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java
index 990c6de..4017499 100755
--- a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java
@@ -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();
diff --git a/broker/core/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper b/broker/core/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper
new file mode 100755
index 0000000..a3bf053
--- /dev/null
+++ b/broker/core/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper
@@ -0,0 +1 @@
+org.keycloak.broker.provider.HardcodedRoleMapper
\ No newline at end of file
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/ExternalKeycloakRoleToRoleMapper.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/ExternalKeycloakRoleToRoleMapper.java
new file mode 100755
index 0000000..f5bf539
--- /dev/null
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/ExternalKeycloakRoleToRoleMapper.java
@@ -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.";
+    }
+
+}
diff --git a/broker/oidc/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper b/broker/oidc/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper
index c2245b6..de86af2 100755
--- a/broker/oidc/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper
+++ b/broker/oidc/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper
@@ -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
\ No newline at end of file
diff --git a/broker/saml/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper b/broker/saml/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper
index 5051d1d..9de7273 100755
--- a/broker/saml/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper
+++ b/broker/saml/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper
@@ -1,2 +1,2 @@
-org.keycloak.broker.saml.mappers.RoleMapper
+org.keycloak.broker.saml.mappers.AttributeToRoleMapper
 org.keycloak.broker.saml.mappers.UserAttributeMapper
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
index 114aa06..539c410 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
@@ -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;
                 }
             }
         }
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
index e51c209..c10ef1e 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -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();
 
diff --git a/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/Tomcat7Test.java b/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/Tomcat7Test.java
index 7a38655..3f20d16 100755
--- a/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/Tomcat7Test.java
+++ b/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/Tomcat7Test.java
@@ -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;