001/*
002 *   Licensed to the Apache Software Foundation (ASF) under one
003 *   or more contributor license agreements.  See the NOTICE file
004 *   distributed with this work for additional information
005 *   regarding copyright ownership.  The ASF licenses this file
006 *   to you under the Apache License, Version 2.0 (the
007 *   "License"); you may not use this file except in compliance
008 *   with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *   Unless required by applicable law or agreed to in writing,
013 *   software distributed under the License is distributed on an
014 *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *   KIND, either express or implied.  See the License for the
016 *   specific language governing permissions and limitations
017 *   under the License.
018 *
019 */
020
021package org.apache.directory.server.config.listener;
022
023
024import static org.apache.directory.server.core.api.InterceptorEnum.AUTHENTICATION_INTERCEPTOR;
025
026import org.apache.directory.api.ldap.model.entry.Attribute;
027import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
028import org.apache.directory.api.ldap.model.entry.Entry;
029import org.apache.directory.api.ldap.model.exception.LdapException;
030import org.apache.directory.api.ldap.model.name.Dn;
031import org.apache.directory.api.ldap.model.schema.AttributeType;
032import org.apache.directory.api.ldap.model.schema.SchemaManager;
033import org.apache.directory.server.config.ConfigPartitionReader;
034import org.apache.directory.server.config.beans.PasswordPolicyBean;
035import org.apache.directory.server.config.builder.ServiceBuilder;
036import org.apache.directory.server.core.api.DirectoryService;
037import org.apache.directory.server.core.api.authn.ppolicy.PasswordPolicyConfiguration;
038import org.apache.directory.server.core.api.event.DirectoryListenerAdapter;
039import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
040import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext;
041import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
042import org.apache.directory.server.core.authn.AuthenticationInterceptor;
043import org.apache.directory.server.core.authn.ppolicy.PpolicyConfigContainer;
044import org.slf4j.Logger;
045import org.slf4j.LoggerFactory;
046
047
048/**
049 * A listener for handling the config partition changes.
050 * 
051 * Note: currently handles password policy related configuration changes only.
052 * 
053 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
054 */
055public class ConfigChangeListener extends DirectoryListenerAdapter
056{
057    /** the config parition reader */
058    private ConfigPartitionReader cpReader;
059
060    /** container holding the current active password policy configurations */
061    private PpolicyConfigContainer ppolicyConfigContainer;
062
063    /** the root DN of password policy configurations */
064    private Dn ppolicyConfigDnRoot;
065
066    private static final String PPOLICY_OC_NAME = "ads-passwordPolicy";
067
068    // attribute holding the value of #PPOLICY_OC_NAME
069    private Attribute passwordPolicyObjectClassAttribute;
070    
071    
072    /** The logger for this class */
073    private static final Logger LOG = LoggerFactory.getLogger( ConfigChangeListener.class );
074
075    /**
076     * 
077     * Creates a new instance of ConfigChangeListener.
078     *
079     * @param cpReader the configuration reader
080     * @param directoryService the DirectoryService instance
081     * @throws LdapException If the instance cannot be created
082     */
083    public ConfigChangeListener( ConfigPartitionReader cpReader, DirectoryService directoryService )
084        throws LdapException
085    {
086        this.cpReader = cpReader;
087
088        SchemaManager schemaManager = directoryService.getSchemaManager();
089
090        ppolicyConfigDnRoot = new Dn( schemaManager,
091            "ou=passwordPolicies,ads-interceptorId=authenticationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config" );
092
093        AuthenticationInterceptor authInterceptor = ( AuthenticationInterceptor ) directoryService
094            .getInterceptor( AUTHENTICATION_INTERCEPTOR.getName() );
095        ppolicyConfigContainer = authInterceptor.getPwdPolicyContainer();
096
097        AttributeType ocType = directoryService.getAtProvider().getObjectClass();
098        passwordPolicyObjectClassAttribute = new DefaultAttribute( ocType, PPOLICY_OC_NAME );
099    }
100
101
102    @Override
103    public void entryAdded( AddOperationContext addContext )
104    {
105        Entry entry = addContext.getEntry();
106        updatePasswordPolicy( entry, false );
107    }
108
109
110    @Override
111    public void entryDeleted( DeleteOperationContext deleteContext )
112    {
113        Entry entry = deleteContext.getEntry();
114        updatePasswordPolicy( entry, true );
115    }
116
117
118    @Override
119    public void entryModified( ModifyOperationContext modifyContext )
120    {
121        Entry entry = modifyContext.getAlteredEntry();
122        updatePasswordPolicy( entry, false );
123    }
124
125
126    /**
127     * Updates the password policy represented by the given configuration entry
128     * 
129     * @param entry the password policy configuration entry
130     * @param deleted flag to detect if this is a deleted entry
131     */
132    private void updatePasswordPolicy( Entry entry, boolean deleted )
133    {
134        Dn dn = entry.getDn();
135
136        if ( !dn.isDescendantOf( ppolicyConfigDnRoot ) )
137        {
138            return;
139        }
140
141        if ( !entry.contains( passwordPolicyObjectClassAttribute ) )
142        {
143            return;
144        }
145
146        if ( deleted )
147        {
148            LOG.debug( "Deleting ppolicy config {}", dn );
149            ppolicyConfigContainer.removePolicyConfig( dn );
150            return;
151        }
152        
153        PasswordPolicyBean bean = null;
154        
155        try
156        {
157            bean = ( PasswordPolicyBean ) cpReader.readConfig( entry );
158        }
159        catch ( Exception e )
160        {
161            LOG.warn( "Failed to read the updated ppolicy configuration from {}", dn );
162            LOG.warn( "", e );
163            return;
164        }
165
166        if ( bean.isDisabled() )
167        {
168            LOG.debug( "Deleting disabled ppolicy config {}", dn );
169            ppolicyConfigContainer.removePolicyConfig( dn );
170        }
171        else
172        {
173            PasswordPolicyConfiguration updated = ServiceBuilder.createPwdPolicyConfig( bean );
174            
175            PasswordPolicyConfiguration existing = ppolicyConfigContainer.getPolicyConfig( dn );
176            
177            if ( existing == null )
178            {
179                LOG.debug( "Adding ppolicy config {}", dn );
180            }
181            else
182            {
183                LOG.debug( "Updating ppolicy config {}", dn );
184            }
185            
186            ppolicyConfigContainer.addPolicy( dn, updated );
187        }
188    }
189}