/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Portions Copyright 2007 Sun Microsystems, Inc. */ package org.opends.server.workflowelement.localbackend; import static org.opends.server.config.ConfigConstants.*; import static org.opends.server.messages.CoreMessages.*; import static org.opends.server.messages.MessageHandler.getMessage; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import org.opends.server.api.PasswordStorageScheme; import org.opends.server.api.PasswordValidator; import org.opends.server.core.AddOperation; import org.opends.server.core.AddOperationWrapper; import org.opends.server.core.DirectoryServer; import org.opends.server.core.PasswordPolicy; import org.opends.server.protocols.asn1.ASN1OctetString; import org.opends.server.schema.AuthPasswordSyntax; import org.opends.server.schema.BooleanSyntax; import org.opends.server.schema.UserPasswordSyntax; import org.opends.server.types.Attribute; import org.opends.server.types.AttributeType; import org.opends.server.types.AttributeValue; import org.opends.server.types.ByteString; import org.opends.server.types.DirectoryException; import org.opends.server.types.Entry; import org.opends.server.types.ObjectClass; import org.opends.server.types.ResultCode; import org.opends.server.types.operation.PostOperationAddOperation; import org.opends.server.types.operation.PostResponseAddOperation; import org.opends.server.types.operation.PreOperationAddOperation; import org.opends.server.util.TimeThread; /** * This class defines an operation used to add an entry in a local backend * of the Directory Server. */ public class LocalBackendAddOperation extends AddOperationWrapper implements PreOperationAddOperation, PostOperationAddOperation, PostResponseAddOperation { // The entry being added to the server. private Entry entry; /** * Creates a new operation that may be used to add a new entry in a * local backend of the Directory Server. * * @param add The operation to enhance. */ public LocalBackendAddOperation(AddOperation add) { super(add); LocalBackendWorkflowElement.attachLocalOperation (add, this); } /** * Retrieves the entry to be added to the server. Note that this will not be * available to pre-parse plugins or during the conflict resolution portion of * the synchronization processing. * * @return The entry to be added to the server, or null if it is * not yet available. */ public final Entry getEntryToAdd() { return entry; } /** * Sets the entry to be added to the server. * * @param entry - The entry to be added to the server, or null * if it is not yet available. */ public final void setEntryToAdd(Entry entry){ this.entry = entry; } /** * Performs all password policy processing necessary for the provided add * operation. * * @param passwordPolicy The password policy associated with the entry to be * added. * @param userEntry The user entry being added. * * @throws DirectoryException If a problem occurs while performing password * policy processing for the add operation. */ public final void handlePasswordPolicy(PasswordPolicy passwordPolicy, Entry userEntry) throws DirectoryException { // See if a password was specified. AttributeType passwordAttribute = passwordPolicy.getPasswordAttribute(); List attrList = userEntry.getAttribute(passwordAttribute); if ((attrList == null) || attrList.isEmpty()) { // The entry doesn't have a password, so no action is required. return; } else if (attrList.size() > 1) { // This must mean there are attribute options, which we won't allow for // passwords. int msgID = MSGID_PWPOLICY_ATTRIBUTE_OPTIONS_NOT_ALLOWED; String message = getMessage(msgID, passwordAttribute.getNameOrOID()); throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message, msgID); } Attribute passwordAttr = attrList.get(0); if (passwordAttr.hasOptions()) { int msgID = MSGID_PWPOLICY_ATTRIBUTE_OPTIONS_NOT_ALLOWED; String message = getMessage(msgID, passwordAttribute.getNameOrOID()); throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message, msgID); } LinkedHashSet values = passwordAttr.getValues(); if (values.isEmpty()) { // This will be treated the same as not having a password. return; } if ((! passwordPolicy.allowMultiplePasswordValues()) && (values.size() > 1)) { // FIXME -- What if they're pre-encoded and might all be the same? int msgID = MSGID_PWPOLICY_MULTIPLE_PW_VALUES_NOT_ALLOWED; String message = getMessage(msgID, passwordAttribute.getNameOrOID()); throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message, msgID); } CopyOnWriteArrayList defaultStorageSchemes = passwordPolicy.getDefaultStorageSchemes(); LinkedHashSet newValues = new LinkedHashSet(defaultStorageSchemes.size()); for (AttributeValue v : values) { ByteString value = v.getValue(); // See if the password is pre-encoded. if (passwordPolicy.usesAuthPasswordSyntax()) { if (AuthPasswordSyntax.isEncoded(value)) { if (passwordPolicy.allowPreEncodedPasswords()) { newValues.add(v); continue; } else { int msgID = MSGID_PWPOLICY_PREENCODED_NOT_ALLOWED; String message = getMessage(msgID, passwordAttribute.getNameOrOID()); throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message, msgID); } } } else { if (UserPasswordSyntax.isEncoded(value)) { if (passwordPolicy.allowPreEncodedPasswords()) { newValues.add(v); continue; } else { int msgID = MSGID_PWPOLICY_PREENCODED_NOT_ALLOWED; String message = getMessage(msgID, passwordAttribute.getNameOrOID()); throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message, msgID); } } } // See if the password passes validation. We should only do this if // validation should be performed for administrators. if (! passwordPolicy.skipValidationForAdministrators()) { // There are never any current passwords for an add operation. HashSet currentPasswords = new HashSet(0); StringBuilder invalidReason = new StringBuilder(); for (PasswordValidator validator : passwordPolicy.getPasswordValidators().values()) { if (! validator.passwordIsAcceptable(value, currentPasswords, this, userEntry, invalidReason)) { int msgID = MSGID_PWPOLICY_VALIDATION_FAILED; String message = getMessage(msgID, passwordAttribute.getNameOrOID(), String.valueOf(invalidReason)); throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message, msgID); } } } // Encode the password. if (passwordPolicy.usesAuthPasswordSyntax()) { for (PasswordStorageScheme s : defaultStorageSchemes) { ByteString encodedValue = s.encodeAuthPassword(value); newValues.add(new AttributeValue(passwordAttribute, encodedValue)); } } else { for (PasswordStorageScheme s : defaultStorageSchemes) { ByteString encodedValue = s.encodePasswordWithScheme(value); newValues.add(new AttributeValue(passwordAttribute, encodedValue)); } } } // Put the new encoded values in the entry. passwordAttr.setValues(newValues); // Set the password changed time attribute. ByteString timeString = new ASN1OctetString(TimeThread.getGeneralizedTime()); AttributeType changedTimeType = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_CHANGED_TIME_LC); if (changedTimeType == null) { changedTimeType = DirectoryServer.getDefaultAttributeType( OP_ATTR_PWPOLICY_CHANGED_TIME); } LinkedHashSet changedTimeValues = new LinkedHashSet(1); changedTimeValues.add(new AttributeValue(changedTimeType, timeString)); ArrayList changedTimeList = new ArrayList(1); changedTimeList.add(new Attribute(changedTimeType, OP_ATTR_PWPOLICY_CHANGED_TIME, changedTimeValues)); userEntry.putAttribute(changedTimeType, changedTimeList); // If we should force change on add, then set the appropriate flag. if (passwordPolicy.forceChangeOnAdd()) { AttributeType resetType = DirectoryServer.getAttributeType(OP_ATTR_PWPOLICY_RESET_REQUIRED_LC); if (resetType == null) { resetType = DirectoryServer.getDefaultAttributeType( OP_ATTR_PWPOLICY_RESET_REQUIRED); } LinkedHashSet resetValues = new LinkedHashSet(1); resetValues.add(BooleanSyntax.createBooleanValue(true)); ArrayList resetList = new ArrayList(1); resetList.add(new Attribute(resetType, OP_ATTR_PWPOLICY_RESET_REQUIRED, resetValues)); userEntry.putAttribute(resetType, resetList); } } /** * Adds the provided objectClass to the entry, along with its superior classes * if appropriate. * * @param objectClass The objectclass to add to the entry. */ public final void addObjectClassChain(ObjectClass objectClass) { Map objectClasses = getObjectClasses(); if (objectClasses != null){ if (! objectClasses.containsKey(objectClass)) { objectClasses.put(objectClass, objectClass.getNameOrOID()); } ObjectClass superiorClass = objectClass.getSuperiorClass(); if ((superiorClass != null) && (! objectClasses.containsKey(superiorClass))) { addObjectClassChain(superiorClass); } } } }