]> source.dussan.org Git - archiva.git/commitdiff
Adding LDAP Rest V2 configuration and check
authorMartin Stockhammer <martin_s@apache.org>
Sun, 10 Jan 2021 22:31:49 +0000 (23:31 +0100)
committerMartin Stockhammer <martin_s@apache.org>
Sun, 10 Jan 2021 22:31:49 +0000 (23:31 +0100)
15 files changed:
archiva-modules/archiva-base/archiva-repository-admin/archiva-repository-admin-default/src/main/java/org/apache/archiva/admin/repository/runtime/DefaultRedbackRuntimeConfigurationAdmin.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/v2/LdapConfiguration.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/v2/SecurityConfigurationService.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/DefaultSecurityConfigurationService.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/v2/ErrorKeys.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/v2/NativeSecurityConfigurationServiceTest.java
archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/package-lock.json
archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/package.json
archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/model/ldap-configuration.ts
archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/core/errors/http-error-interceptor.ts
archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/security-configuration/ldap-security/ldap-security.component.html
archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/security-configuration/ldap-security/ldap-security.component.spec.ts
archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/security-configuration/ldap-security/ldap-security.component.ts
archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/security.service.ts
archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json

index 86cad9e7807c4910e277609bd987f197b7af39c8..1ed25d946ec5ba9a0c2e7a1d30f6a88565aafed5 100644 (file)
@@ -422,7 +422,6 @@ public class DefaultRedbackRuntimeConfigurationAdmin
     private void cleanupProperties( RedbackRuntimeConfiguration redbackRuntimeConfiguration )
     {
         Map<String, String> properties = redbackRuntimeConfiguration.getConfigurationProperties();
-        LdapConfiguration ldapConf = redbackRuntimeConfiguration.getLdapConfiguration( );
         LDAP_MAPPER.getAllAttributes( ).stream( ).forEach( att -> properties.remove( att ) );
     }
 
index 378c5e234c57b2b64f50bd85f5beede816443298..ae789a12f1024b541f7886f09d5c923503637cde 100644 (file)
@@ -20,6 +20,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
 
 import javax.xml.bind.annotation.XmlRootElement;
 import java.io.Serializable;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.TreeMap;
@@ -36,16 +37,18 @@ public class LdapConfiguration implements Serializable
 
     private String hostName = "";
     private int port = 389;
-    private boolean sslEnabled = false;
     private String baseDn = "";
     private String groupsBaseDn = "";
     private String bindDn = "";
     private String bindPassword = "";
-    private String authenticationMethod = "";
+    private String authenticationMethod = "none";
+    private String contextFactory;
+    private boolean sslEnabled = false;
     private boolean bindAuthenticatorEnabled = true;
     private boolean useRoleNameAsGroup = false;
     private final Map<String, String> properties = new TreeMap<>();
     private boolean writable = false;
+    private List<String> availableContextFactories;
 
     public LdapConfiguration( )
     {
@@ -60,9 +63,14 @@ public class LdapConfiguration implements Serializable
         newCfg.setBindPassword( ldapConfiguration.getPassword() );
         newCfg.setBindAuthenticatorEnabled( ldapConfiguration.isBindAuthenticatorEnabled() );
         newCfg.setHostName( ldapConfiguration.getHostName( ) );
-        newCfg.setPort( ldapConfiguration.getPort( ) );
-        newCfg.setProperties( ldapConfiguration.getExtraProperties( ) );
         newCfg.setSslEnabled( ldapConfiguration.isSsl() );
+        if (ldapConfiguration.getPort()<=0) {
+            newCfg.setPort( newCfg.isSslEnabled() ? 636 : 389 );
+        } else
+        {
+            newCfg.setPort( ldapConfiguration.getPort( ) );
+        }
+        newCfg.setProperties( ldapConfiguration.getExtraProperties( ) );
         newCfg.setWritable( ldapConfiguration.isWritable() );
         return newCfg;
     }
@@ -89,7 +97,19 @@ public class LdapConfiguration implements Serializable
         this.port = port;
     }
 
-    @Schema(name="ssl_enabled", description = "If SSL should be used for connecting the LDAP server")
+    @Schema(name="context_factory",description = "The class name of the LDAP context factory")
+    public String getContextFactory( )
+    {
+        return contextFactory;
+    }
+
+    public void setContextFactory( String contextFactory )
+    {
+        this.contextFactory = contextFactory;
+    }
+
+
+    @Schema(name="ssl_enabled", description = "True, if SSL/TLS should be used for connecting the LDAP server")
     public boolean isSslEnabled( )
     {
         return sslEnabled;
@@ -177,7 +197,7 @@ public class LdapConfiguration implements Serializable
         this.useRoleNameAsGroup = useRoleNameAsGroup;
     }
 
-    @Schema(description = "Map of additional properties")
+    @Schema(description = "LDAP ConnectionFactory environment properties")
     public Map<String, String> getProperties( )
     {
         return properties;
@@ -200,6 +220,19 @@ public class LdapConfiguration implements Serializable
         this.writable = writable;
     }
 
+    @Schema(name="available_context_factories", description = "The LDAP context factories that are known and available")
+    public List<String> getAvailableContextFactories( )
+    {
+        return availableContextFactories;
+    }
+
+    public void setAvailableContextFactories( List<String> availableContextFactories )
+    {
+        this.availableContextFactories = availableContextFactories;
+    }
+
+
+
     @Override
     public boolean equals( Object o )
     {
index a8eb4591970995e4e70ba488cde67bf01e6da759..aa02581c6a164a1349b773d000b3f815c5af95ab 100644 (file)
@@ -39,6 +39,7 @@ import org.apache.archiva.security.common.ArchivaRoleConstants;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
+import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
@@ -240,6 +241,30 @@ public interface SecurityConfigurationService
     )
     Response updateLdapConfiguration( LdapConfiguration configuration ) throws ArchivaRestServiceException;
 
+    @Path("config/ldap/verify")
+    @POST
+    @Consumes({ APPLICATION_JSON })
+    @RedbackAuthorization(permissions = ArchivaRoleConstants.OPERATION_MANAGE_CONFIGURATION)
+    @Operation( summary = "Checks the given LDAP configuration.",
+        security = {
+            @SecurityRequirement(
+                name = ArchivaRoleConstants.OPERATION_MANAGE_CONFIGURATION
+            )
+        },
+        responses = {
+            @ApiResponse( responseCode = "200",
+                description = "If the check was successful"
+            ),
+            @ApiResponse( responseCode = "400",
+                description = "If the check was not successful",
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = ArchivaRestError.class ))
+            ),
+            @ApiResponse( responseCode = "403", description = "Authenticated user is not permitted to update the information",
+                content = @Content(mediaType = APPLICATION_JSON, schema = @Schema(implementation = ArchivaRestError.class )) )
+        }
+    )
+    Response verifyLdapConfiguration( LdapConfiguration configuration ) throws ArchivaRestServiceException;
+
     @Path("config/cache")
     @GET
     @Produces({ APPLICATION_JSON })
index d1b4fe1876010ffaa9fc4cac42ed5464d16c97db..80e65c2bcbd9ba7bd5b4d75151b57e26637db97f 100644 (file)
@@ -25,14 +25,16 @@ import org.apache.archiva.components.rest.model.PropertyEntry;
 import org.apache.archiva.components.rest.util.PagingHelper;
 import org.apache.archiva.components.rest.util.QueryHelper;
 import org.apache.archiva.redback.authentication.Authenticator;
+import org.apache.archiva.redback.common.ldap.connection.LdapConnection;
+import org.apache.archiva.redback.common.ldap.connection.LdapConnectionConfiguration;
 import org.apache.archiva.redback.common.ldap.connection.LdapConnectionFactory;
+import org.apache.archiva.redback.common.ldap.connection.LdapException;
 import org.apache.archiva.redback.common.ldap.user.LdapUserMapper;
 import org.apache.archiva.redback.policy.CookieSettings;
 import org.apache.archiva.redback.policy.PasswordRule;
 import org.apache.archiva.redback.rbac.RBACManager;
 import org.apache.archiva.redback.role.RoleManager;
 import org.apache.archiva.redback.users.UserManager;
-import org.apache.archiva.rest.api.model.UserManagerImplementationInformation;
 import org.apache.archiva.rest.api.model.v2.BeanInformation;
 import org.apache.archiva.rest.api.model.v2.CacheConfiguration;
 import org.apache.archiva.rest.api.model.v2.LdapConfiguration;
@@ -41,7 +43,6 @@ import org.apache.archiva.rest.api.services.v2.ArchivaRestServiceException;
 import org.apache.archiva.rest.api.services.v2.ErrorMessage;
 import org.apache.archiva.rest.api.services.v2.SecurityConfigurationService;
 import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.ListUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -51,7 +52,12 @@ import org.springframework.stereotype.Service;
 import javax.annotation.PostConstruct;
 import javax.inject.Inject;
 import javax.inject.Named;
-import javax.management.Query;
+import javax.naming.AuthenticationException;
+import javax.naming.AuthenticationNotSupportedException;
+import javax.naming.CommunicationException;
+import javax.naming.InvalidNameException;
+import javax.naming.NoPermissionException;
+import javax.naming.ServiceUnavailableException;
 import javax.ws.rs.core.Response;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -59,7 +65,7 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
+import java.util.Properties;
 import java.util.ResourceBundle;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
@@ -74,9 +80,14 @@ public class DefaultSecurityConfigurationService implements SecurityConfiguratio
 {
     private static final Logger log = LoggerFactory.getLogger( DefaultSecurityConfigurationService.class );
 
+    private static final String[] KNOWN_LDAP_CONTEXT_PROVIDERS = {"com.sun.jndi.ldap.LdapCtxFactory","com.ibm.jndi.LDAPCtxFactory"};
+    private List<String> availableContextProviders = new ArrayList<>( );
+
     private static final QueryHelper<PropertyEntry> PROP_QUERY_HELPER = new QueryHelper( new String[]{"key"} );
     private static final PagingHelper PROP_PAGING_HELPER = new PagingHelper( );
-    static {
+
+    static
+    {
         PROP_QUERY_HELPER.addStringFilter( "key", PropertyEntry::getKey );
         PROP_QUERY_HELPER.addStringFilter( "value", PropertyEntry::getValue );
         PROP_QUERY_HELPER.addNullsafeFieldComparator( "key", PropertyEntry::getKey );
@@ -94,31 +105,37 @@ public class DefaultSecurityConfigurationService implements SecurityConfiguratio
     private ApplicationContext applicationContext;
 
     @Inject
-    @Named(value = "userManager#default")
+    @Named( value = "userManager#default" )
     private UserManager userManager;
 
     @Inject
-    @Named(value = "rbacManager#default")
+    @Named( value = "rbacManager#default" )
     private RBACManager rbacManager;
 
     @Inject
     private RoleManager roleManager;
 
     @Inject
-    @Named(value = "ldapConnectionFactory#configurable")
+    @Named( value = "ldapConnectionFactory#configurable" )
     private LdapConnectionFactory ldapConnectionFactory;
 
     @Inject
     private LdapUserMapper ldapUserMapper;
 
     @Inject
-    @Named(value = "cache#users")
+    @Named( value = "cache#users" )
     private Cache usersCache;
 
 
     @PostConstruct
-    void init() {
+    void init( )
+    {
         bundle = ResourceBundle.getBundle( "org.apache.archiva.rest.RestBundle" );
+        for (String ldapClass : KNOWN_LDAP_CONTEXT_PROVIDERS) {
+            if (isContextFactoryAvailable( ldapClass )) {
+                availableContextProviders.add( ldapClass );
+            }
+        }
     }
 
     @Override
@@ -138,128 +155,151 @@ public class DefaultSecurityConfigurationService implements SecurityConfiguratio
             throw new ArchivaRestServiceException( ErrorMessage.of( REPOSITORY_ADMIN_ERROR ) );
         }
     }
-    private void updateConfig(SecurityConfiguration newConfig, RedbackRuntimeConfiguration rbConfig) {
-        rbConfig.setUserManagerImpls( newConfig.getActiveUserManagers() );
-        rbConfig.setRbacManagerImpls( newConfig.getActiveRbacManagers() );
-        rbConfig.setUseUsersCache( newConfig.isUserCacheEnabled() );
+
+    private void updateConfig( SecurityConfiguration newConfig, RedbackRuntimeConfiguration rbConfig )
+    {
+        rbConfig.setUserManagerImpls( newConfig.getActiveUserManagers( ) );
+        rbConfig.setRbacManagerImpls( newConfig.getActiveRbacManagers( ) );
+        rbConfig.setUseUsersCache( newConfig.isUserCacheEnabled( ) );
         Map<String, String> props = rbConfig.getConfigurationProperties( );
-        for ( Map.Entry<String,String> newProp : newConfig.getProperties().entrySet() ) {
+        for ( Map.Entry<String, String> newProp : newConfig.getProperties( ).entrySet( ) )
+        {
             props.put( newProp.getKey( ), newProp.getValue( ) );
         }
     }
 
-    private void updateConfig(LdapConfiguration newConfig, RedbackRuntimeConfiguration rbConfig) {
+    private void updateConfig( LdapConfiguration newConfig, RedbackRuntimeConfiguration rbConfig )
+    {
         org.apache.archiva.admin.model.beans.LdapConfiguration ldapConfig = rbConfig.getLdapConfiguration( );
         ldapConfig.setBaseDn( newConfig.getBaseDn( ) );
-        ldapConfig.setAuthenticationMethod( newConfig.getAuthenticationMethod() );
+        ldapConfig.setAuthenticationMethod( newConfig.getAuthenticationMethod( ) );
         ldapConfig.setBindAuthenticatorEnabled( newConfig.isBindAuthenticatorEnabled( ) );
-        ldapConfig.setBindDn( newConfig.getBindDn() );
+        ldapConfig.setBindDn( newConfig.getBindDn( ) );
         ldapConfig.setSsl( newConfig.isSslEnabled( ) );
-        ldapConfig.setBaseGroupsDn( newConfig.getGroupsBaseDn() );
-        ldapConfig.setHostName( newConfig.getHostName() );
-        ldapConfig.setPort( newConfig.getPort() );
-        ldapConfig.setPassword( newConfig.getBindPassword() );
-        ldapConfig.setUseRoleNameAsGroup( newConfig.isUseRoleNameAsGroup() );
+        ldapConfig.setBaseGroupsDn( newConfig.getGroupsBaseDn( ) );
+        ldapConfig.setHostName( newConfig.getHostName( ) );
+        ldapConfig.setPort( newConfig.getPort( ) );
+        ldapConfig.setPassword( newConfig.getBindPassword( ) );
+        ldapConfig.setUseRoleNameAsGroup( newConfig.isUseRoleNameAsGroup( ) );
         ldapConfig.setWritable( newConfig.isWritable( ) );
 
         Map<String, String> props = ldapConfig.getExtraProperties( );
-        for ( Map.Entry<String,String> newProp : newConfig.getProperties().entrySet() ) {
+        for ( Map.Entry<String, String> newProp : newConfig.getProperties( ).entrySet( ) )
+        {
             props.put( newProp.getKey( ), newProp.getValue( ) );
         }
     }
 
-    private void updateConfig(CacheConfiguration newConfig, RedbackRuntimeConfiguration rbConfig) {
+    private void updateConfig( CacheConfiguration newConfig, RedbackRuntimeConfiguration rbConfig )
+    {
         org.apache.archiva.admin.model.beans.CacheConfiguration cacheConfig = rbConfig.getUsersCacheConfiguration( );
-        cacheConfig.setMaxElementsInMemory( newConfig.getMaxEntriesInMemory());
-        cacheConfig.setMaxElementsOnDisk( newConfig.getMaxEntriesOnDisk() );
-        cacheConfig.setTimeToLiveSeconds( newConfig.getTimeToLiveSeconds() );
-        cacheConfig.setTimeToIdleSeconds( newConfig.getTimeToIdleSeconds() );
+        cacheConfig.setMaxElementsInMemory( newConfig.getMaxEntriesInMemory( ) );
+        cacheConfig.setMaxElementsOnDisk( newConfig.getMaxEntriesOnDisk( ) );
+        cacheConfig.setTimeToLiveSeconds( newConfig.getTimeToLiveSeconds( ) );
+        cacheConfig.setTimeToIdleSeconds( newConfig.getTimeToIdleSeconds( ) );
     }
 
     @Override
     public Response updateConfiguration( SecurityConfiguration newConfiguration ) throws ArchivaRestServiceException
     {
-        if (newConfiguration==null) {
+        if ( newConfiguration == null )
+        {
             throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.MISSING_DATA ), 400 );
         }
         try
         {
             RedbackRuntimeConfiguration conf = redbackRuntimeConfigurationAdmin.getRedbackRuntimeConfiguration( );
-            boolean userManagerChanged = !CollectionUtils.isEqualCollection( newConfiguration.getActiveUserManagers( ), conf.getUserManagerImpls() );
+            boolean userManagerChanged = !CollectionUtils.isEqualCollection( newConfiguration.getActiveUserManagers( ), conf.getUserManagerImpls( ) );
             boolean rbacManagerChanged = !CollectionUtils.isEqualCollection( newConfiguration.getActiveRbacManagers( ), conf.getRbacManagerImpls( ) );
 
             boolean ldapConfigured = false;
-            for (String um : newConfiguration.getActiveUserManagers()) {
-                if (um.contains("ldap")) {
-                    ldapConfigured=true;
+            for ( String um : newConfiguration.getActiveUserManagers( ) )
+            {
+                if ( um.contains( "ldap" ) )
+                {
+                    ldapConfigured = true;
                 }
             }
-            if (!ldapConfigured) {
-                for (String rbm : newConfiguration.getActiveRbacManagers()) {
-                    if (rbm.contains("ldap")) {
+            if ( !ldapConfigured )
+            {
+                for ( String rbm : newConfiguration.getActiveRbacManagers( ) )
+                {
+                    if ( rbm.contains( "ldap" ) )
+                    {
                         ldapConfigured = true;
                     }
                 }
             }
 
-            updateConfig( newConfiguration, conf);
+            updateConfig( newConfiguration, conf );
             redbackRuntimeConfigurationAdmin.updateRedbackRuntimeConfiguration( conf );
 
             if ( userManagerChanged )
             {
                 log.info( "user managerImpls changed to {} so reload it",
-                    newConfiguration.getActiveUserManagers() );
-                userManager.initialize();
+                    newConfiguration.getActiveUserManagers( ) );
+                userManager.initialize( );
             }
 
             if ( rbacManagerChanged )
             {
                 log.info( "rbac manager changed to {} so reload it",
-                    newConfiguration.getActiveRbacManagers() );
-                rbacManager.initialize();
-                roleManager.initialize();
+                    newConfiguration.getActiveRbacManagers( ) );
+                rbacManager.initialize( );
+                roleManager.initialize( );
             }
 
-            if (ldapConfigured) {
-                try {
-                    ldapConnectionFactory.initialize();
-                } catch (Exception e) {
+            if ( ldapConfigured )
+            {
+                try
+                {
+                    ldapConnectionFactory.initialize( );
+                }
+                catch ( Exception e )
+                {
                     log.error( "Could not initialize LDAP connection factory: {}", e.getMessage( ) );
-                    throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.LDAP_CF_INIT_FAILED, e.getMessage() ) );
+                    throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.LDAP_CF_INIT_FAILED, e.getMessage( ) ) );
                 }
             }
-            Collection<PasswordRule> passwordRules = applicationContext.getBeansOfType( PasswordRule.class ).values();
+            Collection<PasswordRule> passwordRules = applicationContext.getBeansOfType( PasswordRule.class ).values( );
 
             for ( PasswordRule passwordRule : passwordRules )
             {
-                passwordRule.initialize();
+                passwordRule.initialize( );
             }
 
             Collection<CookieSettings> cookieSettingsList =
-                applicationContext.getBeansOfType( CookieSettings.class ).values();
+                applicationContext.getBeansOfType( CookieSettings.class ).values( );
 
             for ( CookieSettings cookieSettings : cookieSettingsList )
             {
-                cookieSettings.initialize();
+                cookieSettings.initialize( );
             }
 
             Collection<Authenticator> authenticators =
-                applicationContext.getBeansOfType( Authenticator.class ).values();
+                applicationContext.getBeansOfType( Authenticator.class ).values( );
 
             for ( Authenticator authenticator : authenticators )
             {
-                try {
-                    log.debug("Initializing authenticatior "+authenticator.getId());
-                    authenticator.initialize();
-                } catch (Exception e) {
-                    log.error("Initialization of authenticator failed "+authenticator.getId(),e);
+                try
+                {
+                    log.debug( "Initializing authenticatior " + authenticator.getId( ) );
+                    authenticator.initialize( );
+                }
+                catch ( Exception e )
+                {
+                    log.error( "Initialization of authenticator failed " + authenticator.getId( ), e );
                 }
             }
 
-            if (ldapConfigured) {
-                try {
-                    ldapUserMapper.initialize();
-                } catch (Exception e) {
+            if ( ldapConfigured )
+            {
+                try
+                {
+                    ldapUserMapper.initialize( );
+                }
+                catch ( Exception e )
+                {
                     throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.LDAP_USER_MAPPER_INIT_FAILED, e.getMessage( ) ) );
                 }
             }
@@ -268,7 +308,7 @@ public class DefaultSecurityConfigurationService implements SecurityConfiguratio
         {
             throw new ArchivaRestServiceException( ErrorMessage.of( REPOSITORY_ADMIN_ERROR, e.getMessage( ) ) );
         }
-        return Response.ok( ).build();
+        return Response.ok( ).build( );
     }
 
     @Override
@@ -306,10 +346,13 @@ public class DefaultSecurityConfigurationService implements SecurityConfiguratio
         try
         {
             RedbackRuntimeConfiguration conf = redbackRuntimeConfigurationAdmin.getRedbackRuntimeConfiguration( );
-            if (conf.getConfigurationProperties().containsKey( propertyName )) {
+            if ( conf.getConfigurationProperties( ).containsKey( propertyName ) )
+            {
                 String value = conf.getConfigurationProperties( ).get( propertyName );
                 return new PropertyEntry( propertyName, value );
-            } else {
+            }
+            else
+            {
                 throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.PROPERTY_NOT_FOUND ), 404 );
             }
 
@@ -324,17 +367,21 @@ public class DefaultSecurityConfigurationService implements SecurityConfiguratio
     @Override
     public Response updateConfigurationProperty( String propertyName, PropertyEntry propertyValue ) throws ArchivaRestServiceException
     {
-        if (propertyValue==null) {
+        if ( propertyValue == null )
+        {
             throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.MISSING_DATA ), 400 );
         }
         try
         {
             RedbackRuntimeConfiguration conf = redbackRuntimeConfigurationAdmin.getRedbackRuntimeConfiguration( );
-            if (conf.getConfigurationProperties().containsKey( propertyName )) {
+            if ( conf.getConfigurationProperties( ).containsKey( propertyName ) )
+            {
                 conf.getConfigurationProperties( ).put( propertyName, propertyValue.getValue( ) );
                 redbackRuntimeConfigurationAdmin.updateRedbackRuntimeConfiguration( conf );
                 return Response.ok( ).build( );
-            } else {
+            }
+            else
+            {
                 throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.PROPERTY_NOT_FOUND ), 404 );
             }
 
@@ -355,7 +402,9 @@ public class DefaultSecurityConfigurationService implements SecurityConfiguratio
 
             log.debug( "getRedbackRuntimeConfiguration -> {}", redbackRuntimeConfiguration );
 
-            return LdapConfiguration.of( redbackRuntimeConfiguration.getLdapConfiguration() );
+            LdapConfiguration ldapConfig = LdapConfiguration.of( redbackRuntimeConfiguration.getLdapConfiguration( ) );
+            ldapConfig.setAvailableContextFactories( availableContextProviders );
+            return ldapConfig;
         }
         catch ( RepositoryAdminException e )
         {
@@ -387,6 +436,123 @@ public class DefaultSecurityConfigurationService implements SecurityConfiguratio
         }
     }
 
+    static final Properties toProperties( Map<String, String> values )
+    {
+        Properties result = new Properties( );
+        for ( Map.Entry<String, String> entry : values.entrySet( ) )
+        {
+            result.setProperty( entry.getKey( ), entry.getValue( ) );
+        }
+        return result;
+    }
+
+    private static final boolean isContextFactoryAvailable(final String factoryClass)
+    {
+        try
+        {
+            return Thread.currentThread().getContextClassLoader().loadClass( factoryClass )
+                != null;
+        }
+        catch ( ClassNotFoundException e )
+        {
+            return false;
+        }
+    }
+
+
+    @Override
+    public Response verifyLdapConfiguration( LdapConfiguration ldapConfiguration ) throws ArchivaRestServiceException
+    {
+        LdapConnection ldapConnection = null;
+        try
+        {
+            LdapConnectionConfiguration ldapConnectionConfiguration =
+                new LdapConnectionConfiguration( ldapConfiguration.getHostName( ), ldapConfiguration.getPort( ),
+                    ldapConfiguration.getBaseDn( ), ldapConfiguration.getContextFactory( ),
+                    ldapConfiguration.getBindDn( ), ldapConfiguration.getBindPassword( ),
+                    ldapConfiguration.getAuthenticationMethod( ),
+                    toProperties( ldapConfiguration.getProperties( ) ) );
+            ldapConnectionConfiguration.setSsl( ldapConfiguration.isSslEnabled( ) );
+
+            ldapConnection = ldapConnectionFactory.getConnection( ldapConnectionConfiguration );
+        }
+        catch ( InvalidNameException e )
+        {
+            log.warn( "LDAP connection check failed with invalid name : {}", e.getMessage( ), e );
+            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.LDAP_INVALID_NAME, e.getMessage( ) ), 400 );
+        }
+        catch ( LdapException e )
+        {
+            handleLdapException( e );
+        }
+        finally
+        {
+            if ( ldapConnection != null )
+            {
+                ldapConnection.close( );
+            }
+            ldapConnection = null;
+        }
+
+        try
+        {
+            // verify groups dn value too
+
+            LdapConnectionConfiguration ldapConnectionConfiguration = new LdapConnectionConfiguration( ldapConfiguration.getHostName( ), ldapConfiguration.getPort( ),
+                ldapConfiguration.getGroupsBaseDn( ),
+                ldapConfiguration.getContextFactory( ), ldapConfiguration.getBindDn( ),
+                ldapConfiguration.getBindPassword( ),
+                ldapConfiguration.getAuthenticationMethod( ),
+                toProperties( ldapConfiguration.getProperties( ) ) );
+
+            ldapConnectionConfiguration.setSsl( ldapConfiguration.isSslEnabled( ) );
+
+            ldapConnection = ldapConnectionFactory.getConnection( ldapConnectionConfiguration );
+        }
+        catch ( InvalidNameException e )
+        {
+            log.warn( "LDAP connection check failed with invalid name : {}", e.getMessage( ), e );
+            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.LDAP_INVALID_NAME, e.getMessage( ) ), 400 );
+        }
+        catch ( LdapException e )
+        {
+            handleLdapException( e );
+        }
+        finally
+        {
+            if ( ldapConnection != null )
+            {
+                ldapConnection.close( );
+            }
+        }
+
+        return Response.ok( ).build( );
+    }
+
+    private void handleLdapException( LdapException e ) throws ArchivaRestServiceException
+    {
+        Throwable rootCause = e.getRootCause( );
+        if ( rootCause instanceof CommunicationException )
+        {
+            log.warn( "LDAP connection check failed with CommunicationException: {}", e.getMessage( ), e );
+            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.LDAP_COMMUNICATION_ERROR, e.getMessage( ) ), 400 );
+        } else if (rootCause instanceof ServiceUnavailableException ) {
+            log.warn( "LDAP connection check failed with ServiceUnavailableException: {}", e.getMessage( ), e );
+            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.LDAP_SERVICE_UNAVAILABLE, e.getMessage( ) ), 400 );
+        } else if (rootCause instanceof AuthenticationException ) {
+            log.warn( "LDAP connection check failed with AuthenticationException: {}", e.getMessage( ), e );
+            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.LDAP_SERVICE_AUTHENTICATION_FAILED, e.getMessage( ) ), 400 );
+        } else if (rootCause instanceof AuthenticationNotSupportedException ) {
+            log.warn( "LDAP connection check failed with AuthenticationNotSupportedException: {}", e.getMessage( ), e );
+            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.LDAP_SERVICE_AUTHENTICATION_NOT_SUPPORTED, e.getMessage( ) ), 400 );
+        } else if (rootCause instanceof NoPermissionException ) {
+            log.warn( "LDAP connection check failed with NoPermissionException: {}", e.getMessage( ), e );
+            throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.LDAP_SERVICE_NO_PERMISSION, e.getMessage( ) ), 400 );
+        }
+        log.warn( "LDAP connection check failed: {} - {}", e.getClass().getName(), e.getMessage( ), e );
+        throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.LDAP_GENERIC_ERROR, e.getMessage( ) ), 400 );
+    }
+
     @Override
     public CacheConfiguration getCacheConfiguration( ) throws ArchivaRestServiceException
     {
@@ -397,7 +563,7 @@ public class DefaultSecurityConfigurationService implements SecurityConfiguratio
 
             log.debug( "getRedbackRuntimeConfiguration -> {}", redbackRuntimeConfiguration );
 
-            return CacheConfiguration.of( redbackRuntimeConfiguration.getUsersCacheConfiguration() );
+            return CacheConfiguration.of( redbackRuntimeConfiguration.getUsersCacheConfiguration( ) );
         }
         catch ( RepositoryAdminException e )
         {
@@ -409,7 +575,8 @@ public class DefaultSecurityConfigurationService implements SecurityConfiguratio
     @Override
     public Response updateCacheConfiguration( CacheConfiguration cacheConfiguration ) throws ArchivaRestServiceException
     {
-        if (cacheConfiguration==null) {
+        if ( cacheConfiguration == null )
+        {
             throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.MISSING_DATA ), 400 );
         }
         try
@@ -433,20 +600,20 @@ public class DefaultSecurityConfigurationService implements SecurityConfiguratio
     {
         Map<String, UserManager> beans = applicationContext.getBeansOfType( UserManager.class );
 
-        if ( beans.isEmpty() )
+        if ( beans.isEmpty( ) )
         {
-            return Collections.emptyList();
+            return Collections.emptyList( );
         }
 
         return beans.entrySet( ).stream( )
-            .filter( entry -> entry.getValue().isFinalImplementation() )
-            .map( (Map.Entry<String, UserManager> entry) -> {
+            .filter( entry -> entry.getValue( ).isFinalImplementation( ) )
+            .map( ( Map.Entry<String, UserManager> entry ) -> {
                 UserManager um = entry.getValue( );
                 String id = StringUtils.substringAfter( entry.getKey( ), "#" );
                 String displayName = bundle.getString( "user_manager." + id + ".display_name" );
                 String description = bundle.getString( "user_manager." + id + ".description" );
                 return new BeanInformation( StringUtils.substringAfter( entry.getKey( ), "#" ), displayName, um.getDescriptionKey( ), description, um.isReadOnly( ) );
-            } ).collect( Collectors.toList());
+            } ).collect( Collectors.toList( ) );
     }
 
     @Override
@@ -454,19 +621,19 @@ public class DefaultSecurityConfigurationService implements SecurityConfiguratio
     {
         Map<String, RBACManager> beans = applicationContext.getBeansOfType( RBACManager.class );
 
-        if ( beans.isEmpty() )
+        if ( beans.isEmpty( ) )
         {
-            return Collections.emptyList();
+            return Collections.emptyList( );
         }
 
         return beans.entrySet( ).stream( )
-            .filter( entry -> entry.getValue().isFinalImplementation() )
-            .map( (Map.Entry<String, RBACManager> entry) -> {
+            .filter( entry -> entry.getValue( ).isFinalImplementation( ) )
+            .map( ( Map.Entry<String, RBACManager> entry ) -> {
                 RBACManager rm = entry.getValue( );
                 String id = StringUtils.substringAfter( entry.getKey( ), "#" );
                 String displayName = bundle.getString( "rbac_manager." + id + ".display_name" );
                 String description = bundle.getString( "rbac_manager." + id + ".description" );
                 return new BeanInformation( StringUtils.substringAfter( entry.getKey( ), "#" ), displayName, rm.getDescriptionKey( ), description, rm.isReadOnly( ) );
-            } ).collect( Collectors.toList());
+            } ).collect( Collectors.toList( ) );
     }
 }
index 455617e0918ea5a351c7a49fc11d55f58d01973a..22b8543d48baa0b1cd44bd3f13d4b05af7fd38a1 100644 (file)
@@ -24,6 +24,13 @@ public interface ErrorKeys
     String REPOSITORY_ADMIN_ERROR = "archiva.repositoryadmin.error";
     String LDAP_CF_INIT_FAILED = "archiva.ldap.cf.init.failed";
     String LDAP_USER_MAPPER_INIT_FAILED = "archiva.ldap.usermapper.init.failed";
+    String LDAP_COMMUNICATION_ERROR = "archiva.ldap.communication_error";
+    String LDAP_INVALID_NAME = "archiva.ldap.invalid_name";
+    String LDAP_GENERIC_ERROR = "archiva.ldap.generic_error";
+    String LDAP_SERVICE_UNAVAILABLE = "archiva.ldap.service_unavailable";
+    String LDAP_SERVICE_AUTHENTICATION_FAILED = "archiva.ldap.authentication.failed";
+    String LDAP_SERVICE_AUTHENTICATION_NOT_SUPPORTED = "archiva.ldap.authentication.not_supported";
+    String LDAP_SERVICE_NO_PERMISSION = "archiva.ldap.no_permissions";
 
     String PROPERTY_NOT_FOUND = "archiva.property.not.found";
 
index 02d0dc986fe00c426ad8cd7173e1badf6a355ff4..13b0a3e5b32f8cab9736517cdb0e5e901aaa4d05 100644 (file)
@@ -127,7 +127,7 @@ public class NativeSecurityConfigurationServiceTest extends AbstractNativeRestSe
             .then( ).statusCode( 200 ).extract( ).response( );
         assertNotNull( response );
         assertEquals( "", response.getBody( ).jsonPath( ).get( "host_name" ) );
-        assertEquals( 13, response.getBody( ).jsonPath( ).getMap( "properties" ).size( ) );
+        assertEquals( 0, response.getBody( ).jsonPath( ).getMap( "properties" ).size( ) );
     }
 
 
index 4c3d50efd1059b83bcb867cacb4ae14b7deafab7..d668ec0df42a6754475a95beda239c1b83422fe8 100644 (file)
@@ -5,25 +5,25 @@
   "requires": true,
   "dependencies": {
     "@angular-devkit/architect": {
-      "version": "0.1100.2",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1100.2.tgz",
-      "integrity": "sha512-wSMMM8eBPol48OtvIyrIq2H9rOIiJmrPEtPbH0BSuPX0B8BckVImeTPzloqxSrpul4tY7Iwx0zwISDEgb59Vbw==",
+      "version": "0.1100.6",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1100.6.tgz",
+      "integrity": "sha512-4O+cg3AimI2bNAxxdu5NrqSf4Oa8r8xL0+G2Ycd3jLoFv0h0ecJiNKEG5F6IpTprb4aexZD6pcxBJCqQ8MmzWQ==",
       "dev": true,
       "requires": {
-        "@angular-devkit/core": "11.0.2",
+        "@angular-devkit/core": "11.0.6",
         "rxjs": "6.6.3"
       }
     },
     "@angular-devkit/build-angular": {
-      "version": "0.1100.2",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.1100.2.tgz",
-      "integrity": "sha512-5Qo3DDKggzUJKibNgeyE5mIMFYP0tVebNvMatpbnYnR/U0fUuuQdvNC68s380M5KoOuubfeXr0Js0VFk0mkaow==",
+      "version": "0.1100.6",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.1100.6.tgz",
+      "integrity": "sha512-HcqsWiSIUxExGg3HRQScLOmF+ckVkCKolfpPcNOCCpBYxH/i8n4wDGLBP5Rtxky+0Qz+3nnAaFIpNb9p9aUmbg==",
       "dev": true,
       "requires": {
-        "@angular-devkit/architect": "0.1100.2",
-        "@angular-devkit/build-optimizer": "0.1100.2",
-        "@angular-devkit/build-webpack": "0.1100.2",
-        "@angular-devkit/core": "11.0.2",
+        "@angular-devkit/architect": "0.1100.6",
+        "@angular-devkit/build-optimizer": "0.1100.6",
+        "@angular-devkit/build-webpack": "0.1100.6",
+        "@angular-devkit/core": "11.0.6",
         "@babel/core": "7.12.3",
         "@babel/generator": "7.12.1",
         "@babel/plugin-transform-runtime": "7.12.1",
@@ -31,7 +31,7 @@
         "@babel/runtime": "7.12.1",
         "@babel/template": "7.10.4",
         "@jsdevtools/coverage-istanbul-loader": "3.0.5",
-        "@ngtools/webpack": "11.0.2",
+        "@ngtools/webpack": "11.0.6",
         "ansi-colors": "4.1.1",
         "autoprefixer": "9.8.6",
         "babel-loader": "8.1.0",
@@ -77,7 +77,7 @@
         "speed-measure-webpack-plugin": "1.3.3",
         "style-loader": "2.0.0",
         "stylus": "0.54.8",
-        "stylus-loader": "4.1.1",
+        "stylus-loader": "4.3.1",
         "terser": "5.3.7",
         "terser-webpack-plugin": "4.2.3",
         "text-table": "0.2.0",
       }
     },
     "@angular-devkit/build-optimizer": {
-      "version": "0.1100.2",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1100.2.tgz",
-      "integrity": "sha512-2ZdEeAs0a53g9LDkP5H2mCEPLyk7yd9P7eTepNYvIOz3xJ6W6dB2CqotPMfnHgd4o12cbzCOWrPBxbfo/VnMig==",
+      "version": "0.1100.6",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1100.6.tgz",
+      "integrity": "sha512-Qkq7n6510N+nXmfZqpqpI0I6Td+b+06RRNmS7KftSNJntU1z5QYh4FggwlthZ5P0QUT92cnBQsnT8OgYqGnwbg==",
       "dev": true,
       "requires": {
         "loader-utils": "2.0.0",
       }
     },
     "@angular-devkit/build-webpack": {
-      "version": "0.1100.2",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1100.2.tgz",
-      "integrity": "sha512-XVMtWoxNa3wJLRjJ846Y02PzupdbUizdAtggRu2731RLMvI1KawWlsTURi12MNUnoVQYm9eldiIA/Y1UqeE8mQ==",
+      "version": "0.1100.6",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1100.6.tgz",
+      "integrity": "sha512-kK0FlpYJHP25o1yzIGHQqIvO5kp+p6V5OwGpD2GGRZLlJqd3WdjY5DxnyZoX3/IofO6KsTnmm76fzTRqc62z/Q==",
       "dev": true,
       "requires": {
-        "@angular-devkit/architect": "0.1100.2",
-        "@angular-devkit/core": "11.0.2",
+        "@angular-devkit/architect": "0.1100.6",
+        "@angular-devkit/core": "11.0.6",
         "rxjs": "6.6.3"
       }
     },
     "@angular-devkit/core": {
-      "version": "11.0.2",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.0.2.tgz",
-      "integrity": "sha512-vUmmUNmNM9oRcDmt0PunU/ayglo0apq4pGL9Z5jj6alf2WwEiTcGHjyuZSDIO9MOLi41519jp3mDx79qXvvyww==",
+      "version": "11.0.6",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.0.6.tgz",
+      "integrity": "sha512-nhvU5hH01r9qcexAqvIFU233treWWeW3ncs9UFYjD9Hys9sDSvqC3+bvGvl9vCG5FsyY7oDsjaVAipyUc+SFAg==",
       "dev": true,
       "requires": {
         "ajv": "6.12.6",
       }
     },
     "@angular-devkit/schematics": {
-      "version": "11.0.2",
-      "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-11.0.2.tgz",
-      "integrity": "sha512-unNewc+Y9ofrdKxXNoSHKUL6wvV8Vgh2nJMTLI1VAw8nfqgWphI+s5XwbVzog65nhZ10xJeaUm9u5R8pxLDpQg==",
+      "version": "11.0.6",
+      "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-11.0.6.tgz",
+      "integrity": "sha512-hCyu/SSSiC6dKl/NxdWctknIrBqKR6pRe7DMArWowrZX6P9oi36LpKEFnKutE8+tXjsOqQj8XMBq9L64sXZWqg==",
       "dev": true,
       "requires": {
-        "@angular-devkit/core": "11.0.2",
+        "@angular-devkit/core": "11.0.6",
         "ora": "5.1.0",
         "rxjs": "6.6.3"
       }
     },
     "@angular/animations": {
-      "version": "11.0.2",
-      "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-11.0.2.tgz",
-      "integrity": "sha512-uF/RlBY1rznbuw+1lm8Q2HKDrBOQQ2Bi2cUPuef+ALn+lxGl501eHlE+PTtBjDEzJcJPfd4pE3Ww3+3Il+D+Tw==",
+      "version": "11.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-11.0.7.tgz",
+      "integrity": "sha512-P3cluDGIsaj7vqvqIGW7xFCIXWa1lJDsHsmY3Fexk+ZVCncokftp5ZUANb2+DwOD3BPgd/WjBdXVjwzFQFsoVA==",
       "requires": {
         "tslib": "^2.0.0"
       }
       }
     },
     "@angular/cli": {
-      "version": "11.0.2",
-      "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-11.0.2.tgz",
-      "integrity": "sha512-mebt4ikwXD3gsbHRxKCpn83yW3UVnhiVDEpSXljs1YxscZ1X1dXrxb2g6LdAJwVp9xY5ERqRQeZM7eChqLTrvg==",
+      "version": "11.0.6",
+      "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-11.0.6.tgz",
+      "integrity": "sha512-bwrXXyU23HjUlFl0CNCU+XMGa/enooqpMLcTAA15StVpKFHyaA4c57il/aqu+1IuB+zR6rGDzhAABuvRcHd+mQ==",
       "dev": true,
       "requires": {
-        "@angular-devkit/architect": "0.1100.2",
-        "@angular-devkit/core": "11.0.2",
-        "@angular-devkit/schematics": "11.0.2",
-        "@schematics/angular": "11.0.2",
-        "@schematics/update": "0.1100.2",
+        "@angular-devkit/architect": "0.1100.6",
+        "@angular-devkit/core": "11.0.6",
+        "@angular-devkit/schematics": "11.0.6",
+        "@schematics/angular": "11.0.6",
+        "@schematics/update": "0.1100.6",
         "@yarnpkg/lockfile": "1.1.0",
         "ansi-colors": "4.1.1",
         "debug": "4.2.0",
-        "ini": "1.3.5",
+        "ini": "1.3.6",
         "inquirer": "7.3.3",
         "npm-package-arg": "8.1.0",
         "npm-pick-manifest": "6.1.0",
             "ms": "2.1.2"
           }
         },
+        "ini": {
+          "version": "1.3.6",
+          "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.6.tgz",
+          "integrity": "sha512-IZUoxEjNjubzrmvzZU4lKP7OnYmX72XRl3sqkfJhBKweKi5rnGi5+IUdlj/H1M+Ip5JQ1WzaDMOBRY90Ajc5jg==",
+          "dev": true
+        },
         "resolve": {
           "version": "1.18.1",
           "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz",
       }
     },
     "@angular/common": {
-      "version": "11.0.2",
-      "resolved": "https://registry.npmjs.org/@angular/common/-/common-11.0.2.tgz",
-      "integrity": "sha512-DGJuSBDt+bF77AzJNrLzeaFGSdwQ3OjgP9UUv1eKvaxp9D+lDam8suIJMuBwTsJII/yrDndY75ENPNTEqhmB2A==",
+      "version": "11.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/common/-/common-11.0.7.tgz",
+      "integrity": "sha512-9VuT9qrSP7Q91Wp276DDieCIZiTBrpLNoJzK/RygQShTymCVPg4Dsl3tQUKaHBPx9MexeqRG/HjN02DVpeqtsA==",
       "requires": {
         "tslib": "^2.0.0"
       }
     },
     "@angular/compiler": {
-      "version": "11.0.2",
-      "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-11.0.2.tgz",
-      "integrity": "sha512-deDT5+Lcph4nNhh6sZd0mBS5OkJL3HPbX5upDMI28Wuayt18Pn0UNotWY77/KV6wwIAInmlx9N06PoH3pq3hqg==",
+      "version": "11.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-11.0.7.tgz",
+      "integrity": "sha512-U+aGn6lP3iB184D2Z+OSK5vCwNtxtsF59T7nNJBMCCwcN7Qc5tfDBbSj1GI11XrIiuuoOxXbAp9BKS0dAyNCWw==",
       "requires": {
         "tslib": "^2.0.0"
       }
     },
     "@angular/compiler-cli": {
-      "version": "11.0.2",
-      "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-11.0.2.tgz",
-      "integrity": "sha512-I39zNcf6q0NN4PKCbY6Lm4WP69ujLrAew56X5yvlECW9CJlidV0qi1S/DGgAWhXTDOt8XA/KP1hD1pgJtMHjJQ==",
+      "version": "11.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-11.0.7.tgz",
+      "integrity": "sha512-04agqIPmw1exMeZjJmRzeYFgTEXYJ7gKD8TZipsGlu99uU/NQJIsetpzR7/bhPAKDH6YO0p/uavxV0nRpSTIQw==",
       "dev": true,
       "requires": {
         "@babel/core": "^7.8.6",
           "dev": true
         },
         "yargs": {
-          "version": "16.1.1",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.1.1.tgz",
-          "integrity": "sha512-hAD1RcFP/wfgfxgMVswPE+z3tlPFtxG8/yWUrG2i17sTWGCGqWnxKcLTF4cUKDUK8fzokwsmO9H0TDkRbMHy8w==",
+          "version": "16.2.0",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+          "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
           "dev": true,
           "requires": {
             "cliui": "^7.0.2",
       }
     },
     "@angular/core": {
-      "version": "11.0.2",
-      "resolved": "https://registry.npmjs.org/@angular/core/-/core-11.0.2.tgz",
-      "integrity": "sha512-GyDebks5ZPHDyChDW3VvzJq00Ct0iuesNpb9z/GpKtOXqug3sGr4KgkFDUTbfizKPWyeoaLH9FQYP55215nCKQ==",
+      "version": "11.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/core/-/core-11.0.7.tgz",
+      "integrity": "sha512-Kj5uRZoK5+xfMTjkP3tw8oIF5hKTnoF9Bwh5m9GUKqg1wHVKOJcT5JBIEMc8qPyiFgALREA01reIzQdGMjX36A==",
       "requires": {
         "tslib": "^2.0.0"
       }
     },
     "@angular/forms": {
-      "version": "11.0.2",
-      "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-11.0.2.tgz",
-      "integrity": "sha512-Rn17VPviTTwiDn8Yt/UzdkXjFX0LdvjkmTNZoakqOk8/QNnsCG5sUDJAV7BKHk+2nEfUGCopS4kpBiLKLoaBpQ==",
+      "version": "11.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-11.0.7.tgz",
+      "integrity": "sha512-+3A+SciMyHTdUwkKUz4XzC1DSYexQEbFLe0PKQIFSFOROmbssjnWJv7yO2HbzCpGa7oGKPYNlE5twYWyLxpvFg==",
       "requires": {
         "tslib": "^2.0.0"
       }
     },
     "@angular/localize": {
-      "version": "11.0.2",
-      "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-11.0.2.tgz",
-      "integrity": "sha512-G7v/WPjno5QgY2XvYqK9pKP5lsaE17rP6/FIYhTFoA2TTSTJQ0mWlIQigcTvr+AT2t4U6nFJeteGuyIIvpMJYg==",
+      "version": "11.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-11.0.7.tgz",
+      "integrity": "sha512-NDs08oAELLn7tA/hHLuW8APULg25C7iINYTA168QzOdFTEsJ2MoLf3SiVQExUV65h3MnB24xNbhaNodmBKUNPg==",
       "dev": true,
       "requires": {
         "@babel/core": "7.8.3",
           "dev": true
         },
         "yargs": {
-          "version": "16.1.1",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.1.1.tgz",
-          "integrity": "sha512-hAD1RcFP/wfgfxgMVswPE+z3tlPFtxG8/yWUrG2i17sTWGCGqWnxKcLTF4cUKDUK8fzokwsmO9H0TDkRbMHy8w==",
+          "version": "16.2.0",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+          "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
           "dev": true,
           "requires": {
             "cliui": "^7.0.2",
       }
     },
     "@angular/platform-browser": {
-      "version": "11.0.2",
-      "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-11.0.2.tgz",
-      "integrity": "sha512-RHPm5/h8g3lSBgdg9OvO7w06juEwwBurvQcugXlk7+AeqznwzBodTWGPIATKzMySXQFmpy3bAZ3IxS0NkRrbWA==",
+      "version": "11.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-11.0.7.tgz",
+      "integrity": "sha512-W8Wt8jMUjcbpqGtqrNWAj0p7CLdjOxgVlbrgBXTbaoqdchvXH85YzGr7ohA3MuE61H90OcVK9OhfYQk5o6joSg==",
       "requires": {
         "tslib": "^2.0.0"
       }
     },
     "@angular/platform-browser-dynamic": {
-      "version": "11.0.2",
-      "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.0.2.tgz",
-      "integrity": "sha512-iV7xz90FdmYFiXZRLkZtP9Lr+OXXh4bhkX7zN1L5H8SSUF4iOJGBdOts5Fiy5GZjYYILjF1pJoEIicfW/RSHjA==",
+      "version": "11.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.0.7.tgz",
+      "integrity": "sha512-pUXCum1Z2DZV/34WR4Vfmkc5nWxbmVdwAA9pXbAarwAYqHIqOzX8rpRaHsuHBAR+SK+VH+xjproeLgsVfV8FSA==",
       "requires": {
         "tslib": "^2.0.0"
       }
     },
     "@angular/router": {
-      "version": "11.0.2",
-      "resolved": "https://registry.npmjs.org/@angular/router/-/router-11.0.2.tgz",
-      "integrity": "sha512-EU0lQ+3vv1ozly+Z4SgaGj/6CWMIExjnSnA1F7SI2yWmMgMMSb5CsGJ2xzr0V8ex3XZzuU2VuKF74muC58qSyg==",
+      "version": "11.0.7",
+      "resolved": "https://registry.npmjs.org/@angular/router/-/router-11.0.7.tgz",
+      "integrity": "sha512-oh/MOPRSOCLRPsM/3CVUNYZ3pz3g+CzLOk5Vad/zFJmnGwjA/lQGJo2pl7VXVq3RF7MieaHlDWG5TexGlXAP5w==",
       "requires": {
         "tslib": "^2.0.0"
       }
     },
     "@babel/code-frame": {
-      "version": "7.10.4",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
-      "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+      "version": "7.12.11",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz",
+      "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==",
       "dev": true,
       "requires": {
         "@babel/highlight": "^7.10.4"
       }
     },
     "@babel/helper-annotate-as-pure": {
-      "version": "7.10.4",
-      "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz",
-      "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==",
+      "version": "7.12.10",
+      "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.10.tgz",
+      "integrity": "sha512-XplmVbC1n+KY6jL8/fgLVXXUauDIB+lD5+GsQEh6F6GBF1dq1qy4DP4yXWzDKcoqXB3X58t61e85Fitoww4JVQ==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.10.4"
+        "@babel/types": "^7.12.10"
       }
     },
     "@babel/helper-builder-binary-assignment-operator-visitor": {
       "requires": {
         "@babel/helper-annotate-as-pure": "^7.10.4",
         "regexpu-core": "^4.7.1"
-      },
-      "dependencies": {
-        "regexpu-core": {
-          "version": "4.7.1",
-          "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz",
-          "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==",
-          "dev": true,
-          "requires": {
-            "regenerate": "^1.4.0",
-            "regenerate-unicode-properties": "^8.2.0",
-            "regjsgen": "^0.5.1",
-            "regjsparser": "^0.6.4",
-            "unicode-match-property-ecmascript": "^1.0.4",
-            "unicode-match-property-value-ecmascript": "^1.2.0"
-          }
-        }
       }
     },
     "@babel/helper-define-map": {
       }
     },
     "@babel/helper-function-name": {
-      "version": "7.10.4",
-      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
-      "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
+      "version": "7.12.11",
+      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz",
+      "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==",
       "dev": true,
       "requires": {
-        "@babel/helper-get-function-arity": "^7.10.4",
-        "@babel/template": "^7.10.4",
-        "@babel/types": "^7.10.4"
+        "@babel/helper-get-function-arity": "^7.12.10",
+        "@babel/template": "^7.12.7",
+        "@babel/types": "^7.12.11"
+      },
+      "dependencies": {
+        "@babel/template": {
+          "version": "7.12.7",
+          "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz",
+          "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.10.4",
+            "@babel/parser": "^7.12.7",
+            "@babel/types": "^7.12.7"
+          }
+        }
       }
     },
     "@babel/helper-get-function-arity": {
-      "version": "7.10.4",
-      "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
-      "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
+      "version": "7.12.10",
+      "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz",
+      "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.10.4"
+        "@babel/types": "^7.12.10"
       }
     },
     "@babel/helper-hoist-variables": {
       }
     },
     "@babel/helper-optimise-call-expression": {
-      "version": "7.12.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.7.tgz",
-      "integrity": "sha512-I5xc9oSJ2h59OwyUqjv95HRyzxj53DAubUERgQMrpcCEYQyToeHA+NEcUEsVWB4j53RDeskeBJ0SgRAYHDBckw==",
+      "version": "7.12.10",
+      "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz",
+      "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.12.7"
+        "@babel/types": "^7.12.10"
       }
     },
     "@babel/helper-plugin-utils": {
       }
     },
     "@babel/helper-replace-supers": {
-      "version": "7.12.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz",
-      "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==",
+      "version": "7.12.11",
+      "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz",
+      "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==",
       "dev": true,
       "requires": {
-        "@babel/helper-member-expression-to-functions": "^7.12.1",
-        "@babel/helper-optimise-call-expression": "^7.10.4",
-        "@babel/traverse": "^7.12.5",
-        "@babel/types": "^7.12.5"
+        "@babel/helper-member-expression-to-functions": "^7.12.7",
+        "@babel/helper-optimise-call-expression": "^7.12.10",
+        "@babel/traverse": "^7.12.10",
+        "@babel/types": "^7.12.11"
       }
     },
     "@babel/helper-simple-access": {
       }
     },
     "@babel/helper-split-export-declaration": {
-      "version": "7.11.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
-      "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
+      "version": "7.12.11",
+      "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz",
+      "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==",
       "dev": true,
       "requires": {
-        "@babel/types": "^7.11.0"
+        "@babel/types": "^7.12.11"
       }
     },
     "@babel/helper-validator-identifier": {
-      "version": "7.10.4",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
-      "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+      "version": "7.12.11",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
+      "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==",
       "dev": true
     },
     "@babel/helper-validator-option": {
-      "version": "7.12.1",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz",
-      "integrity": "sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A==",
+      "version": "7.12.11",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.11.tgz",
+      "integrity": "sha512-TBFCyj939mFSdeX7U7DDj32WtzYY7fDcalgq8v3fBZMNOJQNn7nOYzMaUCiPxPYfCup69mtIpqlKgMZLvQ8Xhw==",
       "dev": true
     },
     "@babel/helper-wrap-function": {
       }
     },
     "@babel/parser": {
-      "version": "7.12.7",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.7.tgz",
-      "integrity": "sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg==",
+      "version": "7.12.11",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz",
+      "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==",
       "dev": true
     },
     "@babel/plugin-proposal-async-generator-functions": {
-      "version": "7.12.1",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.1.tgz",
-      "integrity": "sha512-d+/o30tJxFxrA1lhzJqiUcEJdI6jKlNregCv5bASeGf2Q4MXmnwH7viDo7nhx1/ohf09oaH8j1GVYG/e3Yqk6A==",
+      "version": "7.12.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.12.tgz",
+      "integrity": "sha512-nrz9y0a4xmUrRq51bYkWJIO5SBZyG2ys2qinHsN0zHDHVsUaModrkpyWWWXfGqYQmOL3x9sQIcTNN/pBGpo09A==",
       "dev": true,
       "requires": {
         "@babel/helper-plugin-utils": "^7.10.4",
       }
     },
     "@babel/plugin-transform-block-scoping": {
-      "version": "7.12.1",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.1.tgz",
-      "integrity": "sha512-zJyAC9sZdE60r1nVQHblcfCj29Dh2Y0DOvlMkcqSo0ckqjiCwNiUezUKw+RjOCwGfpLRwnAeQ2XlLpsnGkvv9w==",
+      "version": "7.12.12",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.12.tgz",
+      "integrity": "sha512-VOEPQ/ExOVqbukuP7BYJtI5ZxxsmegTwzZ04j1aF0dkSypGo9XpDHuOrABsJu+ie+penpSJheDJ11x1BEZNiyQ==",
       "dev": true,
       "requires": {
         "@babel/helper-plugin-utils": "^7.10.4"
       }
     },
     "@babel/plugin-transform-typeof-symbol": {
-      "version": "7.12.1",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.1.tgz",
-      "integrity": "sha512-EPGgpGy+O5Kg5pJFNDKuxt9RdmTgj5sgrus2XVeMp/ZIbOESadgILUbm50SNpghOh3/6yrbsH+NB5+WJTmsA7Q==",
+      "version": "7.12.10",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.10.tgz",
+      "integrity": "sha512-JQ6H8Rnsogh//ijxspCjc21YPd3VLVoYtAwv3zQmqAt8YGYUtdo5usNhdl4b9/Vir2kPFZl6n1h0PfUz4hJhaA==",
       "dev": true,
       "requires": {
         "@babel/helper-plugin-utils": "^7.10.4"
       }
     },
     "@babel/traverse": {
-      "version": "7.12.9",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz",
-      "integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==",
+      "version": "7.12.12",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz",
+      "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==",
       "dev": true,
       "requires": {
-        "@babel/code-frame": "^7.10.4",
-        "@babel/generator": "^7.12.5",
-        "@babel/helper-function-name": "^7.10.4",
-        "@babel/helper-split-export-declaration": "^7.11.0",
-        "@babel/parser": "^7.12.7",
-        "@babel/types": "^7.12.7",
+        "@babel/code-frame": "^7.12.11",
+        "@babel/generator": "^7.12.11",
+        "@babel/helper-function-name": "^7.12.11",
+        "@babel/helper-split-export-declaration": "^7.12.11",
+        "@babel/parser": "^7.12.11",
+        "@babel/types": "^7.12.12",
         "debug": "^4.1.0",
         "globals": "^11.1.0",
         "lodash": "^4.17.19"
       },
       "dependencies": {
         "@babel/generator": {
-          "version": "7.12.5",
-          "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz",
-          "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==",
+          "version": "7.12.11",
+          "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz",
+          "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==",
           "dev": true,
           "requires": {
-            "@babel/types": "^7.12.5",
+            "@babel/types": "^7.12.11",
             "jsesc": "^2.5.1",
             "source-map": "^0.5.0"
           }
       }
     },
     "@babel/types": {
-      "version": "7.12.7",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz",
-      "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==",
+      "version": "7.12.12",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz",
+      "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==",
       "dev": true,
       "requires": {
-        "@babel/helper-validator-identifier": "^7.10.4",
+        "@babel/helper-validator-identifier": "^7.12.11",
         "lodash": "^4.17.19",
         "to-fast-properties": "^2.0.0"
       }
     },
     "@fortawesome/fontawesome-common-types": {
-      "version": "0.2.29",
-      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.29.tgz",
-      "integrity": "sha512-cY+QfDTbZ7XVxzx7jxbC98Oxr/zc7R2QpTLqTxqlfyXDrAJjzi/xUIqAUsygELB62JIrbsWxtSRhayKFkGI7MA=="
+      "version": "0.2.32",
+      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.32.tgz",
+      "integrity": "sha512-ux2EDjKMpcdHBVLi/eWZynnPxs0BtFVXJkgHIxXRl+9ZFaHPvYamAfCzeeQFqHRjuJtX90wVnMRaMQAAlctz3w=="
     },
     "@fortawesome/fontawesome-free": {
-      "version": "5.13.1",
-      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.13.1.tgz",
-      "integrity": "sha512-D819f34FLHeBN/4xvw0HR0u7U2G7RqjPSggXqf7LktsxWQ48VAfGwvMrhcVuaZV2fF069c/619RdgCCms0DHhw=="
+      "version": "5.15.1",
+      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.1.tgz",
+      "integrity": "sha512-OEdH7SyC1suTdhBGW91/zBfR6qaIhThbcN8PUXtXilY4GYnSBbVqOntdHbC1vXwsDnX0Qix2m2+DSU1J51ybOQ=="
     },
     "@fortawesome/fontawesome-svg-core": {
-      "version": "1.2.29",
-      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.29.tgz",
-      "integrity": "sha512-xmPmP2t8qrdo8RyKihTkGb09RnZoc+7HFBCnr0/6ZhStdGDSLeEd7ajV181+2W29NWIFfylO13rU+s3fpy3cnA==",
+      "version": "1.2.32",
+      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.32.tgz",
+      "integrity": "sha512-XjqyeLCsR/c/usUpdWcOdVtWFVjPbDFBTQkn2fQRrWhhUoxriQohO2RWDxLyUM8XpD+Zzg5xwJ8gqTYGDLeGaQ==",
       "requires": {
-        "@fortawesome/fontawesome-common-types": "^0.2.29"
+        "@fortawesome/fontawesome-common-types": "^0.2.32"
       }
     },
     "@istanbuljs/schema": {
       }
     },
     "@ng-bootstrap/ng-bootstrap": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-8.0.0.tgz",
-      "integrity": "sha512-v77Gfd8xHH+exq0WqIqVRlxbUEHdA/2+RUJenUP2IDTQN9E1rWl7O461/kosr+0XPuxPArHQJxhh/WsCYckcNg==",
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-8.0.1.tgz",
+      "integrity": "sha512-v/TyLbXOguEGiV1SuNzHoBeahGwCihBcB96LJz91DexPbM403jiLNxhoFtEYbAPRoPI9v3MILxSgOwCkdyeBVw==",
       "requires": {
         "tslib": "^2.0.0"
       }
     },
     "@ngtools/webpack": {
-      "version": "11.0.2",
-      "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-11.0.2.tgz",
-      "integrity": "sha512-GbNP6HMBVoee2CkYW/pknprFCeiOLz4FGE06yr4m0700c1i6wuX7AzyHfBcLGAIP6nVblNOT3eh5M41b3cDf8g==",
+      "version": "11.0.6",
+      "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-11.0.6.tgz",
+      "integrity": "sha512-vf5YNEpXWRa0fKC/BRq5sVVj2WnEqW8jn14YQRHwVt5ppUeyu8IKUF69p6W1MwZMgMqMaw/vPQ8LI5cFbyf3uw==",
       "dev": true,
       "requires": {
-        "@angular-devkit/core": "11.0.2",
+        "@angular-devkit/core": "11.0.6",
         "enhanced-resolve": "5.3.1",
         "webpack-sources": "2.0.1"
       }
       }
     },
     "@nodelib/fs.scandir": {
-      "version": "2.1.3",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",
-      "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==",
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
+      "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==",
       "dev": true,
       "requires": {
-        "@nodelib/fs.stat": "2.0.3",
+        "@nodelib/fs.stat": "2.0.4",
         "run-parallel": "^1.1.9"
       }
     },
     "@nodelib/fs.stat": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz",
-      "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==",
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz",
+      "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==",
       "dev": true
     },
     "@nodelib/fs.walk": {
-      "version": "1.2.4",
-      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz",
-      "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==",
+      "version": "1.2.6",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz",
+      "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==",
       "dev": true,
       "requires": {
-        "@nodelib/fs.scandir": "2.1.3",
+        "@nodelib/fs.scandir": "2.1.4",
         "fastq": "^1.6.0"
       }
     },
       }
     },
     "@schematics/angular": {
-      "version": "11.0.2",
-      "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-11.0.2.tgz",
-      "integrity": "sha512-tUIuCYJUzHYuiXGJ2KCuwxMocS56kPHaM8+neVYWwWbOxKzLZXv80gMm/pIWxrqUDCkIUi3yb4ienudFhgQLYg==",
+      "version": "11.0.6",
+      "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-11.0.6.tgz",
+      "integrity": "sha512-XUcpOrlcp55PBHrgpIVx69lnhDY6ro35BSRmqNmjXik56qcOkfvdki8vvyW9EsWvu9/sfBSsVDdparlbVois7w==",
       "dev": true,
       "requires": {
-        "@angular-devkit/core": "11.0.2",
-        "@angular-devkit/schematics": "11.0.2",
+        "@angular-devkit/core": "11.0.6",
+        "@angular-devkit/schematics": "11.0.6",
         "jsonc-parser": "2.3.1"
       }
     },
     "@schematics/update": {
-      "version": "0.1100.2",
-      "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.1100.2.tgz",
-      "integrity": "sha512-pETCmQylIQ7RM+8uqDkI3KfOaX5H7nuzmMXby28zdLPMZniYti0gJxieiVFhvdz2Ot2Axj0hznfmraFgC9mQMw==",
+      "version": "0.1100.6",
+      "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.1100.6.tgz",
+      "integrity": "sha512-+B8n+k+zZ3VYOhjNBsLqzjp8O9ZdUWgdpf9L8XAA7mh/oPwufXpExyEc66uAS07imvUMmjz6i8E2eNWV/IjBJg==",
       "dev": true,
       "requires": {
-        "@angular-devkit/core": "11.0.2",
-        "@angular-devkit/schematics": "11.0.2",
+        "@angular-devkit/core": "11.0.6",
+        "@angular-devkit/schematics": "11.0.6",
         "@yarnpkg/lockfile": "1.1.0",
-        "ini": "1.3.5",
+        "ini": "1.3.6",
         "npm-package-arg": "^8.0.0",
         "pacote": "9.5.12",
         "semver": "7.3.2",
         "semver-intersect": "1.4.0"
+      },
+      "dependencies": {
+        "ini": {
+          "version": "1.3.6",
+          "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.6.tgz",
+          "integrity": "sha512-IZUoxEjNjubzrmvzZU4lKP7OnYmX72XRl3sqkfJhBKweKi5rnGi5+IUdlj/H1M+Ip5JQ1WzaDMOBRY90Ajc5jg==",
+          "dev": true
+        }
       }
     },
     "@types/color-name": {
       "dev": true
     },
     "@types/node": {
-      "version": "12.12.47",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.47.tgz",
-      "integrity": "sha512-yzBInQFhdY8kaZmqoL2+3U5dSTMrKaYcb561VU+lDzAYvqt+2lojvBEy+hmpSNuXnPTx7m9+04CzWYOUqWME2A==",
+      "version": "12.19.12",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.12.tgz",
+      "integrity": "sha512-UwfL2uIU9arX/+/PRcIkT08/iBadGN2z6ExOROA2Dh5mAuWTBj6iJbQX4nekiV5H8cTrEG569LeX+HRco9Cbxw==",
       "dev": true
     },
     "@types/parse-json": {
       "dev": true
     },
     "bootstrap": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.5.0.tgz",
-      "integrity": "sha512-Z93QoXvodoVslA+PWNdk23Hze4RBYIkpb5h8I2HY2Tu2h7A0LpAgLcyrhrSUyo2/Oxm2l1fRZPs1e5hnxnliXA=="
+      "version": "4.5.3",
+      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.5.3.tgz",
+      "integrity": "sha512-o9ppKQioXGqhw8Z7mah6KdTYpNQY//tipnkxppWhPbiSWdD+1raYsnhwEZjkTHYbGee4cVQ0Rx65EhOY/HNLcQ=="
     },
     "brace-expansion": {
       "version": "1.1.11",
       }
     },
     "browserslist": {
-      "version": "4.14.7",
-      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.7.tgz",
-      "integrity": "sha512-BSVRLCeG3Xt/j/1cCGj1019Wbty0H+Yvu2AOuZSuoaUWn3RatbL33Cxk+Q4jRMRAbOm0p7SLravLjpnT6s0vzQ==",
+      "version": "4.16.1",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.1.tgz",
+      "integrity": "sha512-UXhDrwqsNcpTYJBTZsbGATDxZbiVDsx6UjpmRUmtnP10pr8wAYr5LgFoEFw9ixriQH2mv/NX2SfGzE/o8GndLA==",
       "dev": true,
       "requires": {
-        "caniuse-lite": "^1.0.30001157",
+        "caniuse-lite": "^1.0.30001173",
         "colorette": "^1.2.1",
-        "electron-to-chromium": "^1.3.591",
+        "electron-to-chromium": "^1.3.634",
         "escalade": "^3.1.1",
-        "node-releases": "^1.1.66"
+        "node-releases": "^1.1.69"
       }
     },
     "browserstack": {
       }
     },
     "call-bind": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz",
-      "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.1.tgz",
+      "integrity": "sha512-tvAvUwNcRikl3RVF20X9lsYmmepsovzTWeJiXjO0PkJp15uy/6xKFZOQtuiSULwYW+6ToZBprphCgWXC2dSgcQ==",
       "dev": true,
       "requires": {
         "function-bind": "^1.1.1",
-        "get-intrinsic": "^1.0.0"
+        "get-intrinsic": "^1.0.2"
       }
     },
     "caller-callsite": {
       }
     },
     "caniuse-lite": {
-      "version": "1.0.30001161",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001161.tgz",
-      "integrity": "sha512-JharrCDxOqPLBULF9/SPa6yMcBRTjZARJ6sc3cuKrPfyIk64JN6kuMINWqA99Xc8uElMFcROliwtz0n9pYej+g==",
+      "version": "1.0.30001173",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001173.tgz",
+      "integrity": "sha512-R3aqmjrICdGCTAnSXtNyvWYMK3YtV5jwudbq0T7nN9k4kmE4CBuwPqyJ+KBzepSTh0huivV2gLbSMEzTTmfeYw==",
       "dev": true
     },
     "canonical-path": {
       }
     },
     "codelyzer": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-6.0.0.tgz",
-      "integrity": "sha512-edJIQCIcxD9DhVSyBEdJ38AbLikm515Wl91t5RDGNT88uA6uQdTm4phTWfn9JhzAI8kXNUcfYyAE90lJElpGtA==",
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-6.0.1.tgz",
+      "integrity": "sha512-cOyGQgMdhnRYtW2xrJUNrNYDjEgwQ+BrE2y93Bwz3h4DJ6vJRLfupemU5N3pbYsUlBHJf0u1j1UGk+NLW4d97g==",
       "dev": true,
       "requires": {
         "@angular/compiler": "9.0.0",
           "dev": true
         },
         "tslib": {
-          "version": "1.13.0",
-          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
-          "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
+          "version": "1.14.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+          "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+          "dev": true
+        },
+        "zone.js": {
+          "version": "0.10.3",
+          "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.10.3.tgz",
+          "integrity": "sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg==",
           "dev": true
         }
       }
       "dev": true
     },
     "core-js-compat": {
-      "version": "3.8.0",
-      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.0.tgz",
-      "integrity": "sha512-o9QKelQSxQMYWHXc/Gc4L8bx/4F7TTraE5rhuN8I7mKBt5dBIUpXpIR3omv70ebr8ST5R3PqbDQr+ZI3+Tt1FQ==",
+      "version": "3.8.2",
+      "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.2.tgz",
+      "integrity": "sha512-LO8uL9lOIyRRrQmZxHZFl1RV+ZbcsAkFWTktn5SmH40WgLtSNYN4m4W2v9ONT147PxBY/XrRhrWq8TlvObyUjQ==",
       "dev": true,
       "requires": {
-        "browserslist": "^4.14.7",
+        "browserslist": "^4.16.0",
         "semver": "7.0.0"
       },
       "dependencies": {
       "dev": true
     },
     "css-selector-tokenizer": {
-      "version": "0.7.2",
-      "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.2.tgz",
-      "integrity": "sha512-yj856NGuAymN6r8bn8/Jl46pR+OC3eEvAhfGYDUe7YPtTPAYrSSw4oAniZ9Y8T5B92hjhwTBLUen0/vKPxf6pw==",
+      "version": "0.7.3",
+      "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz",
+      "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==",
       "dev": true,
       "requires": {
         "cssesc": "^3.0.0",
-        "fastparse": "^1.1.2",
-        "regexpu-core": "^4.6.0"
+        "fastparse": "^1.1.2"
       }
     },
     "css-tree": {
       },
       "dependencies": {
         "domelementtype": {
-          "version": "2.0.2",
-          "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.2.tgz",
-          "integrity": "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA==",
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz",
+          "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==",
           "dev": true
         }
       }
       "dev": true
     },
     "electron-to-chromium": {
-      "version": "1.3.610",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.610.tgz",
-      "integrity": "sha512-eFDC+yVQpEhtlapk4CYDPfV9ajF9cEof5TBcO49L1ETO+aYogrKWDmYpZyxBScMNe8Bo/gJamH4amQ4yyvXg4g==",
+      "version": "1.3.635",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.635.tgz",
+      "integrity": "sha512-RRriZOLs9CpW6KTLmgBqyUdnY0QNqqWs0HOtuQGGEMizOTNNn1P7sGRBxARnUeLejOsgwjDyRqT3E/CSst02ZQ==",
       "dev": true
     },
     "elliptic": {
       "dev": true
     },
     "errno": {
-      "version": "0.1.7",
-      "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
-      "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
+      "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
       "dev": true,
       "requires": {
         "prr": "~1.0.1"
       "dev": true
     },
     "fastq": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.9.0.tgz",
-      "integrity": "sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w==",
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.10.0.tgz",
+      "integrity": "sha512-NL2Qc5L3iQEsyYzweq7qfgy5OtXCmGzGvhElGEd/SoFWEMOEczNh5s5ocaF01HDetxz+p8ecjNPA6cZxxIHmzA==",
       "dev": true,
       "requires": {
         "reusify": "^1.0.4"
       "dev": true
     },
     "get-intrinsic": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz",
-      "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==",
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz",
+      "integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==",
       "dev": true,
       "requires": {
         "function-bind": "^1.1.1",
       "dev": true
     },
     "globby": {
-      "version": "11.0.1",
-      "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz",
-      "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==",
+      "version": "11.0.2",
+      "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz",
+      "integrity": "sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==",
       "dev": true,
       "requires": {
         "array-union": "^2.1.0",
       "dev": true
     },
     "html-entities": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz",
-      "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==",
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz",
+      "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==",
       "dev": true
     },
     "html-escaper": {
       }
     },
     "is-arguments": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
-      "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
-      "dev": true
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz",
+      "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.0"
+      }
     },
     "is-arrayish": {
       "version": "0.2.1",
       "dev": true
     },
     "is-negative-zero": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
-      "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=",
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz",
+      "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==",
       "dev": true
     },
     "is-number": {
       "dev": true
     },
     "js-yaml": {
-      "version": "3.14.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
-      "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
+      "version": "3.14.1",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+      "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
       "dev": true,
       "requires": {
         "argparse": "^1.0.7",
       }
     },
     "node-releases": {
-      "version": "1.1.67",
-      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.67.tgz",
-      "integrity": "sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg==",
+      "version": "1.1.69",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.69.tgz",
+      "integrity": "sha512-DGIjo79VDEyAnRlfSqYTsy+yoHd2IOjJiKUozD2MV2D85Vso6Bug56mb9tT/fY5Urt0iqk01H7x+llAruDR2zA==",
       "dev": true
     },
     "normalize-package-data": {
       }
     },
     "object-inspect": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
-      "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz",
+      "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==",
       "dev": true
     },
     "object-is": {
           }
         },
         "import-fresh": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz",
-          "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==",
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+          "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
           "dev": true,
           "requires": {
             "parent-module": "^1.0.0",
       "dev": true
     },
     "regenerate": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz",
-      "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==",
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
+      "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
       "dev": true
     },
     "regenerate-unicode-properties": {
       }
     },
     "regexpu-core": {
-      "version": "4.7.0",
-      "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz",
-      "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==",
+      "version": "4.7.1",
+      "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz",
+      "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==",
       "dev": true,
       "requires": {
         "regenerate": "^1.4.0",
       "dev": true
     },
     "resolve": {
-      "version": "1.17.0",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
-      "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+      "version": "1.19.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz",
+      "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==",
       "dev": true,
       "requires": {
+        "is-core-module": "^2.1.0",
         "path-parse": "^1.0.6"
       }
     },
       }
     },
     "stylus-loader": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-4.1.1.tgz",
-      "integrity": "sha512-Vnm7J/nIs/P6swIrdwJW/dflhsCOiFmb1U3PeQ6phRtg1soPLN4uKnnL7AtGIJDe173elbtYIXVzmCyF493CfA==",
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-4.3.1.tgz",
+      "integrity": "sha512-apDYJEM5ZpOAWbWInWcsbtI8gHNr/XYVcSY/tWqOUPt7M5tqhtwXVsAkgyiVjhuvw2Yrjq474a9H+g4d047Ebw==",
       "dev": true,
       "requires": {
         "fast-glob": "^3.2.4",
       "dev": true
     },
     "tapable": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.1.1.tgz",
-      "integrity": "sha512-Wib1S8m2wdpLbmQz0RBEVosIyvb/ykfKXf3ZIDqvWoMg/zTNm6G/tDSuUM61J1kNCDXWJrLHGSFeMhAG+gAGpQ==",
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz",
+      "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==",
       "dev": true
     },
     "tar": {
-      "version": "6.0.5",
-      "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.5.tgz",
-      "integrity": "sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==",
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz",
+      "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==",
       "dev": true,
       "requires": {
         "chownr": "^2.0.0",
       "dev": true
     },
     "tslib": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz",
-      "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g=="
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
+      "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A=="
     },
     "tslint": {
-      "version": "6.1.2",
-      "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.2.tgz",
-      "integrity": "sha512-UyNrLdK3E0fQG/xWNqAFAC5ugtFyPO4JJR1KyyfQAyzR8W0fTRrC91A8Wej4BntFzcvETdCSDa/4PnNYJQLYiA==",
+      "version": "6.1.3",
+      "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz",
+      "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.0.0",
         "mkdirp": "^0.5.3",
         "resolve": "^1.3.2",
         "semver": "^5.3.0",
-        "tslib": "^1.10.0",
+        "tslib": "^1.13.0",
         "tsutils": "^2.29.0"
       },
       "dependencies": {
           "dev": true
         },
         "tslib": {
-          "version": "1.13.0",
-          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
-          "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
+          "version": "1.14.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+          "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
           "dev": true
         }
       }
       },
       "dependencies": {
         "tslib": {
-          "version": "1.13.0",
-          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
-          "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
+          "version": "1.14.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+          "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
           "dev": true
         }
       }
       },
       "dependencies": {
         "mime": {
-          "version": "2.4.6",
-          "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
-          "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==",
+          "version": "2.4.7",
+          "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.7.tgz",
+          "integrity": "sha512-dhNd1uA2u397uQk3Nv5LM4lm93WYDUXFn3Fu291FJerns4jyTudqhIWe4W04YLy7Uk1tm1Ore04NpjRvQp/NPA==",
           "dev": true
         }
       }
index 06b66662a10f1a5e42ac55f94aff5496aa160115..d4445e55c39a84a18b929395ae5c0462ba068ec1 100644 (file)
   },
   "private": true,
   "dependencies": {
-    "@angular/animations": "~11.0.2",
+    "@angular/animations": "^11.0.7",
     "@angular/cdk": "^11.0.3",
-    "@angular/common": "~11.0.2",
-    "@angular/compiler": "~11.0.2",
-    "@angular/core": "~11.0.2",
-    "@angular/forms": "~11.0.2",
-    "@angular/platform-browser": "~11.0.2",
-    "@angular/platform-browser-dynamic": "~11.0.2",
-    "@angular/router": "~11.0.2",
-    "@fortawesome/fontawesome-free": "^5.13.1",
-    "@fortawesome/fontawesome-svg-core": "^1.2.29",
-    "@ng-bootstrap/ng-bootstrap": "^8.0.0",
+    "@angular/common": "^11.0.7",
+    "@angular/compiler": "^11.0.7",
+    "@angular/core": "^11.0.7",
+    "@angular/forms": "^11.0.7",
+    "@angular/platform-browser": "^11.0.7",
+    "@angular/platform-browser-dynamic": "^11.0.7",
+    "@angular/router": "^11.0.7",
+    "@fortawesome/fontawesome-free": "^5.15.1",
+    "@fortawesome/fontawesome-svg-core": "^1.2.32",
+    "@ng-bootstrap/ng-bootstrap": "^8.0.1",
     "@ngx-translate/core": "^13.0.0",
     "@ngx-translate/http-loader": "^6.0.0",
-    "bootstrap": "^4.5.0",
+    "bootstrap": "^4.5.3",
     "flag-icon-css": "^3.5.0",
     "jquery": "^3.5.1",
     "messageformat": "^2.3.0",
     "popper.js": "^1.16.1",
     "rxjs": "~6.6.3",
     "service": "^0.1.4",
-    "tslib": "^2.0.0",
-    "zone.js": "~0.10.3"
+    "tslib": "^2.1.0",
+    "zone.js": "^0.10.3"
   },
   "devDependencies": {
-    "@angular-devkit/build-angular": "~0.1100.2",
-    "@angular/cli": "~11.0.2",
-    "@angular/compiler-cli": "~11.0.2",
-    "@angular/localize": "^11.0.2",
+    "@angular-devkit/build-angular": "^0.1100.6",
+    "@angular/cli": "^11.0.6",
+    "@angular/compiler-cli": "^11.0.7",
+    "@angular/localize": "^11.0.7",
     "@types/jasmine": "~3.6.0",
     "@types/jasminewd2": "~2.0.3",
-    "@types/node": "^12.11.1",
-    "codelyzer": "^6.0.0",
+    "@types/node": "^12.19.12",
+    "codelyzer": "^6.0.1",
     "jasmine-core": "~3.6.0",
     "jasmine-spec-reporter": "~5.0.0",
     "karma": "~5.1.1",
@@ -54,7 +54,7 @@
     "karma-jasmine-html-reporter": "^1.5.0",
     "protractor": "~7.0.0",
     "ts-node": "~8.3.0",
-    "tslint": "~6.1.0",
+    "tslint": "^6.1.3",
     "typescript": "~4.0.5"
   }
 }
index 94b64369ce31a38b67bce33ab839637a7af18e14..eb01950c2d439148c924ac832d379e97297e0107 100644 (file)
@@ -18,8 +18,9 @@
 
 export class LdapConfiguration {
     host_name : string = "";
-    port : number = -1;
+    port : number = 389;
     ssl_enabled : boolean  = false;
+    context_factory: string = "";
     base_dn : string = "";
     groups_base_dn : string = "";
     bind_dn : string = "";
@@ -29,4 +30,5 @@ export class LdapConfiguration {
     use_role_name_as_group : boolean = false;
     properties : Map<string,string> = new Map<string, string>()
     writable : boolean = false;
+    available_context_factories : string[];
 }
index a4ebbd5a881db8311702c6d849897d9cb7bd7f2a..78fe8fb3e9c95631ce0f61c69428af5199a650cb 100644 (file)
@@ -41,6 +41,9 @@ export class HttpErrorInterceptor implements HttpInterceptor {
     ): Observable<HttpEvent<any>> {
         return next.handle(request).pipe(
             catchError((error: HttpErrorResponse) => {
+                if (error.url.includes('security/config/ldap/verify')) {
+                    return  throwError(error);
+                }
                 console.error("Error from HTTP error interceptor", error);
                 if (error.status==0 && error.statusText=="Unknown Error") {
                     console.log("Unknown error");
index e982398949c8d8731d6e8493615bfe516ab3ed6b..c6850e3307ae66cd3d34ece5771b2c9e20eb134a 100644 (file)
@@ -18,7 +18,7 @@
 
 <form class="mt-3 mb-3" [formGroup]="userForm" (ngSubmit)="onSubmit()">
     <p class="row col-md-10">{{'security.config.ldap.explain'|translate}}</p>
-    <div class="form-group row col-md-10" *ngFor="let attName of ['host_name','port','base_dn','groups_base_dn','bind_dn','bind_password']">
+    <div class="form-group row col-md-10" *ngFor="let attName of ['host_name','port','base_dn','groups_base_dn','bind_dn','bind_password','context_factory']">
         <label class="col-md-3 col-form-label" for="{{attName}}">{{'security.config.ldap.attributes.'+attName |translate}}</label>
         <div [attr.class]="attName=='port'?'col-md-3':'col-md-7'">
             <input [attr.type]="attName=='bind_password'?'password':'text'" formControlName="{{attName}}" id="{{attName}}"
@@ -37,7 +37,7 @@
     <div class="form-group row col-md-10">
         <div class="col-md-3">{{'security.config.ldap.flags'|translate}}</div>
         <div class="col-md-7">
-            <div class="form-check" *ngFor="let flagName of ['writable','ssl_enabled','bind_authenticator_enabled','use_role_name_as_group']">
+            <div class="form-check pt-1 pb-1" *ngFor="let flagName of ['writable','ssl_enabled','bind_authenticator_enabled','use_role_name_as_group']">
                 <input class="form-check-input" type="checkbox" formControlName="{{flagName}}"
                        id="{{flagName}}" >
                 <label class="form-check-label " for="{{flagName}}">
         </div>
     </div>
 
+    <div class="row col-md-10 mt-4" >
+        <button class="btn btn-primary col-md-2" type="submit"
+                [disabled]="userForm.invalid || !userForm.dirty">{{'form.button.save'|translate}}</button>
+        <button class="btn btn-primary col-md-2 offset-1" type="button" (click)="checkLdapConnection()"
+                [disabled]="userForm.invalid || !userForm.dirty">{{'form.button.check'|translate}}</button>
+    </div>
+
+
 </form>
+
index 85bcf6b6a5e0b099c787197b777e1052c52d269b..d29e5e7f60af3623b3ddf99c9b2443352e091539 100644 (file)
  * under the License.
  */
 
-import { ComponentFixture, TestBed } from '@angular/core/testing';
+import {ComponentFixture, TestBed} from '@angular/core/testing';
 
-import { LdapSecurityComponent, dnValidator } from './ldap-security.component';
-import {ValidatorFn} from "@angular/forms";
+import {LdapSecurityComponent} from './ldap-security.component';
+import {FormBuilder} from "@angular/forms";
+import {RouterTestingModule} from '@angular/router/testing';
+import {NO_ERRORS_SCHEMA} from "@angular/core";
+import {ActivatedRoute, Router} from "@angular/router";
+import {HttpClientTestingModule} from "@angular/common/http/testing";
+import {TranslateModule} from "@ngx-translate/core";
 
 describe('LdapSecurityComponent', () => {
   let component: LdapSecurityComponent;
   let fixture: ComponentFixture<LdapSecurityComponent>;
+  let router;
+  let route;
 
   beforeEach(async () => {
     await TestBed.configureTestingModule({
-      declarations: [ LdapSecurityComponent ]
+      declarations: [ LdapSecurityComponent ],
+      providers: [FormBuilder],
+      schemas:[NO_ERRORS_SCHEMA],
+      imports:[
+        TranslateModule.forRoot(),
+        RouterTestingModule.withRoutes([]),
+          HttpClientTestingModule
+      ]
     })
     .compileComponents();
   });
@@ -36,6 +50,8 @@ describe('LdapSecurityComponent', () => {
     fixture = TestBed.createComponent(LdapSecurityComponent);
     component = fixture.componentInstance;
     fixture.detectChanges();
+    router = TestBed.get(Router);
+    route = TestBed.get(ActivatedRoute)
   });
 
   it('should create', () => {
@@ -43,15 +59,30 @@ describe('LdapSecurityComponent', () => {
   });
 
   describe('Test Custom DN Validator', () => {
-    it('check valid 1', () => {
+    it('valid 1', () => {
       const ctrl = component.userForm.controls['base_dn']
       ctrl.setValue('cn=abc');
       expect(ctrl.valid).toBeTruthy();
     })
-    it('check invalid 1', () => {
+    it('valid 2', () => {
+      const ctrl = component.userForm.controls['base_dn']
+      ctrl.setValue('cn=abc,dc=abc');
+      expect(ctrl.valid).toBeTruthy()
+    })
+    it('valid with space', () => {
+      const ctrl = component.userForm.controls['base_dn']
+      ctrl.setValue('cn=abc , dc=abc');
+      expect(ctrl.valid).toBeTruthy()
+    })
+    it('invalid postfix', () => {
       const ctrl = component.userForm.controls['base_dn']
       ctrl.setValue('cn=abc,');
       expect(ctrl.invalid).toBeTruthy()
     })
+    it('invalid RDN', () => {
+      const ctrl = component.userForm.controls['base_dn']
+      ctrl.setValue('cn=abc,dc,dc=abc');
+      expect(ctrl.invalid).toBeTruthy()
+    })
   });
 });
index 7cc829eebc453a88a334d24de284a67de091ddfb..bc00ad87382831fc38707477ee4fc907cb6576f3 100644 (file)
  * under the License.
  */
 
-import { Component, OnInit } from '@angular/core';
+import {Component, OnInit} from '@angular/core';
 import {EditBaseComponent} from "@app/modules/shared/edit-base.component";
 import {LdapConfiguration} from "@app/model/ldap-configuration";
 import {ActivatedRoute} from "@angular/router";
 import {AbstractControl, FormBuilder, ValidatorFn, Validators} from "@angular/forms";
 import {SecurityService} from "@app/services/security.service";
 import {ToastService} from "@app/services/toast.service";
-import {propertyDescriptorPatch} from "zone.js/lib/browser/property-descriptor";
+import {ErrorResult} from "@app/model/error-result";
 
 @Component({
   selector: 'app-ldap-security',
@@ -33,6 +33,9 @@ import {propertyDescriptorPatch} from "zone.js/lib/browser/property-descriptor";
 export class LdapSecurityComponent extends EditBaseComponent<LdapConfiguration> implements OnInit {
 
   authenticationMethods=['none','simple','strong']
+  formFields = ['host_name', 'port', 'ssl_enabled', 'context_factory',
+    'base_dn', 'groups_base_dn', 'bind_dn', 'bind_password', 'authentication_method', 'bind_authenticator_enabled',
+    'use_role_name_as_group', 'writable'];
 
   constructor(private route: ActivatedRoute,
               public fb: FormBuilder, private securityService: SecurityService, private toastService: ToastService) {
@@ -41,9 +44,10 @@ export class LdapSecurityComponent extends EditBaseComponent<LdapConfiguration>
       host_name:[''],
       port:['', [Validators.min(1), Validators.max(65535)]],
       ssl_enabled:[false],
+      context_factory:[''],
       base_dn:['',[dnValidator()]],
       groups_base_dn:['',[dnValidator()]],
-      bind_dn:['',[dnValidator()]],
+      bind_dn:[''],
       bind_password:[''],
       authentication_method:['none'],
       bind_authenticator_enabled:[false],
@@ -53,6 +57,23 @@ export class LdapSecurityComponent extends EditBaseComponent<LdapConfiguration>
   }
 
   ngOnInit(): void {
+    this.securityService.getLdapConfiguration().subscribe( ldapConfiguration => {
+          this.copyToForm(this.formFields, ldapConfiguration);
+          if ((ldapConfiguration.context_factory==null || ldapConfiguration.context_factory=='') && ldapConfiguration.available_context_factories.length==1) {
+            this.userForm.controls['context_factory'].setValue(ldapConfiguration.available_context_factories[0]);
+          }
+          if (ldapConfiguration.authentication_method=='') {
+            this.userForm.controls['authentication_method'].setValue('none');
+          }
+        }
+    )
+    this.userForm.controls['bind_dn'].valueChanges.subscribe(selectedValue => {
+      if (selectedValue!='' && this.userForm.controls['authentication_method'].value=='none') {
+          this.userForm.controls['authentication_method'].setValue('simple',{emitEvent: false})
+      } else if (selectedValue=='' && this.userForm.controls['authentication_method'].value!='none') {
+        this.userForm.controls['authentication_method'].setValue('none',{emitEvent: false})
+      }
+    })
   }
 
   createEntity(): LdapConfiguration {
@@ -60,6 +81,11 @@ export class LdapSecurityComponent extends EditBaseComponent<LdapConfiguration>
   }
 
   onSubmit() {
+    console.log("Saving configuration");
+    let config = this.copyFromForm(this.formFields)
+
+
+
   }
 
   getInputClasses(field: string) : string[] {
@@ -68,15 +94,32 @@ export class LdapSecurityComponent extends EditBaseComponent<LdapConfiguration>
       csClasses = [];
     }
     csClasses.push('form-control');
-    console.log("Classes "+field+" " + csClasses);
     return csClasses;
   }
+
+  checkLdapConnection() {
+    console.log("Checking LDAP connection");
+    let config = this.copyFromForm(this.formFields)
+    this.securityService.verifyLdapConfiguration(config).subscribe(()=>{
+      this.toastService.showSuccessByKey('ldap-security', 'security.config.ldap.check_success');
+    },
+        (error:ErrorResult) =>{
+          this.toastService.showErrorByKey('ldap-security', error.firstMessageString());
+        }
+        );
+  }
 }
 
+/**
+ * This validator checks the DN names for valid RDN segments
+ */
 export function dnValidator(): ValidatorFn {
   return (control: AbstractControl): {[key: string]: any} | null => {
     let parts = []
     let value = control.value.toString()
+    if (value=='') {
+      return null;
+    }
     let escape = false;
     let partKey : string = ''
     let partValue : string = ''
@@ -113,6 +156,9 @@ export function dnValidator(): ValidatorFn {
       }
 
     }
+    if (partKey=='' || partValue=='') {
+      return {'invalidDnBadRdn':{value:value,index:value.length-1}}
+    }
     return null;
   };
 }
index 954d448427386edea9cb3689429c393cc2c34097..8758901b9302a65695aaa81de942e67b90b28c05 100644 (file)
@@ -23,6 +23,7 @@ import {Observable, throwError} from "rxjs";
 import {catchError} from "rxjs/operators";
 import {HttpErrorResponse, HttpResponse} from "@angular/common/http";
 import {BeanInformation} from "@app/model/bean-information";
+import {LdapConfiguration} from "@app/model/ldap-configuration";
 
 @Injectable({
   providedIn: 'root'
@@ -66,4 +67,21 @@ export class SecurityService {
         );
     }
 
+    getLdapConfiguration() : Observable<LdapConfiguration> {
+        return this.rest.executeRestCall<LdapConfiguration>("get", "archiva", "security/config/ldap", null).pipe(
+            catchError((error: HttpErrorResponse) => {
+                return throwError(this.rest.getTranslatedErrorResult(error));
+            })
+        );
+    }
+
+    verifyLdapConfiguration(ldapConfig : LdapConfiguration) : Observable<HttpResponse<any>> {
+        return this.rest.executeResponseCall<any>("post", "archiva", "security/config/ldap/verify", ldapConfig).pipe(
+            catchError((error: HttpErrorResponse) => {
+                return throwError(this.rest.getTranslatedErrorResult(error));
+            })
+        );
+    }
+
+
 }
index 5bffc14122d165685494daba51d1607cc65c6949..4f0b01996ae5439435799da840e2f6d28e520e8a 100644 (file)
     "user.password.violation.numeric": "You must provide a password containing at least {arg0} numeric {arg0, plural, one {character} other {characters}}.",
     "user.password.violation.reuse": "The password must not match any of the previous {arg0} {arg0, plural, one {password} other {passwords}}.",
     "user.password.violation.alphanum.only": "You must provide a password containing all alpha-numeric characters.",
-    "user.password.violation.whitespace.detected": "You must provide a password without whitespace characters."
+    "user.password.violation.whitespace.detected": "You must provide a password without whitespace characters.",
+    "archiva.ldap.communication_error": "Communication to LDAP server failed: {arg0}",
+    "archiva.ldap.invalid_name": "There was a invalid name: {arg0}",
+    "archiva.ldap.generic_error":  "LDAP Error: {arg0}",
+    "archiva.ldap.service_unavailable": "LDAP Server not available: {arg0}",
+    "archiva.ldap.authentication.failed": "LDAP Authentication failed: {arg0}",
+    "archiva.ldap.authentication.not_supported": "LDAP Authentication not supported: {arg0}",
+    "archiva.ldap.no_permissions":  "No permission on LDAP server: {arg0}"
   },
   "users": {
     "attributes": {
           "port": "Port",
           "base_dn": "Base DN",
           "groups_base_dn": "Base DN for Group Search",
-          "bind_dn": "Bind User DN",
+          "bind_dn": "Bind User",
           "bind_password":"Bind Password",
           "authentication_method": "Method used for Bind Authentication",
-          "bind_authenticator_enabled": "Use LDAP Bind during archiva authentication",
+          "bind_authenticator_enabled": "Use LDAP Bind for Archiva authentication",
           "use_role_name_as_group": "Archiva role names are LDAP group names",
           "writable": "Bind User can write to LDAP Server",
-          "ssl_enabled": "Enable SSL"
+          "ssl_enabled": "Enable SSL",
+          "context_factory": "Context Factory"
 
         },
         "flags": "Flags"
     "button": {
       "yes": "Yes",
       "no": "No",
-      "save": "Save Changes"
+      "save": "Save Changes",
+      "check": "Check configuration"
     },
     "edit": "Edit",
     "emptyContent": "No values",