Package org.wso2.carbon.apacheds.impl

Source Code of org.wso2.carbon.apacheds.impl.ApacheDirectoryPartitionManager

/*
*  Copyright (c) 2005-2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
*  WSO2 Inc. 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.wso2.carbon.apacheds.impl;

import org.apache.axiom.om.util.Base64;
import org.apache.directory.server.core.CoreSession;
import org.apache.directory.server.core.DirectoryService;
import org.apache.directory.server.core.factory.JdbmPartitionFactory;
import org.apache.directory.server.core.factory.PartitionFactory;
import org.apache.directory.server.core.interceptor.Interceptor;
import org.apache.directory.server.core.partition.Partition;
import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
import org.apache.directory.server.kerberos.shared.store.KerberosAttribute;
import org.apache.directory.server.xdbm.Index;
import org.apache.directory.shared.ldap.entry.ServerEntry;
import org.apache.directory.shared.ldap.exception.LdapException;
import org.apache.directory.shared.ldap.exception.LdapInvalidDnException;
import org.apache.directory.shared.ldap.name.DN;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.apacheds.AdminGroupInfo;
import org.wso2.carbon.apacheds.AdminInfo;
import org.wso2.carbon.apacheds.PartitionInfo;
import org.wso2.carbon.apacheds.PartitionManager;
import org.wso2.carbon.apacheds.PasswordAlgorithm;
import org.wso2.carbon.apacheds.exception.DirectoryServerException;

import javax.naming.NamingException;
import java.io.File;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
* ApacheDS implementation of LDAP server. Contains methods to manipulate LDAP partitions.
*/
class ApacheDirectoryPartitionManager implements PartitionManager {

    private DirectoryService directoryService = null;
    private String workingDirectory;
    private PartitionFactory partitionFactory = null;
    /*Partition cache size is expressed as number of entries*/
    private static final int PARTITION_CACHE_SIZE = 500;

    private static final Logger logger = LoggerFactory.getLogger(
            ApacheDirectoryPartitionManager.class);

    public ApacheDirectoryPartitionManager(DirectoryService directoryService, String wd) {
        this.directoryService = directoryService;
        this.workingDirectory = wd;
        this.partitionFactory = new JdbmPartitionFactory();
    }

    /**
     * @inheritDoc
     */
    public void addPartition(PartitionInfo partitionInformation)
            throws DirectoryServerException {

        try {
            JdbmPartition partition = createNewPartition(partitionInformation.getPartitionId(),
                                                         partitionInformation.getRootDN());
            this.directoryService.addPartition(partition);

            CoreSession adminSession = this.directoryService.getAdminSession();

            if (!adminSession.exists(partition.getSuffixDn())) {

                addPartitionAttributes(partitionInformation.getRootDN(), partitionInformation.
                        getObjectClasses(), partitionInformation.getRealm(),
                                       partitionInformation.getPreferredDomainComponent());

                // Create user ou
                addUserStoreToPartition(partition.getSuffix());

                // Create group ou
                addGroupStoreToPartition(partition.getSuffix());
                // create tenant administrator entry at the time of tenant-partition created.
                addAdmin(partitionInformation.getPartitionAdministrator(), partition.
                        getSuffix(), partitionInformation.getRealm(), partitionInformation.
                        isKdcEnabled());
                addAdminGroup(partitionInformation.getPartitionAdministrator(), partition.
                        getSuffix());

                addAdminACLEntry(partitionInformation.getPartitionAdministrator().getAdminUID(),
                                 partition.getSuffix());

                this.directoryService.sync();
            }


        } catch (Exception e) {
            String errorMessage = "Could not add the partition";
            logger.error(errorMessage, e);
            throw new DirectoryServerException(errorMessage, e);

        }
    }

    /**
     * @inheritDoc
     */
    public boolean partitionDirectoryExists(String partitionID) throws DirectoryServerException {
        boolean partitionDirectoryExists = false;
        String partitionDirectoryName = this.workingDirectory + File.separator + partitionID;
        File partitionDirectory = new File(partitionDirectoryName);

        //if a partition directory exists,it should be initialized without creating a new partition.
        if (partitionDirectory.exists()) {
            if (logger.isDebugEnabled()) {
                logger.debug("Partition directory - " + partitionDirectoryName + " already exists.");
            }

            partitionDirectoryExists = true;
        }
        return partitionDirectoryExists;
    }

    /**
     * @inheritDoc
     */
    public boolean partitionInitialized(String partitionId) {
        Set<? extends Partition> partitions = this.directoryService.getPartitions();

        for (Partition partition : partitions) {
            if (partition.getId().equals(partitionId)) {
                return true;
            }
        }

        return false;
    }

    /**
     * @inheritDoc
     */
    public int getNumberOfPartitions() {
        int numOfPartitions = 0; //if no partition is created

        Set<? extends Partition> partitions = this.directoryService.getPartitions();

        numOfPartitions = partitions.size();

        return numOfPartitions;
    }

    /**
     * This method initializes a partition from existing partition directory.
     */
    public void initializeExistingPartition(PartitionInfo partitionInfo) throws
                                                                         DirectoryServerException {

        Partition existingPartition = null;
        try {
            existingPartition = partitionFactory.createPartition(
                    partitionInfo.getPartitionId(), partitionInfo.getRootDN(), PARTITION_CACHE_SIZE,
                    new File(this.workingDirectory, partitionInfo.getPartitionId()));
            existingPartition.setSchemaManager(directoryService.getSchemaManager());

            if (logger.isDebugEnabled()) {
                logger.debug("Partition" + partitionInfo.getPartitionId() +
                        " created from existing partition directory.");
            }

        } catch (Exception e) {
            logger.error("Error in creating partition from existing partition directory.", e);
            throw new RuntimeException(e);
        }
        /**
         *Initialize the existing partition in the directory service.
         */

        try {
            this.directoryService.addPartition(existingPartition);
            this.directoryService.sync();

            if (logger.isDebugEnabled()) {
                logger.debug("Partition" + partitionInfo.getPartitionId() +
                        " added to directory service.");
            }

        } catch (Exception e) {
            logger.error("Error in initializing partition in directory service", e);
            throw new DirectoryServerException(e);
        }


    }

    /**
     * @inheritDoc
     */
    public void removePartition(String partitionSuffix)
            throws DirectoryServerException {

        Partition partition = getPartition(partitionSuffix);

        if (partition == null) {
            String msg = "Error deleting partition. Could not find a partition with suffix " +
                         partitionSuffix;
            logger.error(msg);
            throw new DirectoryServerException(msg);
        }

        try {
            this.directoryService.removePartition(partition);
        } catch (Exception e) {
            String msg = "Unable to delete partition with suffix " + partitionSuffix;
            logger.error(msg, e);
            throw new DirectoryServerException("Unable to delete partition with suffix " +
                                               partitionSuffix, e);
        }
    }


    public void removeAllPartitions() throws DirectoryServerException {
        Set<? extends Partition> partitions = this.directoryService.getPartitions();

        for (Partition partition : partitions) {
            if (!partition.getId().equalsIgnoreCase("schema")) {

                try {

                    if (logger.isDebugEnabled()) {
                        logger.debug("Removing partition with id - " + partition.getId() + " suffix - " +
                                partition.getSuffix());
                    }

                    this.directoryService.removePartition(partition);
                } catch (Exception e) {
                    String msg = "Unable to remove partition with id " + partition.getId() +
                                 " with suffix " + partition.getSuffix();
                    logger.error(msg, e);
                    throw new DirectoryServerException(msg, e);
                }
            }
        }
    }

    /**
     * @inheritDoc
     */
    public void synchronizePartitions()
            throws DirectoryServerException {

        try {

            this.directoryService.sync();
            List<Interceptor> interceptors = this.directoryService.getInterceptors();
            for (Interceptor interceptor : interceptors) {
                interceptor.init(this.directoryService);
            }

        } catch (Exception e) {
            throw new DirectoryServerException("Unable to sync partitions. ", e);
        }

    }

    private static void throwDirectoryServerException(String message, Throwable e)
            throws DirectoryServerException {

        logger.error(message, e);
        throw new DirectoryServerException(message, e);
    }

    private static void addObjectClasses(ServerEntry serverEntry, List<String> objectClasses)
            throws DirectoryServerException {

        for (String objectClass : objectClasses) {
            try {
                serverEntry.add("objectClass", objectClass);
            } catch (LdapException e) {
                throwDirectoryServerException("Could not add class to partition " +
                                              serverEntry.getDn().getName(), e);
            }
        }
    }

    private void addAccessControlAttributes(ServerEntry serverEntry)
            throws LdapException {
        serverEntry.add("administrativeRole", "accessControlSpecificArea");
    }

    private void addPartitionAttributes(String partitionDN, List<String> objectClasses,
                                        String realm, String dc)
            throws DirectoryServerException {

        try {
            DN adminDN = new DN(partitionDN);
            ServerEntry serverEntry = this.directoryService.newEntry(adminDN);

            addObjectClasses(serverEntry, objectClasses);

            serverEntry.add("o", realm);

            if (dc == null) {
                logger.warn("Domain component not found for partition with DN - " + partitionDN +
                            ". Not setting domain component.");
            } else {
                serverEntry.add("dc", dc);
            }

            addAccessControlAttributes(serverEntry);

            this.directoryService.getAdminSession().add(serverEntry);

        } catch (Exception e) {

            String msg = "Could not add partition attributes for partition - " + partitionDN;
            throwDirectoryServerException(msg, e);
        }

    }

    private void addUserStoreToPartition(String partitionSuffixDn)
            throws DirectoryServerException {

        try {
            DN usersDN = new DN("ou=Users," + partitionSuffixDn);
            ServerEntry usersEntry = this.directoryService.newEntry(usersDN);
            usersEntry.add("objectClass", "organizationalUnit", "top");
            usersEntry.add("ou", "Users");

            this.directoryService.getAdminSession().add(usersEntry);

        } catch (LdapInvalidDnException e) {
            String msg = "Could not add user store to partition - " + partitionSuffixDn +
                         ". Cause - partition domain name is not valid.";
            throwDirectoryServerException(msg, e);

        } catch (LdapException e) {
            String msg = "Could not add user store to partition - " + partitionSuffixDn;
            throwDirectoryServerException(msg, e);
        } catch (NamingException e) {
            String msg = "Could not add user store to partition - " + partitionSuffixDn +
                         ". Cause - partition domain name is not valid.";
            throwDirectoryServerException(msg, e);
        } catch (Exception e) {
            String msg = "Could not add user store to partition admin session. - " +
                         partitionSuffixDn;
            throwDirectoryServerException(msg, e);
        }

    }

    private void addGroupStoreToPartition(String partitionSuffixDn)
            throws DirectoryServerException {

        ServerEntry groupsEntry;
        try {

            DN groupsDN = new DN("ou=Groups," + partitionSuffixDn);

            groupsEntry = this.directoryService.newEntry(groupsDN);
            groupsEntry.add("objectClass", "organizationalUnit", "top");
            groupsEntry.add("ou", "Groups");

            this.directoryService.getAdminSession().add(groupsEntry);
        } catch (NamingException e) {
            String msg = "Could not add group store to partition - " + partitionSuffixDn +
                         ". Cause - partition domain name is not valid.";
            throwDirectoryServerException(msg, e);
        } catch (LdapException e) {
            String msg = "Could not add group store to partition - " + partitionSuffixDn;
            throwDirectoryServerException(msg, e);
        } catch (Exception e) {
            String msg = "Could not add group store to partition admin session. - " +
                         partitionSuffixDn;
            throwDirectoryServerException(msg, e);
        }

    }

    private Partition getPartition(String partitionSuffix) {
        Set availablePartitions = this.directoryService.getPartitions();
        Partition partition;

        for (Object object : availablePartitions) {
            partition = (Partition) object;
            if (partition.getSuffix().equals(partitionSuffix)) {
                return partition;
            }
        }

        return null;
    }

    private JdbmPartition createNewPartition(String partitionId, String partitionSuffix)
            throws DirectoryServerException {
        try {
            JdbmPartition partition = new JdbmPartition();
            String partitionDirectoryName = this.workingDirectory + File.separator + partitionId;
            File partitionDirectory = new File(partitionDirectoryName);

            partition.setId(partitionId);
            partition.setSuffix(partitionSuffix);
            partition.setPartitionDir(partitionDirectory);

            Set<Index<?, ServerEntry, Long>> indexedAttrs =
                    new HashSet<Index<?, ServerEntry, Long>>();

            indexedAttrs.add(new JdbmIndex<String, ServerEntry>("1.3.6.1.4.1.18060.0.4.1.2.1"));
            indexedAttrs.add(new JdbmIndex<String, ServerEntry>("1.3.6.1.4.1.18060.0.4.1.2.2"));
            indexedAttrs.add(new JdbmIndex<String, ServerEntry>("1.3.6.1.4.1.18060.0.4.1.2.3"));
            indexedAttrs.add(new JdbmIndex<String, ServerEntry>("1.3.6.1.4.1.18060.0.4.1.2.4"));
            indexedAttrs.add(new JdbmIndex<String, ServerEntry>("1.3.6.1.4.1.18060.0.4.1.2.5"));
            indexedAttrs.add(new JdbmIndex<String, ServerEntry>("1.3.6.1.4.1.18060.0.4.1.2.6"));
            indexedAttrs.add(new JdbmIndex<String, ServerEntry>("1.3.6.1.4.1.18060.0.4.1.2.7"));

            indexedAttrs.add(new JdbmIndex<String, ServerEntry>("ou"));
            indexedAttrs.add(new JdbmIndex<String, ServerEntry>("dc"));
            indexedAttrs.add(new JdbmIndex<String, ServerEntry>("objectClass"));
            indexedAttrs.add(new JdbmIndex<String, ServerEntry>("cn"));
            indexedAttrs.add(new JdbmIndex<String, ServerEntry>("uid"));
            partition.setIndexedAttributes(indexedAttrs);

            String message = MessageFormat.format(
                    "Partition created with following attributes, partition id - {0}, Partition " +
                    "domain - {1}, Partition working directory {2}", partitionId,
                    partitionSuffix, partitionDirectoryName);

            if (logger.isDebugEnabled()) {
                logger.debug(message);
            }


            return partition;

        } catch (LdapInvalidDnException e) {
            String msg = "Could not add a new partition with partition id " + partitionId +
                         " and suffix " + partitionSuffix;
            logger.error(msg, e);
            throw new DirectoryServerException(msg, e);
        }
    }

    private void addAdminACLEntry(String adminUid, String tenantSuffix)
            throws DirectoryServerException {

        try {

            //add the permission entry
            DN adminACLEntrydn = new DN("cn=adminACLEntry," + tenantSuffix);
            ServerEntry adminACLEntry = directoryService.newEntry(adminACLEntrydn);
            adminACLEntry.add("objectClass", "accessControlSubentry", "subentry", "top");
            adminACLEntry.add("cn", "adminACLEntry");

            String aclScript = "{ " +
                               "identificationTag \"adminACLEntryTag\", " +
                               "precedence 1, " +
                               "authenticationLevel simple, " +
                               "itemOrUserFirst userFirst: " +
                               "{ " +
                               "userClasses " +
                               "{ " +
                               "name { " +
                               "\"uid=" + adminUid + ",ou=Users," + tenantSuffix + "\" " +
                               "}  " +
                               "}, " +
                               "userPermissions " +
                               "{ " +
                               "{ " +
                               "protectedItems { entry, allUserAttributeTypesAndValues }, " +
                               "grantsAndDenials { " +
                               "grantBrowse, " +
                               "grantFilterMatch, " +
                               "grantModify, " +
                               "grantAdd, " +
                               "grantCompare, " +
                               "grantRename, " +
                               "grantRead, " +
                               "grantReturnDN, " +
                               "grantImport, " +
                               "grantInvoke, " +
                               "grantRemove, " +
                               "grantExport, " +
                               "grantDiscloseOnError " +
                               "} " +
                               "} " +
                               "} " +
                               "} " +
                               "}";

            adminACLEntry.add("prescriptiveACI", aclScript);
            adminACLEntry.add("subtreeSpecification", "{ }");

            directoryService.getAdminSession().add(adminACLEntry);

        } catch (LdapInvalidDnException e) {
            throwDirectoryServerException("Domain name invalid - cn=adminACLEntry," +
                                          tenantSuffix, e);
        } catch (LdapException e) {
            throwDirectoryServerException("Unable to create ACL entry for user " + adminUid, e);
        } catch (NamingException e) {
            throwDirectoryServerException("Invalid domain name entry - cn=adminACLEntry," +
                                          tenantSuffix, e);
        } catch (Exception e) {
            throwDirectoryServerException(
                    "Unable to add ACL entry for user - " + adminUid +
                    " with DN - cn=adminACLEntry," + tenantSuffix, e);
        }

    }

    private void addAdminPassword(ServerEntry adminEntry, String password,
                                  PasswordAlgorithm algorithm,
                                  final boolean kdcEnabled)
            throws DirectoryServerException {

        try {
            String passwordToStore = "{" + algorithm.getAlgorithmName() + "}";
            if (algorithm != PasswordAlgorithm.PLAIN_TEXT && !kdcEnabled) {
                MessageDigest md = MessageDigest.getInstance(algorithm.getAlgorithmName());
                md.update(password.getBytes());
                byte[] bytes = md.digest();
                String hash = Base64.encode(bytes);
                passwordToStore = passwordToStore + hash;

            } else {

                if (kdcEnabled) {
                    logger.warn(
                            "KDC enabled. Enforcing passwords to be plain text. Cause - KDC " +
                            "cannot operate with hashed passwords.");
                }

                passwordToStore = password;
            }

            adminEntry.put("userPassword", passwordToStore.getBytes());

        } catch (NoSuchAlgorithmException e) {
            throwDirectoryServerException("Could not find matching hash algorithm - " +
                                          algorithm.getAlgorithmName(), e);
        }

    }

    private void addAdminGroup(AdminInfo adminInfo, String partitionSuffix)
            throws DirectoryServerException {
        AdminGroupInfo groupInfo = adminInfo.getGroupInformation();
        String domainName = "";
        try {

            if (groupInfo != null) {

                domainName = groupInfo.getGroupNameAttribute() + "=" +
                             groupInfo.getAdminRoleName() + "," + "ou=Groups," + partitionSuffix;

                DN adminGroup = new DN(domainName);
                ServerEntry adminGroupEntry = directoryService.newEntry(adminGroup);
                addObjectClasses(adminGroupEntry, groupInfo.getObjectClasses());

                adminGroupEntry.add(groupInfo.getGroupNameAttribute(),
                                    groupInfo.getAdminRoleName());
                adminGroupEntry.add(groupInfo.getMemberNameAttribute(),
                                    "uid=" + adminInfo.getAdminUID() + "," + "ou=Users," +
                                    partitionSuffix);
                directoryService.getAdminSession().add(adminGroupEntry);
            }

        } catch (LdapInvalidDnException e) {
            String msg = "Domain name invalid " + domainName;
            throwDirectoryServerException(msg, e);
        } catch (LdapException e) {
            throwDirectoryServerException("Could not add group entry - " + domainName, e);
        } catch (NamingException e) {
            throwDirectoryServerException("Domain name invalid - " + domainName, e);
        } catch (Exception e) {
            throwDirectoryServerException("Could not add group entry to admin session. DN - " +
                                          domainName, e);
        }
    }

    private void addAdmin(AdminInfo adminInfo, String partitionSuffix, final String realm,
                          final boolean kdcEnabled) throws DirectoryServerException {

        String domainName = "uid=" + adminInfo.getAdminUID() + "," + "ou=Users," + partitionSuffix;

        try {
            DN adminDn = new DN(domainName);

            ServerEntry adminEntry = directoryService.newEntry(adminDn);

            List<String> objectClasses = adminInfo.getObjectClasses();
            if (kdcEnabled) {
                // Add Kerberose specific object classes
                objectClasses = new ArrayList<String>(adminInfo.getObjectClasses());
                objectClasses.add("krb5principal");
                objectClasses.add("krb5kdcentry");
            }

            addObjectClasses(adminEntry, objectClasses);

            adminEntry.add("uid", adminInfo.getAdminUID());
            adminEntry.add("sn", adminInfo.getAdminLastName());
            adminEntry.add("givenName", adminInfo.getAdminCommonName());
            //setting admin full name as uid since 'cn' is a compulsory attribute when constructing a
            // user entry.
            adminEntry.add("cn", adminInfo.getAdminUID());

            adminEntry.add("mail", adminInfo.getAdminEmail());

            if (kdcEnabled) {
                String principal = adminInfo.getAdminUID() + "@" + realm;
                adminEntry.put(KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, principal);
                adminEntry.put(KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT, "0");
            }

            addAdminPassword(adminEntry, adminInfo.getAdminPassword(),
                             adminInfo.getPasswordAlgorithm(), kdcEnabled);

            directoryService.getAdminSession().add(adminEntry);

        } catch (LdapInvalidDnException e) {
            throwDirectoryServerException("Domain name invalid " + domainName, e);
        } catch (LdapException e) {
            throwDirectoryServerException("Could not add entry to partition. DN - " +
                                          domainName, e);
        } catch (NamingException e) {
            throwDirectoryServerException("Domain name invalid - " + domainName, e);
        } catch (Exception e) {
            throwDirectoryServerException("Could not add group entry to admin session. DN - " +
                                          domainName, e);
        }

    }
}
TOP

Related Classes of org.wso2.carbon.apacheds.impl.ApacheDirectoryPartitionManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.