blob: 8db1272660521f2128ba68440f2b7563488600af [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.directory.fortress.core.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.commons.lang.StringUtils;
import org.apache.directory.api.ldap.model.constants.SchemaConstants;
import org.apache.directory.api.ldap.model.entry.*;
import org.apache.directory.api.ldap.model.exception.LdapEntryAlreadyExistsException;
import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.api.ldap.model.exception.LdapNoSuchObjectException;
import org.apache.directory.fortress.core.CreateException;
import org.apache.directory.fortress.core.FinderException;
import org.apache.directory.fortress.core.GlobalErrIds;
import org.apache.directory.fortress.core.GlobalIds;
import org.apache.directory.fortress.core.RemoveException;
import org.apache.directory.fortress.core.UpdateException;
import org.apache.directory.fortress.core.ldap.LdapDataProvider;
import org.apache.directory.fortress.core.model.Configuration;
import org.apache.directory.fortress.core.util.PropUtil;
import org.apache.directory.fortress.core.util.Config;
import org.apache.directory.ldap.client.api.LdapConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class provides data access for the standard ldap object device that has been extended to support name/value pairs.
* Fortress uses this data structure to store its remote cfg parameters.
* <p>
* The Fortress Config node is a combination of:
* <p>
* 'device' Structural Object Class is used to store basic attributes like cn which will be used for config node name.
* <ul>
* <li> ------------------------------------------
* <li> <code>objectclass ( 2.5.6.14 NAME 'device'</code>
* <li> <code>DESC 'RFC2256: a device''</code>
* <li> <code>SUP top STRUCTURAL</code>
* <li> <code>MUST cn</code>
* <li> <code>MAY ( serialNumber $ seeAlso $ owner $ ou $ o $ l $ description ) )</code>
* <li> ------------------------------------------
* </ul>
* <p>
* 'ftProperties' AUXILIARY Object Class is used to store name/value pairs on target node.<br>
* <code>This aux object class can be used to store custom attributes.</code><br>
* <code>The properties collections consist of name/value pairs and are not constrainted by Fortress.</code><br>
* <ul>
* <li> ------------------------------------------
* <li> <code>objectclass ( 1.3.6.1.4.1.38088.3.2</code>
* <li> <code>NAME 'ftProperties'</code>
* <li> <code>DESC 'Fortress Properties AUX Object Class'</code>
* <li> <code>AUXILIARY</code>
* <li> <code>MAY ( ftProps ) ) </code>
* <li> ------------------------------------------
* </ul>
* <p>
* This class is thread safe.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
final class ConfigDAO extends LdapDataProvider
{
private static final String CLS_NM = ConfigDAO.class.getName();
private static final Logger LOG = LoggerFactory.getLogger( CLS_NM );
private String CONFIG_ROOT_DN;
public static final String GID_NUMBER_SEQUENCE = "ftGidNumber";
public static final String UID_NUMBER_SEQUENCE = "ftUidNumber";
private final String[] CONFIG_OBJ_CLASS =
{
SchemaConstants.DEVICE_OC, GlobalIds.PROPS_AUX_OBJECT_CLASS_NAME, GlobalIds.FT_CONFIG_AUX_OBJECT_CLASS_NAME
};
private final String[] POSIX_IDS =
{
GID_NUMBER_SEQUENCE, UID_NUMBER_SEQUENCE
};
private final String[] CONFIG_ATRS =
{
SchemaConstants.CN_AT, GlobalIds.PROPS, GID_NUMBER_SEQUENCE, UID_NUMBER_SEQUENCE
};
/**
* Package private default constructor.
*/
ConfigDAO()
{
super();
CONFIG_ROOT_DN = Config.getInstance().getProperty( GlobalIds.CONFIG_ROOT_PARAM );
}
/**
* Create a new configuration node and load it with properties.
* @param cfg the new configuration node to be created in ldap.
* @return what was just added.
* @throws org.apache.directory.fortress.core.CreateException
*/
Configuration create( Configuration cfg )
throws CreateException
{
LdapConnection ld = null;
String dn = getDn( cfg.getName() );
LOG.info( "create dn [{}]", dn );
try
{
Entry myEntry = new DefaultEntry( dn );
myEntry.add( SchemaConstants.OBJECT_CLASS_AT, CONFIG_OBJ_CLASS );
myEntry.add( SchemaConstants.CN_AT, cfg.getName() );
loadProperties( cfg.getProperties(), myEntry, GlobalIds.PROPS );
// These attributes hold sequence numbers:
if (StringUtils.isNotEmpty(cfg.getUidNumber()))
{
myEntry.add( UID_NUMBER_SEQUENCE, cfg.getUidNumber() );
}
else
{
myEntry.add( UID_NUMBER_SEQUENCE, "0" );
}
if (StringUtils.isNotEmpty(cfg.getGidNumber()))
{
myEntry.add( GID_NUMBER_SEQUENCE, cfg.getGidNumber() );
}
else
{
myEntry.add( GID_NUMBER_SEQUENCE, "0" );
}
ld = getAdminConnection();
add( ld, myEntry, cfg );
}
catch ( LdapEntryAlreadyExistsException e )
{
String warning = "create config dn [" + dn + "] caught LdapEntryAlreadyExistsException="
+ e;
throw new CreateException( GlobalErrIds.FT_CONFIG_ALREADY_EXISTS, warning, e );
}
catch ( LdapException e )
{
String error;
error = "create config dn [" + dn + "] caught LDAPException=" + e;
LOG.error( error, e );
throw new CreateException( GlobalErrIds.FT_CONFIG_CREATE_FAILED, error, e );
}
finally
{
closeAdminConnection( ld );
}
return cfg;
}
/**
* Update existing node with new properties.
* @param cfg contains the name and each property name value will loaded into an attribute (ftprops) under the config node.
* @return the updated entity.
* @throws org.apache.directory.fortress.core.UpdateException
*/
Configuration update( Configuration cfg )
throws UpdateException
{
LdapConnection ld = null;
String dn = getDn( cfg.getName() );
LOG.debug( "update dn [{}]", dn );
try
{
List<Modification> mods = new ArrayList<>();
if ( PropUtil.isNotEmpty( cfg.getProperties() ) )
{
loadProperties( cfg.getProperties(), mods, GlobalIds.PROPS, false );
}
if (StringUtils.isNotEmpty(cfg.getUidNumber()))
{
mods.add(new DefaultModification(ModificationOperation.REPLACE_ATTRIBUTE, UID_NUMBER_SEQUENCE,
cfg.getUidNumber()));
}
if (StringUtils.isNotEmpty(cfg.getGidNumber()))
{
mods.add(new DefaultModification(ModificationOperation.REPLACE_ATTRIBUTE, GID_NUMBER_SEQUENCE,
cfg.getGidNumber()));
}
if ( mods.size() > 0 )
{
ld = getAdminConnection();
modify( ld, dn, mods, cfg );
}
}
catch ( LdapException e )
{
String error = "update dn [" + dn + "] caught LDAPException=" + e;
throw new UpdateException( GlobalErrIds.FT_CONFIG_UPDATE_FAILED, error, e );
}
finally
{
closeAdminConnection( ld );
}
return cfg;
}
/**
* This method will update a single property with a new value.
*
* @param name of the config node, mostly likely 'DEFAULT'.
* @param key used for the property.
* @param value this is old value to be replaced with newValue.
* @param newValue new value for the property
* @throws UpdateException in the event the attribute can't be replaced.
* @throws FinderException in the event the config node and/or property key:value can't be located.
*/
void updateProperty( String name, String key, String value, String newValue ) throws UpdateException
{
LdapConnection ld = null;
String dn = getDn( name );
LOG.debug( "update dn [{}], key [{}], value [{}], newValue [{}]", dn, key, value, newValue );
try
{
List<Modification> mods = new ArrayList<Modification>();
mods.add( new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE, GlobalIds.PROPS, key + GlobalIds.PROP_SEP + value ) );
mods.add( new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, GlobalIds.PROPS, key + GlobalIds.PROP_SEP + newValue ) );
ld = getAdminConnection();
modify( ld, dn, mods );
}
catch ( LdapException e )
{
String error = "updateProperty dn [" + dn + "] caught LDAPException=" + e;
throw new UpdateException( GlobalErrIds.FT_CONFIG_UPDATE_FAILED, error, e );
}
finally
{
closeAdminConnection( ld );
}
}
/**
* Remove the configuration node from the directory.
* @param name of the node to be removed.
* @throws org.apache.directory.fortress.core.RemoveException
*/
void remove( String name )
throws RemoveException
{
LdapConnection ld = null;
String dn = getDn( name );
LOG.info( "remove dn [{}]", dn );
try
{
ld = getAdminConnection();
delete( ld, dn );
}
catch ( LdapException e )
{
String error = "remove dn [" + dn + "] LDAPException=" + e;
throw new RemoveException( GlobalErrIds.FT_CONFIG_DELETE_FAILED, error, e );
}
finally
{
closeAdminConnection( ld );
}
}
/**
* A set of properties from a configuration node.
* @param name the name of config node.
* @param props each name/value will be a ftprop attribute to remove.
* @return
* @throws org.apache.directory.fortress.core.UpdateException
*/
Properties remove( String name, Properties props )
throws UpdateException
{
LdapConnection ld = null;
String dn = getDn( name );
LOG.info( "remove props dn [{}]", dn );
try
{
List<Modification> mods = new ArrayList<>();
if ( PropUtil.isNotEmpty( props ) )
{
removeProperties( props, mods, GlobalIds.PROPS );
}
if ( mods.size() > 0 )
{
ld = getAdminConnection();
modify( ld, dn, mods );
}
}
catch ( LdapException e )
{
String error = "remove props dn [" + dn + "] caught LDAPException=" + e;
throw new UpdateException( GlobalErrIds.FT_CONFIG_DELETE_PROPS_FAILED, error, e );
}
finally
{
closeAdminConnection( ld );
}
return props;
}
/**
* @param name
* @return the existing config node.
* @throws org.apache.directory.fortress.core.FinderException
*/
Configuration getConfig( String name )
throws FinderException
{
Configuration configuration = new Configuration();
LdapConnection ld = null;
String dn = getDn( name );
LOG.info( "getConfig dn [{}]", dn );
try
{
ld = getAdminConnection();
Entry findEntry = read( ld, dn, CONFIG_ATRS );
configuration.setName( name );
configuration.addProperties( PropUtil.getProperties( getAttributes( findEntry, GlobalIds.PROPS ) ) );
configuration.setUidNumber( getAttribute( findEntry, UID_NUMBER_SEQUENCE ) );
configuration.setGidNumber( getAttribute( findEntry, GID_NUMBER_SEQUENCE ) );
}
catch ( LdapNoSuchObjectException e )
{
String warning = "getConfig COULD NOT FIND ENTRY for dn [" + dn + "]";
throw new FinderException( GlobalErrIds.FT_CONFIG_NOT_FOUND, warning, e );
}
catch ( LdapException e )
{
String error = "getConfig dn [" + dn + "] caught LdapException=" + e;
throw new FinderException( GlobalErrIds.FT_CONFIG_READ_FAILED, error, e );
}
finally
{
closeAdminConnection( ld );
}
return configuration;
}
/**
*
* @param name
* @return
* @throws FinderException
*/
Configuration getPosixIds( String name )
throws FinderException
{
Configuration configuration = new Configuration();
LdapConnection ld = null;
String dn = getDn( name );
LOG.debug( "getPosixIds dn [{}]", dn );
try
{
ld = getAdminConnection();
Entry findEntry = read( ld, dn, POSIX_IDS );
configuration.setName( name );
configuration.setUidNumber( getAttribute( findEntry, UID_NUMBER_SEQUENCE ) );
configuration.setGidNumber( getAttribute( findEntry, GID_NUMBER_SEQUENCE ) );
}
catch ( LdapNoSuchObjectException e )
{
String warning = "getPosixIds COULD NOT FIND ENTRY for dn [" + dn + "]";
throw new FinderException( GlobalErrIds.FT_CONFIG_NOT_FOUND, warning, e );
}
catch ( LdapException e )
{
String error = "getPosixIds dn [" + dn + "] caught LdapException=" + e;
throw new FinderException( GlobalErrIds.FT_CONFIG_READ_FAILED, error, e );
}
finally
{
closeAdminConnection( ld );
}
return configuration;
}
/**
*
* @param name
* @return
*/
private String getDn( String name )
{
return SchemaConstants.CN_AT + "=" + name + "," + CONFIG_ROOT_DN;
}
}