gt:                 .setValidator(new StringAllowedValuesValidator(BASE64, HEX))
context: /*
   * JBoss, Home of Professional Open Source.
   * Copyright 2015 Red Hat, Inc., and individual 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.wildfly.extension.elytron;
  
  import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY;
  import static org.wildfly.extension.elytron.ElytronDefinition.commonDependencies;
  import static org.wildfly.extension.elytron.ElytronDescriptionConstants.BASE64;
  import static org.wildfly.extension.elytron.ElytronDescriptionConstants.BCRYPT_MAPPER;
  import static org.wildfly.extension.elytron.ElytronDescriptionConstants.CLEAR_PASSWORD_MAPPER;
  import static org.wildfly.extension.elytron.ElytronDescriptionConstants.HEX;
  import static org.wildfly.extension.elytron.ElytronDescriptionConstants.MODULAR_CRYPT_MAPPER;
  import static org.wildfly.extension.elytron.ElytronDescriptionConstants.SALTED_SIMPLE_DIGEST_MAPPER;
  import static org.wildfly.extension.elytron.ElytronDescriptionConstants.SCRAM_MAPPER;
  import static org.wildfly.extension.elytron.ElytronDescriptionConstants.SIMPLE_DIGEST_MAPPER;
  import static org.wildfly.extension.elytron._private.ElytronSubsystemMessages.ROOT_LOGGER;
  
  import java.security.InvalidKeyException;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import javax.sql.DataSource;
  
  import org.jboss.as.controller.AbstractAddStepHandler;
  import org.jboss.as.controller.AttributeDefinition;
  import org.jboss.as.controller.ObjectListAttributeDefinition;
  import org.jboss.as.controller.ObjectTypeAttributeDefinition;
  import org.jboss.as.controller.OperationContext;
  import org.jboss.as.controller.OperationFailedException;
  import org.jboss.as.controller.OperationStepHandler;
  import org.jboss.as.controller.PathElement;
  import org.jboss.as.controller.ResourceDefinition;
  import org.jboss.as.controller.RunningMode;
  import org.jboss.as.controller.SimpleAttributeDefinition;
  import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
  import org.jboss.as.controller.SimpleResourceDefinition;
  import org.jboss.as.controller.capability.RuntimeCapability;
  import org.jboss.as.controller.operations.validation.StringAllowedValuesValidator;
  import org.jboss.as.controller.operations.validation.IntRangeValidator;
  import org.jboss.as.controller.registry.AttributeAccess;
  import org.jboss.as.controller.registry.ManagementResourceRegistration;
  import org.jboss.as.controller.registry.OperationEntry;
  import org.jboss.dmr.ModelNode;
  import org.jboss.dmr.ModelType;
  import org.jboss.msc.inject.InjectionException;
  import org.jboss.msc.inject.Injector;
  import org.jboss.msc.service.ServiceBuilder;
  import org.jboss.msc.service.ServiceController;
  import org.jboss.msc.service.ServiceName;
  import org.jboss.msc.service.ServiceTarget;
  import org.wildfly.security.auth.realm.jdbc.JdbcSecurityRealm;
  import org.wildfly.security.auth.realm.jdbc.JdbcSecurityRealmBuilder;
  import org.wildfly.security.auth.realm.jdbc.KeyMapper;
  import org.wildfly.security.auth.realm.jdbc.QueryBuilder;
  import org.wildfly.security.auth.realm.jdbc.mapper.AttributeMapper;
  import org.wildfly.security.auth.realm.jdbc.mapper.PasswordKeyMapper;
  import org.wildfly.security.auth.server.SecurityRealm;
  import org.wildfly.security.password.interfaces.BCryptPassword;
  import org.wildfly.security.password.interfaces.ClearPassword;
  import org.wildfly.security.password.interfaces.SaltedSimpleDigestPassword;
  import org.wildfly.security.password.interfaces.ScramDigestPassword;
  import org.wildfly.security.password.interfaces.SimpleDigestPassword;
  
  /**
   * A {@link ResourceDefinition} for a {@link SecurityRealm} backed by a database using JDBC.
   *
   * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
   */
  class JdbcRealmDefinition extends SimpleResourceDefinition {
  
      /**
       * {@link ElytronDescriptionConstants#CLEAR_PASSWORD_MAPPER} complex attribute;
       */
      static class ClearPasswordObjectDefinition implements PasswordMapperObjectDefinition {
  
          static final SimpleAttributeDefinition ALGORITHM = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.ALGORITHM, ModelType.STRING)
                  .setRequired(false)
                  .setDefaultValue(new ModelNode(ClearPassword.ALGORITHM_CLEAR))
                  .setValidator(new StringAllowedValuesValidator(ClearPassword.ALGORITHM_CLEAR))
                  .setAllowExpression(false)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final SimpleAttributeDefinition PASSWORD = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.PASSWORD_INDEX, ModelType.INT, false)
                  .setMinSize(1)
                  .setValidator(new IntRangeValidator(1))
                  .setAllowExpression(true)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final ObjectTypeAttributeDefinition OBJECT_DEFINITION = new ObjectTypeAttributeDefinition.Builder(
                  ElytronDescriptionConstants.CLEAR_PASSWORD_MAPPER, PASSWORD)
                  .setRequired(false)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          @Override
          public PasswordKeyMapper toPasswordKeyMapper(OperationContext context, ModelNode propertyNode) throws OperationFailedException {
              String algorithm = ALGORITHM.resolveModelAttribute(context, propertyNode).asStringOrNull();
              int password = PASSWORD.resolveModelAttribute(context, propertyNode).asInt();
  
              return PasswordKeyMapper
                      .builder()
                      .setDefaultAlgorithm(algorithm)
                      .setHashColumn(password)
                      .build();
          }
      }
  
      /**
       * {@link ElytronDescriptionConstants#BCRYPT_MAPPER} complex attribute;
       */
      static class BcryptPasswordObjectDefinition implements PasswordMapperObjectDefinition {
  
          static final SimpleAttributeDefinition ALGORITHM = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.ALGORITHM, ModelType.STRING)
                  .setRequired(false)
                  .setDefaultValue(new ModelNode(BCryptPassword.ALGORITHM_BCRYPT))
                  .setValidator(new StringAllowedValuesValidator(BCryptPassword.ALGORITHM_BCRYPT))
                  .setAllowExpression(false)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final SimpleAttributeDefinition PASSWORD = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.PASSWORD_INDEX, ModelType.INT, false)
                  .setMinSize(1)
                  .setValidator(new IntRangeValidator(1))
                  .setAllowExpression(true)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final SimpleAttributeDefinition ITERATION_COUNT = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.ITERATION_COUNT_INDEX, ModelType.INT, false)
                  .setValidator(new IntRangeValidator(1))
                  .setAllowExpression(true)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final SimpleAttributeDefinition SALT = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.SALT_INDEX, ModelType.INT, false)
                  .setValidator(new IntRangeValidator(1))
                  .setAllowExpression(true)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final SimpleAttributeDefinition HASH_ENCODING = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.HASH_ENCODING, ModelType.STRING)
                  .setRequired(false)
                  .setDefaultValue(new ModelNode(BASE64))
                  .setValidator(new StringAllowedValuesValidator(BASE64, HEX))
                  .setAllowExpression(true)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final SimpleAttributeDefinition SALT_ENCODING = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.SALT_ENCODING, ModelType.STRING)
                  .setRequired(false)
                  .setDefaultValue(new ModelNode(BASE64))
                  .setValidator(new StringAllowedValuesValidator(BASE64, HEX))
                  .setAllowExpression(true)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          @Deprecated
          static final ObjectTypeAttributeDefinition OBJECT_DEFINITION = new ObjectTypeAttributeDefinition.Builder(
                  ElytronDescriptionConstants.BCRYPT_MAPPER, PASSWORD, SALT, ITERATION_COUNT)
                  .setRequired(false)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final ObjectTypeAttributeDefinition OBJECT_DEFINITION_7_0 = new ObjectTypeAttributeDefinition.Builder(
                  ElytronDescriptionConstants.BCRYPT_MAPPER, PASSWORD, SALT, ITERATION_COUNT, HASH_ENCODING, SALT_ENCODING)
                  .setRequired(false)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          @Override
          public PasswordKeyMapper toPasswordKeyMapper(OperationContext context, ModelNode propertyNode) throws OperationFailedException {
              String algorithm = ALGORITHM.resolveModelAttribute(context, propertyNode).asStringOrNull();
              int password = PASSWORD.resolveModelAttribute(context, propertyNode).asInt();
              int salt = SALT.resolveModelAttribute(context, propertyNode).asInt();
              int iterationCount = ITERATION_COUNT.resolveModelAttribute(context, propertyNode).asInt();
              String hashEncoding = HASH_ENCODING.resolveModelAttribute(context, propertyNode).asStringOrNull();
              String saltEncoding = SALT_ENCODING.resolveModelAttribute(context, propertyNode).asStringOrNull();
  
              return PasswordKeyMapper.builder()
                      .setDefaultAlgorithm(algorithm)
                      .setHashColumn(password)
                      .setSaltColumn(salt)
                      .setIterationCountColumn(iterationCount)
                      .setHashEncoding(HEX.equals(hashEncoding) ? PasswordKeyMapper.Encoding.HEX : PasswordKeyMapper.Encoding.BASE64)
                      .setSaltEncoding(HEX.equals(saltEncoding) ? PasswordKeyMapper.Encoding.HEX : PasswordKeyMapper.Encoding.BASE64)
                      .build();
          }
      }
  
      /**
       * {@link ElytronDescriptionConstants#SALTED_SIMPLE_DIGEST_MAPPER} complex attribute;
       */
      static class SaltedSimpleDigestObjectDefinition implements PasswordMapperObjectDefinition {
  
          static final SimpleAttributeDefinition ALGORITHM = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.ALGORITHM, ModelType.STRING)
                  .setRequired(false)
                  .setDefaultValue(new ModelNode(SaltedSimpleDigestPassword.ALGORITHM_PASSWORD_SALT_DIGEST_MD5))
                  .setValidator(new StringAllowedValuesValidator(
                          SaltedSimpleDigestPassword.ALGORITHM_PASSWORD_SALT_DIGEST_MD5,
                          SaltedSimpleDigestPassword.ALGORITHM_PASSWORD_SALT_DIGEST_SHA_1,
                          SaltedSimpleDigestPassword.ALGORITHM_PASSWORD_SALT_DIGEST_SHA_256,
                          SaltedSimpleDigestPassword.ALGORITHM_PASSWORD_SALT_DIGEST_SHA_384,
                          SaltedSimpleDigestPassword.ALGORITHM_PASSWORD_SALT_DIGEST_SHA_512,
                          SaltedSimpleDigestPassword.ALGORITHM_SALT_PASSWORD_DIGEST_MD5,
                          SaltedSimpleDigestPassword.ALGORITHM_SALT_PASSWORD_DIGEST_SHA_1,
                          SaltedSimpleDigestPassword.ALGORITHM_SALT_PASSWORD_DIGEST_SHA_256,
                          SaltedSimpleDigestPassword.ALGORITHM_SALT_PASSWORD_DIGEST_SHA_384,
                          SaltedSimpleDigestPassword.ALGORITHM_SALT_PASSWORD_DIGEST_SHA_512
                  ))
                  .setAllowExpression(true)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final SimpleAttributeDefinition PASSWORD = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.PASSWORD_INDEX, ModelType.INT, false)
                  .setMinSize(1)
                  .setValidator(new IntRangeValidator(1))
                  .setAllowExpression(true)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final SimpleAttributeDefinition SALT = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.SALT_INDEX, ModelType.INT, false)
                  .setValidator(new IntRangeValidator(1))
                  .setAllowExpression(true)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final SimpleAttributeDefinition HASH_ENCODING = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.HASH_ENCODING, ModelType.STRING)
                  .setRequired(false)
                  .setDefaultValue(new ModelNode(BASE64))
                  .setValidator(new StringAllowedValuesValidator(BASE64, HEX))
                  .setAllowExpression(true)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final SimpleAttributeDefinition SALT_ENCODING = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.SALT_ENCODING, ModelType.STRING)
                  .setRequired(false)
                  .setDefaultValue(new ModelNode(BASE64))
                  .setValidator(new StringAllowedValuesValidator(BASE64, HEX))
                  .setAllowExpression(true)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          @Deprecated
          static final ObjectTypeAttributeDefinition OBJECT_DEFINITION = new ObjectTypeAttributeDefinition.Builder(
                  ElytronDescriptionConstants.SALTED_SIMPLE_DIGEST_MAPPER, ALGORITHM, PASSWORD, SALT)
                  .setRequired(false)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final ObjectTypeAttributeDefinition OBJECT_DEFINITION_7_0 = new ObjectTypeAttributeDefinition.Builder(
                  ElytronDescriptionConstants.SALTED_SIMPLE_DIGEST_MAPPER, ALGORITHM, PASSWORD, SALT, HASH_ENCODING, SALT_ENCODING)
                  .setRequired(false)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          @Override
          public PasswordKeyMapper toPasswordKeyMapper(OperationContext context, ModelNode propertyNode) throws OperationFailedException {
              String algorithm = ALGORITHM.resolveModelAttribute(context, propertyNode).asStringOrNull();
              int password = PASSWORD.resolveModelAttribute(context, propertyNode).asInt();
              int salt = SALT.resolveModelAttribute(context, propertyNode).asInt();
              String hashEncoding = HASH_ENCODING.resolveModelAttribute(context, propertyNode).asStringOrNull();
              String saltEncoding = SALT_ENCODING.resolveModelAttribute(context, propertyNode).asStringOrNull();
  
              return PasswordKeyMapper.builder()
                      .setDefaultAlgorithm(algorithm)
                      .setHashColumn(password)
                      .setSaltColumn(salt)
                      .setHashEncoding(HEX.equals(hashEncoding) ? PasswordKeyMapper.Encoding.HEX : PasswordKeyMapper.Encoding.BASE64)
                      .setSaltEncoding(HEX.equals(saltEncoding) ? PasswordKeyMapper.Encoding.HEX : PasswordKeyMapper.Encoding.BASE64)
                      .build();
          }
      }
  
      /**
       * {@link ElytronDescriptionConstants#SIMPLE_DIGEST_MAPPER} complex attribute;
       */
      static class SimpleDigestMapperObjectDefinition implements PasswordMapperObjectDefinition {
  
          static final SimpleAttributeDefinition ALGORITHM = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.ALGORITHM, ModelType.STRING)
                  .setRequired(false)
                  .setDefaultValue(new ModelNode(SimpleDigestPassword.ALGORITHM_SIMPLE_DIGEST_MD5))
                  .setValidator(new StringAllowedValuesValidator(
                          SimpleDigestPassword.ALGORITHM_SIMPLE_DIGEST_MD2,
                          SimpleDigestPassword.ALGORITHM_SIMPLE_DIGEST_MD5,
                          SimpleDigestPassword.ALGORITHM_SIMPLE_DIGEST_SHA_1,
                          SimpleDigestPassword.ALGORITHM_SIMPLE_DIGEST_SHA_256,
                          SimpleDigestPassword.ALGORITHM_SIMPLE_DIGEST_SHA_384,
                          SimpleDigestPassword.ALGORITHM_SIMPLE_DIGEST_SHA_512
                  ))
                  .setAllowExpression(false)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final SimpleAttributeDefinition PASSWORD = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.PASSWORD_INDEX, ModelType.INT, false)
                  .setMinSize(1)
                  .setValidator(new IntRangeValidator(1))
                  .setAllowExpression(true)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final SimpleAttributeDefinition HASH_ENCODING = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.HASH_ENCODING, ModelType.STRING)
                  .setRequired(false)
                  .setDefaultValue(new ModelNode(BASE64))
                  .setValidator(new StringAllowedValuesValidator(BASE64, HEX))
                  .setAllowExpression(true)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          @Deprecated
          static final ObjectTypeAttributeDefinition OBJECT_DEFINITION = new ObjectTypeAttributeDefinition.Builder(
                  ElytronDescriptionConstants.SIMPLE_DIGEST_MAPPER, ALGORITHM, PASSWORD)
                  .setRequired(false)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final ObjectTypeAttributeDefinition OBJECT_DEFINITION_7_0 = new ObjectTypeAttributeDefinition.Builder(
                  ElytronDescriptionConstants.SIMPLE_DIGEST_MAPPER, ALGORITHM, PASSWORD, HASH_ENCODING)
                  .setRequired(false)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          @Override
          public PasswordKeyMapper toPasswordKeyMapper(OperationContext context, ModelNode propertyNode) throws OperationFailedException {
              String algorithm = ALGORITHM.resolveModelAttribute(context, propertyNode).asStringOrNull();
              int password = PASSWORD.resolveModelAttribute(context, propertyNode).asInt();
              String hashEncoding = HASH_ENCODING.resolveModelAttribute(context, propertyNode).asStringOrNull();
  
              return PasswordKeyMapper.builder()
                      .setDefaultAlgorithm(algorithm)
                      .setHashColumn(password)
                      .setHashEncoding(HEX.equals(hashEncoding) ? PasswordKeyMapper.Encoding.HEX : PasswordKeyMapper.Encoding.BASE64)
                      .build();
          }
      }
  
      /**
       * {@link ElytronDescriptionConstants#SCRAM_MAPPER} complex attribute;
       */
      static class ScramMapperObjectDefinition implements PasswordMapperObjectDefinition {
  
          static final SimpleAttributeDefinition ALGORITHM = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.ALGORITHM, ModelType.STRING)
                  .setRequired(false)
                  .setDefaultValue(new ModelNode(ScramDigestPassword.ALGORITHM_SCRAM_SHA_256))
                  .setValidator(new StringAllowedValuesValidator(
                          ScramDigestPassword.ALGORITHM_SCRAM_SHA_1,
                          ScramDigestPassword.ALGORITHM_SCRAM_SHA_256,
                          ScramDigestPassword.ALGORITHM_SCRAM_SHA_384,
                          ScramDigestPassword.ALGORITHM_SCRAM_SHA_512
                  ))
                  .setAllowExpression(true)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final SimpleAttributeDefinition PASSWORD = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.PASSWORD_INDEX, ModelType.INT, false)
                  .setMinSize(1)
                  .setValidator(new IntRangeValidator(1))
                  .setAllowExpression(true)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final SimpleAttributeDefinition ITERATION_COUNT = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.ITERATION_COUNT_INDEX, ModelType.INT, false)
                  .setValidator(new IntRangeValidator(1))
                  .setAllowExpression(true)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final SimpleAttributeDefinition SALT = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.SALT_INDEX, ModelType.INT, false)
                  .setValidator(new IntRangeValidator(1))
                  .setAllowExpression(true)
                  .setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)
                  .build();
  
          static final SimpleAttributeDefinition HASH_ENCODING = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.HASH_ENCODING, ModelType.STRING)
                  .setRequired(false)
                  .setDefaultValue(new ModelNode(BASE64))
  