/* * 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 * * * Copyright 2011 ForgeRock AS. */ package org.opends.server.api; import static org.opends.messages.CoreMessages.*; import static org.opends.server.config.ConfigConstants.*; import static org.opends.server.loggers.ErrorLogger.logError; import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString; import java.util.List; import org.opends.messages.Message; import org.opends.server.core.DirectoryServer; import org.opends.server.loggers.debug.DebugLogger; import org.opends.server.loggers.debug.DebugTracer; import org.opends.server.types.*; import org.opends.server.util.TimeThread; /** * An abstract authentication policy. */ public abstract class AuthenticationPolicy { /** * The tracer object for the debug logger. */ private static final DebugTracer TRACER = DebugLogger.getTracer(); /** * 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 final 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.getDN().toString(); AttributeType type = DirectoryServer.getAttributeType( OP_ATTR_PWPOLICY_POLICY_DN, true); List<Attribute> attrList = userEntry.getAttribute(type); if (attrList != null) { for (Attribute a : attrList) { if (a.isEmpty()) continue; AttributeValue v = a.iterator().next(); DN subentryDN; try { subentryDN = DN.decode(v.getValue()); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } if (debugEnabled()) { TRACER.debugError("Could not parse password policy subentry " + "DN %s for user %s: %s", v.getValue().toString(), userDNString, stackTraceToSingleLineString(e)); } Message message = ERR_PWPSTATE_CANNOT_DECODE_SUBENTRY_VALUE_AS_DN .get(v.getValue().toString(), userDNString, e.getMessage()); if (useDefaultOnError) { logError(message); return DirectoryServer.getDefaultPasswordPolicy(); } else { throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX, message, e); } } AuthenticationPolicy policy = DirectoryServer .getAuthenticationPolicy(subentryDN); if (policy == null) { if (debugEnabled()) { TRACER.debugError("Password policy subentry %s for user %s " + "is not defined in the Directory Server.", String.valueOf(subentryDN), userDNString); } Message message = ERR_PWPSTATE_NO_SUCH_POLICY.get(userDNString, String.valueOf(subentryDN)); if (useDefaultOnError) { logError(message); return DirectoryServer.getDefaultPasswordPolicy(); } else { throw new DirectoryException( DirectoryServer.getServerErrorResultCode(), message); } } if (debugEnabled()) { TRACER.debugInfo("Using password policy subentry %s for user %s.", String.valueOf(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. if (debugEnabled()) { TRACER.debugError("Found unknown password policy subentry " + "DN %s for user %s", subentry.getDN().toString(), userDNString); } break; } return policy; } } catch (Exception e) { if (debugEnabled()) { TRACER.debugError("Could not parse password policy subentry " + "DN %s for user %s: %s", subentry.getDN().toString(), userDNString, stackTraceToSingleLineString(e)); } } } } // No authentication policy found, so use the global default. if (debugEnabled()) { TRACER.debugInfo("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. } }