/* * 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 legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * 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 legal-notices/CDDLv1_0.txt. * 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 2011-2015 ForgeRock AS. * Portions Copyright 2014 ForgeRock AS */ package org.opends.server.api; import static org.opends.messages.CoreMessages.*; import static org.opends.server.config.ConfigConstants.*; import java.util.List; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.slf4j.LocalizedLogger; import org.forgerock.opendj.ldap.ByteString; import org.opends.server.core.DirectoryServer; import org.opends.server.types.*; import org.forgerock.opendj.ldap.ResultCode; import org.opends.server.util.TimeThread; /** * An abstract authentication policy. */ public abstract class AuthenticationPolicy { private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); /** * Returns the authentication policy for the user provided user. The following * algorithm is used in order to obtain the appropriate authentication policy: * <ul> * <li>if the user entry contains the {@code ds-pwp-password-policy-dn} * attribute (whether real or virtual), then the referenced authentication * policy will be returned * <li>otherwise, a search is performed in order to find the nearest * applicable password policy sub-entry to the user entry, * <li>otherwise, the default password policy will be returned. * </ul> * * @param userEntry * The user entry. * @param useDefaultOnError * Indicates whether the server should fall back to using the default * password policy if there is a problem with the configured policy * for the user. * @return The password policy for the user. * @throws DirectoryException * If a problem occurs while attempting to determine the password * policy for the user. */ public static AuthenticationPolicy forUser(Entry userEntry, boolean useDefaultOnError) throws DirectoryException { // First check to see if the ds-pwp-password-policy-dn is present. String userDNString = userEntry.getName().toString(); AttributeType type = DirectoryServer.getAttributeTypeOrDefault(OP_ATTR_PWPOLICY_POLICY_DN); List<Attribute> attrList = userEntry.getAttribute(type); if (attrList != null) { for (Attribute a : attrList) { if (a.isEmpty()) { continue; } ByteString v = a.iterator().next(); DN subentryDN; try { subentryDN = DN.decode(v); } catch (Exception e) { logger.traceException(e); logger.trace("Could not parse password policy subentry DN %s for user %s", v, userDNString, e); if (useDefaultOnError) { logger.error(ERR_PWPSTATE_CANNOT_DECODE_SUBENTRY_VALUE_AS_DN, v, userDNString, e.getMessage()); return DirectoryServer.getDefaultPasswordPolicy(); } else { LocalizableMessage message = ERR_PWPSTATE_CANNOT_DECODE_SUBENTRY_VALUE_AS_DN .get(v, userDNString, e.getMessage()); throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX, message, e); } } AuthenticationPolicy policy = DirectoryServer .getAuthenticationPolicy(subentryDN); if (policy == null) { logger.trace("Password policy subentry %s for user %s is not defined in the Directory Server.", subentryDN, userDNString); LocalizableMessage message = ERR_PWPSTATE_NO_SUCH_POLICY.get(userDNString, subentryDN); if (useDefaultOnError) { logger.error(message); return DirectoryServer.getDefaultPasswordPolicy(); } else { throw new DirectoryException( DirectoryServer.getServerErrorResultCode(), message); } } logger.trace("Using password policy subentry %s for user %s.", subentryDN, userDNString); return policy; } } // The ds-pwp-password-policy-dn attribute was not present, so instead // search for the nearest applicable sub-entry. List<SubEntry> pwpSubEntries = DirectoryServer.getSubentryManager() .getSubentries(userEntry); if (pwpSubEntries != null && !pwpSubEntries.isEmpty()) { for (SubEntry subentry : pwpSubEntries) { try { if (subentry.getEntry().isPasswordPolicySubentry()) { AuthenticationPolicy policy = DirectoryServer .getAuthenticationPolicy(subentry.getDN()); if (policy == null) { // This shouldn't happen but if it does debug log // this problem and fall back to default policy. logger.trace("Found unknown password policy subentry DN %s for user %s", subentry.getDN(), userDNString); break; } return policy; } } catch (Exception e) { logger.traceException(e, "Could not parse password policy subentry DN %s for user %s", subentry.getDN(), userDNString); } } } // No authentication policy found, so use the global default. logger.trace("Using the default password policy for user %s", userDNString); return DirectoryServer.getDefaultPasswordPolicy(); } /** * Creates a new abstract authentication policy. */ protected AuthenticationPolicy() { // No implementation required. } /** * Returns the name of the configuration entry associated with this * authentication policy. * * @return The name of the configuration entry associated with this * authentication policy. */ public abstract DN getDN(); /** * Returns {@code true} if this authentication policy is a password policy and * the methods {@link #createAuthenticationPolicyState(Entry)} and * {@link #createAuthenticationPolicyState(Entry, long)} will return a * {@code PasswordPolicyState}. * <p> * The default implementation is to return {@code false}. * * @return {@code true} if this authentication policy is a password policy, * otherwise {@code false}. */ public boolean isPasswordPolicy() { return false; } /** * Returns the authentication policy state object for the provided user using * the current time as the basis for all time-based state logic (such as * expiring passwords). * <p> * The default implementation is to call * {@link #createAuthenticationPolicyState(Entry, long)} with the current * time. * * @param userEntry * The user's entry. * @return The authentication policy state object for the provided user. * @throws DirectoryException * If a problem occurs while attempting to initialize the state * object from the provided user entry. */ public AuthenticationPolicyState createAuthenticationPolicyState( Entry userEntry) throws DirectoryException { return createAuthenticationPolicyState(userEntry, TimeThread.getTime()); } /** * Returns an authentication policy state object for the provided user using * the specified time as the basis for all time-based state logic (such as * expiring passwords). * * @param userEntry * The user's entry. * @param time * The time since the epoch to use for all time-based state logic * (such as expiring passwords). * @return The authentication policy state object for the provided user. * @throws DirectoryException * If a problem occurs while attempting to initialize the state * object from the provided user entry. */ public abstract AuthenticationPolicyState createAuthenticationPolicyState( Entry userEntry, long time) throws DirectoryException; /** * Performs any necessary work to finalize this authentication policy. * <p> * The default implementation is to do nothing. */ public void finalizeAuthenticationPolicy() { // Do nothing by default. } }