/* * 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.protocol.oidc; import org.jboss.logging.Logger; import org.keycloak.common.constants.KerberosConstants; import org.keycloak.common.util.UriUtils; import org.keycloak.events.EventBuilder; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientTemplateModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RealmModel; import org.keycloak.protocol.AbstractLoginProtocolFactory; import org.keycloak.protocol.LoginProtocol; import org.keycloak.protocol.oidc.mappers.AddressMapper; import org.keycloak.protocol.oidc.mappers.FullNameMapper; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.protocol.oidc.mappers.UserAttributeMapper; import org.keycloak.protocol.oidc.mappers.UserPropertyMapper; import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientTemplateRepresentation; import org.keycloak.services.ServicesLogger; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @version $Revision: 1 $ */ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory { private static final Logger logger = Logger.getLogger(OIDCLoginProtocolFactory.class); public static final String USERNAME = "username"; public static final String EMAIL = "email"; public static final String EMAIL_VERIFIED = "email verified"; public static final String GIVEN_NAME = "given name"; public static final String FAMILY_NAME = "family name"; public static final String FULL_NAME = "full name"; public static final String LOCALE = "locale"; public static final String USERNAME_CONSENT_TEXT = "${username}"; public static final String EMAIL_CONSENT_TEXT = "${email}"; public static final String EMAIL_VERIFIED_CONSENT_TEXT = "${emailVerified}"; public static final String GIVEN_NAME_CONSENT_TEXT = "${givenName}"; public static final String FAMILY_NAME_CONSENT_TEXT = "${familyName}"; public static final String FULL_NAME_CONSENT_TEXT = "${fullName}"; public static final String LOCALE_CONSENT_TEXT = "${locale}"; @Override public LoginProtocol create(KeycloakSession session) { return new OIDCLoginProtocol().setSession(session); } @Override public List<ProtocolMapperModel> getBuiltinMappers() { return builtins; } @Override public List<ProtocolMapperModel> getDefaultBuiltinMappers() { return defaultBuiltins; } static List<ProtocolMapperModel> builtins = new ArrayList<>(); static List<ProtocolMapperModel> defaultBuiltins = new ArrayList<>(); static { ProtocolMapperModel model; model = UserPropertyMapper.createClaimMapper(USERNAME, "username", "preferred_username", "String", true, USERNAME_CONSENT_TEXT, true, true); builtins.add(model); defaultBuiltins.add(model); model = UserPropertyMapper.createClaimMapper(EMAIL, "email", "email", "String", true, EMAIL_CONSENT_TEXT, true, true); builtins.add(model); defaultBuiltins.add(model); model = UserPropertyMapper.createClaimMapper(GIVEN_NAME, "firstName", "given_name", "String", true, GIVEN_NAME_CONSENT_TEXT, true, true); builtins.add(model); defaultBuiltins.add(model); model = UserPropertyMapper.createClaimMapper(FAMILY_NAME, "lastName", "family_name", "String", true, FAMILY_NAME_CONSENT_TEXT, true, true); builtins.add(model); defaultBuiltins.add(model); model = UserPropertyMapper.createClaimMapper(EMAIL_VERIFIED, "emailVerified", "email_verified", "boolean", false, EMAIL_VERIFIED_CONSENT_TEXT, true, true); builtins.add(model); model = UserAttributeMapper.createClaimMapper(LOCALE, "locale", "locale", "String", false, LOCALE_CONSENT_TEXT, true, true, false); builtins.add(model); ProtocolMapperModel fullName = new ProtocolMapperModel(); fullName.setName(FULL_NAME); fullName.setProtocolMapper(FullNameMapper.PROVIDER_ID); fullName.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); fullName.setConsentRequired(true); fullName.setConsentText(FULL_NAME_CONSENT_TEXT); Map<String, String> config = new HashMap<String, String>(); config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); fullName.setConfig(config); builtins.add(fullName); defaultBuiltins.add(fullName); ProtocolMapperModel address = AddressMapper.createAddressMapper(); builtins.add(address); model = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME, KerberosConstants.GSS_DELEGATION_CREDENTIAL, KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String", true, "${gssDelegationCredential}", true, false); builtins.add(model); } @Override protected void addDefaults(ClientModel client) { for (ProtocolMapperModel model : defaultBuiltins) client.addProtocolMapper(model); } @Override public Object createProtocolEndpoint(RealmModel realm, EventBuilder event) { return new OIDCLoginProtocolService(realm, event); } @Override public String getId() { return OIDCLoginProtocol.LOGIN_PROTOCOL; } @Override public void setupClientDefaults(ClientRepresentation rep, ClientModel newClient) { if (rep.getRootUrl() != null && (rep.getRedirectUris() == null || rep.getRedirectUris().isEmpty())) { String root = rep.getRootUrl(); if (root.endsWith("/")) root = root + "*"; else root = root + "/*"; newClient.addRedirectUri(root); Set<String> origins = new HashSet<String>(); String origin = UriUtils.getOrigin(root); logger.debugv("adding default client origin: {0}" , origin); origins.add(origin); newClient.setWebOrigins(origins); } if (rep.isBearerOnly() == null && rep.isPublicClient() == null) { newClient.setPublicClient(true); } if (rep.isBearerOnly() == null) newClient.setBearerOnly(false); if (rep.getAdminUrl() == null && rep.getRootUrl() != null) { newClient.setManagementUrl(rep.getRootUrl()); } // Backwards compatibility only if (rep.isDirectGrantsOnly() != null) { ServicesLogger.LOGGER.usingDeprecatedDirectGrantsOnly(); newClient.setStandardFlowEnabled(!rep.isDirectGrantsOnly()); newClient.setDirectAccessGrantsEnabled(rep.isDirectGrantsOnly()); } else { if (rep.isStandardFlowEnabled() == null) newClient.setStandardFlowEnabled(true); if (rep.isDirectAccessGrantsEnabled() == null) newClient.setDirectAccessGrantsEnabled(true); } if (rep.isImplicitFlowEnabled() == null) newClient.setImplicitFlowEnabled(false); if (rep.isPublicClient() == null) newClient.setPublicClient(true); if (rep.isFrontchannelLogout() == null) newClient.setFrontchannelLogout(false); } @Override public void setupTemplateDefaults(ClientTemplateRepresentation clientRep, ClientTemplateModel newClient) { } }