Merge pull request #1162 from patriot1burke/master

saml broker role mapper
This commit is contained in:
Bill Burke 2015-04-20 12:31:31 -04:00
commit 22f26a6221
3 changed files with 169 additions and 0 deletions

View file

@ -0,0 +1,167 @@
package org.keycloak.broker.saml.mappers;
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.broker.saml.SAMLEndpoint;
import org.keycloak.broker.saml.SAMLIdentityProviderFactory;
import org.keycloak.dom.saml.v2.assertion.AssertionType;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.dom.saml.v2.assertion.AttributeType;
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 org.keycloak.representations.JsonWebToken;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RoleMapper 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";
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty();
property.setName(ATTRIBUTE_NAME);
property.setLabel("Attribute Name");
property.setHelpText("Name of attribute to search for in assertion. You can leave this blank and specify a friendly name instead.");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
property = new ProviderConfigProperty();
property.setName(ATTRIBUTE_FRIENDLY_NAME);
property.setLabel("Attribute Name");
property.setHelpText("Friendly name of attribute to search for in assertion. You can leave this blank and specify a name instead.");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
property = new ProviderConfigProperty();
property.setName(ATTRIBUTE_VALUE);
property.setLabel("Attribute Value");
property.setHelpText("Value the attribute must have. If the attribute is a list, then the value must be contained in the list.");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(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 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;
}
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public String[] getCompatibleProviders() {
return COMPATIBLE_PROVIDERS;
}
@Override
public String getDisplayCategory() {
return "Role Mapper";
}
@Override
public String getDisplayType() {
return "Role Mapper";
}
@Override
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String roleName = mapperModel.getConfig().get(ROLE);
if (isAttributePresent(mapperModel, context)) {
RoleModel role = 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;
String friendly = mapperModel.getConfig().get(ATTRIBUTE_FRIENDLY_NAME);
if (friendly != null && friendly.trim().equals("")) friendly = null;
String desiredValue = mapperModel.getConfig().get(ATTRIBUTE_VALUE);
AssertionType assertion = (AssertionType)context.getContextData().get(SAMLEndpoint.SAML_ASSERTION);
for (AttributeStatementType statement : assertion.getAttributeStatements()) {
for (AttributeStatementType.ASTChoiceType choice : statement.getAttributes()) {
AttributeType attr = choice.getAttribute();
if (name != null && !name.equals(attr.getName())) continue;
if (friendly != null && !name.equals(attr.getFriendlyName())) continue;
for (Object val : attr.getAttributeValue()) {
if (val.equals(desiredValue)) return true;
}
}
}
return false;
}
@Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String roleName = mapperModel.getConfig().get(ROLE);
if (!isAttributePresent(mapperModel, context)) {
RoleModel role = getRoleFromString(realm, roleName);
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
user.deleteRoleMapping(role);
}
}
@Override
public String getHelpText() {
return "If a claim exists, grant the user the specified realm or application role.";
}
}

View file

@ -0,0 +1 @@
org.keycloak.broker.saml.mappers.RoleMapper

View file

@ -163,6 +163,7 @@
</fieldset>
<div class="pull-right form-actions">
<a data-ng-show="!newIdentityProvider" class="btn btn-lg btn-primary" href="#/realms/{{realm.realm}}/identity-provider-mappers/{{identityProvider.alias}}/mappers">Mappers</a>
<a class="btn btn-lg btn-primary" href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}/export" data-ng-show="!importFile && !newIdentityProvider">Export</a>
<button kc-save data-ng-show="changed">Save</button>
<button type="submit" data-ng-click="cancel()" data-ng-show="changed" class="btn btn-lg btn-default">Cancel</button>