/* * 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.broker.saml.mappers; import org.keycloak.broker.provider.AbstractIdentityProviderMapper; import org.keycloak.broker.provider.BrokeredIdentityContext; 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.dom.saml.v2.assertion.NameIDType; import org.keycloak.dom.saml.v2.assertion.SubjectType; import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.provider.ProviderConfigProperty; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @version $Revision: 1 $ */ public class UsernameTemplateMapper extends AbstractIdentityProviderMapper { public static final String[] COMPATIBLE_PROVIDERS = {SAMLIdentityProviderFactory.PROVIDER_ID}; private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>(); public static final String TEMPLATE = "template"; static { ProviderConfigProperty property; property = new ProviderConfigProperty(); property.setName(TEMPLATE); property.setLabel("Template"); property.setHelpText("Template to use to format the username to import. Substitutions are enclosed in ${}. For example: '${ALIAS}.${NAMEID}'. ALIAS is the provider alias. NAMEID is that SAML name id assertion. ATTRIBUTE.<NAME> references a SAML attribute where name is the attribute name or friendly name."); property.setType(ProviderConfigProperty.STRING_TYPE); property.setDefaultValue("${ALIAS}.${NAMEID}"); configProperties.add(property); } public static final String PROVIDER_ID = "saml-username-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 "Preprocessor"; } @Override public String getDisplayType() { return "Username Template Importer"; } @Override public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) { } static Pattern substitution = Pattern.compile("\\$\\{([^}]+)\\}"); @Override public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) { AssertionType assertion = (AssertionType)context.getContextData().get(SAMLEndpoint.SAML_ASSERTION); String template = mapperModel.getConfig().get(TEMPLATE); Matcher m = substitution.matcher(template); StringBuffer sb = new StringBuffer(); while (m.find()) { String variable = m.group(1); if (variable.equals("ALIAS")) { m.appendReplacement(sb, context.getIdpConfig().getAlias()); } else if (variable.equals("UUID")) { m.appendReplacement(sb, KeycloakModelUtils.generateId()); } else if (variable.equals("NAMEID")) { SubjectType subject = assertion.getSubject(); SubjectType.STSubType subType = subject.getSubType(); NameIDType subjectNameID = (NameIDType) subType.getBaseID(); m.appendReplacement(sb, subjectNameID.getValue()); } else if (variable.startsWith("ATTRIBUTE.")) { String name = variable.substring("ATTRIBUTE.".length()); String value = ""; for (AttributeStatementType statement : assertion.getAttributeStatements()) { for (AttributeStatementType.ASTChoiceType choice : statement.getAttributes()) { AttributeType attr = choice.getAttribute(); if (name.equals(attr.getName()) || name.equals(attr.getFriendlyName())) { List<Object> attributeValue = attr.getAttributeValue(); if (attributeValue != null && !attributeValue.isEmpty()) { value = attributeValue.get(0).toString(); } break; } } } m.appendReplacement(sb, value); } else { m.appendReplacement(sb, m.group(1)); } } m.appendTail(sb); context.setModelUsername(sb.toString()); } @Override public String getHelpText() { return "Format the username to import."; } }