content: /*
   * 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.alvearie.keycloak;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.List;
  
  import org.keycloak.models.ClientSessionContext;
  import org.keycloak.models.KeycloakSession;
  import org.keycloak.models.ProtocolMapperModel;
  import org.keycloak.models.UserModel;
  import org.keycloak.models.UserSessionModel;
  import org.keycloak.models.utils.KeycloakModelUtils;
  import org.keycloak.protocol.ProtocolMapperUtils;
  import org.keycloak.protocol.oidc.mappers.AbstractOIDCProtocolMapper;
  import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper;
  import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenResponseMapper;
  import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
  import org.keycloak.protocol.oidc.mappers.OIDCIDTokenMapper;
  import org.keycloak.protocol.oidc.mappers.UserInfoTokenMapper;
  import org.keycloak.provider.ProviderConfigProperty;
  import org.keycloak.representations.AccessTokenResponse;
  import org.keycloak.representations.IDToken;
  
  /**
   * Maps a UserModel.attribute to a token claim and/or token response field.
   * The token claim name can be a full qualified nested object name, e.g. "address.country".
   * This will create a nested json object within the token claim.
   *
   * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
   * @version $Revision: 1 $
   */
  public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, OIDCAccessTokenResponseMapper {
  
      private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
      static {
          ProviderConfigProperty property;
          property = new ProviderConfigProperty();
          property.setName(ProtocolMapperUtils.USER_ATTRIBUTE);
          property.setLabel(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_LABEL);
          property.setHelpText(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_HELP_TEXT);
          property.setType(ProviderConfigProperty.STRING_TYPE);
          configProperties.add(property);
          OIDCAttributeMapperHelper.addAttributeConfig(configProperties, UserAttributeMapper.class);
  
          property = new ProviderConfigProperty();
          property.setName(ProtocolMapperUtils.MULTIVALUED);
          property.setLabel(ProtocolMapperUtils.MULTIVALUED_LABEL);
          property.setHelpText(ProtocolMapperUtils.MULTIVALUED_HELP_TEXT);
          property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
          configProperties.add(property);
  
          property = new ProviderConfigProperty();
          property.setName(ProtocolMapperUtils.AGGREGATE_ATTRS);
          property.setLabel(ProtocolMapperUtils.AGGREGATE_ATTRS_LABEL);
          property.setHelpText(ProtocolMapperUtils.AGGREGATE_ATTRS_HELP_TEXT);
          property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
          configProperties.add(property);
      }
  
      public static final String PROVIDER_ID = "oidc-usermodel-attribute-mapper-with-token-response-support";
  
  
      @Override
      public List<ProviderConfigProperty> getConfigProperties() {
          return configProperties;
      }
  
      @Override
      public String getId() {
          return PROVIDER_ID;
      }
  
      @Override
      public String getDisplayType() {
          return "User Attribute (with token response support)";
      }
  
      @Override
      public String getDisplayCategory() {
          return TOKEN_MAPPER_CATEGORY;
      }
  
      @Override
      public String getHelpText() {
          return "Map a custom user attribute to a token claim and/or token response field.";
      }
  
      @Override
      protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) {
  
          UserModel user = userSession.getUser();
          String attributeName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE);
          boolean aggregateAttrs = Boolean.valueOf(mappingModel.getConfig().get(ProtocolMapperUtils.AGGREGATE_ATTRS));
          Collection<String> attributeValue = KeycloakModelUtils.resolveAttribute(user, attributeName, aggregateAttrs);
          if (attributeValue == null) return;
          OIDCAttributeMapperHelper.mapClaim(token, mappingModel, attributeValue);
      }
  
      public static ProtocolMapperModel createClaimMapper(String name,
                                                          String userAttribute,
                                                          String tokenClaimName, String claimType,
                                                          boolean accessToken, boolean idToken, boolean multivalued) {
          return createClaimMapper(name, userAttribute, tokenClaimName, claimType,
                  accessToken, idToken, multivalued, false);
      }
  
      public static ProtocolMapperModel createClaimMapper(String name,
                                                          String userAttribute,
                                                          String tokenClaimName, String claimType,
                                                          boolean accessToken, boolean idToken,
                                                          boolean multivalued, boolean aggregateAttrs) {
          ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute,
                  tokenClaimName, claimType,
                  accessToken, idToken,
                  PROVIDER_ID);
  
          if (multivalued) {
              mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, "true");
          }
          if (aggregateAttrs) {
              mapper.getConfig().put(ProtocolMapperUtils.AGGREGATE_ATTRS, "true");
          }
  
          return mapper;
      }
  
      @Override
      protected void setClaim(AccessTokenResponse accessTokenResponse, ProtocolMapperModel mappingModel,
              UserSessionModel userSession, KeycloakSession keycloakSession, ClientSessionContext clientSessionCtx) {
  
          UserModel user = userSession.getUser();
          String attributeName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE);
          boolean aggregateAttrs = Boolean.valueOf(mappingModel.getConfig().get(ProtocolMapperUtils.AGGREGATE_ATTRS));
          Collection<String> attributeValue = KeycloakModelUtils.resolveAttribute(user, attributeName, aggregateAttrs);
          if (attributeValue == null) return;
          OIDCAttributeMapperHelper.mapClaim(accessTokenResponse, mappingModel, attributeValue);
      }
  }
  