001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Portions Copyright 2011-2015 ForgeRock AS. 025 * Portions Copyright 2014 ForgeRock AS 026 */ 027package org.opends.server.api; 028 029import static org.opends.messages.CoreMessages.*; 030import static org.opends.server.config.ConfigConstants.*; 031 032import java.util.List; 033 034import org.forgerock.i18n.LocalizableMessage; 035import org.forgerock.i18n.slf4j.LocalizedLogger; 036import org.forgerock.opendj.ldap.ByteString; 037import org.opends.server.core.DirectoryServer; 038import org.opends.server.types.*; 039import org.forgerock.opendj.ldap.ResultCode; 040import org.opends.server.util.TimeThread; 041 042/** 043 * An abstract authentication policy. 044 */ 045public abstract class AuthenticationPolicy 046{ 047 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 048 049 /** 050 * Returns the authentication policy for the user provided user. The following 051 * algorithm is used in order to obtain the appropriate authentication policy: 052 * <ul> 053 * <li>if the user entry contains the {@code ds-pwp-password-policy-dn} 054 * attribute (whether real or virtual), then the referenced authentication 055 * policy will be returned 056 * <li>otherwise, a search is performed in order to find the nearest 057 * applicable password policy sub-entry to the user entry, 058 * <li>otherwise, the default password policy will be returned. 059 * </ul> 060 * 061 * @param userEntry 062 * The user entry. 063 * @param useDefaultOnError 064 * Indicates whether the server should fall back to using the default 065 * password policy if there is a problem with the configured policy 066 * for the user. 067 * @return The password policy for the user. 068 * @throws DirectoryException 069 * If a problem occurs while attempting to determine the password 070 * policy for the user. 071 */ 072 public static AuthenticationPolicy forUser(Entry userEntry, 073 boolean useDefaultOnError) throws DirectoryException 074 { 075 // First check to see if the ds-pwp-password-policy-dn is present. 076 String userDNString = userEntry.getName().toString(); 077 AttributeType type = DirectoryServer.getAttributeTypeOrDefault(OP_ATTR_PWPOLICY_POLICY_DN); 078 List<Attribute> attrList = userEntry.getAttribute(type); 079 080 if (attrList != null) 081 { 082 for (Attribute a : attrList) 083 { 084 if (a.isEmpty()) 085 { 086 continue; 087 } 088 089 ByteString v = a.iterator().next(); 090 DN subentryDN; 091 try 092 { 093 subentryDN = DN.decode(v); 094 } 095 catch (Exception e) 096 { 097 logger.traceException(e); 098 099 logger.trace("Could not parse password policy subentry DN %s for user %s", 100 v, userDNString, e); 101 102 if (useDefaultOnError) 103 { 104 logger.error(ERR_PWPSTATE_CANNOT_DECODE_SUBENTRY_VALUE_AS_DN, 105 v, userDNString, e.getMessage()); 106 return DirectoryServer.getDefaultPasswordPolicy(); 107 } 108 else 109 { 110 LocalizableMessage message = ERR_PWPSTATE_CANNOT_DECODE_SUBENTRY_VALUE_AS_DN 111 .get(v, userDNString, e.getMessage()); 112 throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX, message, e); 113 } 114 } 115 116 AuthenticationPolicy policy = DirectoryServer 117 .getAuthenticationPolicy(subentryDN); 118 if (policy == null) 119 { 120 logger.trace("Password policy subentry %s for user %s is not defined in the Directory Server.", 121 subentryDN, userDNString); 122 123 LocalizableMessage message = ERR_PWPSTATE_NO_SUCH_POLICY.get(userDNString, subentryDN); 124 if (useDefaultOnError) 125 { 126 logger.error(message); 127 return DirectoryServer.getDefaultPasswordPolicy(); 128 } 129 else 130 { 131 throw new DirectoryException( 132 DirectoryServer.getServerErrorResultCode(), message); 133 } 134 } 135 136 logger.trace("Using password policy subentry %s for user %s.", 137 subentryDN, userDNString); 138 139 return policy; 140 } 141 } 142 143 // The ds-pwp-password-policy-dn attribute was not present, so instead 144 // search for the nearest applicable sub-entry. 145 List<SubEntry> pwpSubEntries = DirectoryServer.getSubentryManager() 146 .getSubentries(userEntry); 147 if (pwpSubEntries != null && !pwpSubEntries.isEmpty()) 148 { 149 for (SubEntry subentry : pwpSubEntries) 150 { 151 try 152 { 153 if (subentry.getEntry().isPasswordPolicySubentry()) 154 { 155 AuthenticationPolicy policy = DirectoryServer 156 .getAuthenticationPolicy(subentry.getDN()); 157 if (policy == null) 158 { 159 // This shouldn't happen but if it does debug log 160 // this problem and fall back to default policy. 161 logger.trace("Found unknown password policy subentry DN %s for user %s", 162 subentry.getDN(), userDNString); 163 break; 164 } 165 return policy; 166 } 167 } 168 catch (Exception e) 169 { 170 logger.traceException(e, "Could not parse password policy subentry DN %s for user %s", 171 subentry.getDN(), userDNString); 172 } 173 } 174 } 175 176 // No authentication policy found, so use the global default. 177 logger.trace("Using the default password policy for user %s", userDNString); 178 179 return DirectoryServer.getDefaultPasswordPolicy(); 180 } 181 182 183 184 /** 185 * Creates a new abstract authentication policy. 186 */ 187 protected AuthenticationPolicy() 188 { 189 // No implementation required. 190 } 191 192 193 194 /** 195 * Returns the name of the configuration entry associated with this 196 * authentication policy. 197 * 198 * @return The name of the configuration entry associated with this 199 * authentication policy. 200 */ 201 public abstract DN getDN(); 202 203 204 205 /** 206 * Returns {@code true} if this authentication policy is a password policy and 207 * the methods {@link #createAuthenticationPolicyState(Entry)} and 208 * {@link #createAuthenticationPolicyState(Entry, long)} will return a 209 * {@code PasswordPolicyState}. 210 * <p> 211 * The default implementation is to return {@code false}. 212 * 213 * @return {@code true} if this authentication policy is a password policy, 214 * otherwise {@code false}. 215 */ 216 public boolean isPasswordPolicy() 217 { 218 return false; 219 } 220 221 222 223 /** 224 * Returns the authentication policy state object for the provided user using 225 * the current time as the basis for all time-based state logic (such as 226 * expiring passwords). 227 * <p> 228 * The default implementation is to call 229 * {@link #createAuthenticationPolicyState(Entry, long)} with the current 230 * time. 231 * 232 * @param userEntry 233 * The user's entry. 234 * @return The authentication policy state object for the provided user. 235 * @throws DirectoryException 236 * If a problem occurs while attempting to initialize the state 237 * object from the provided user entry. 238 */ 239 public AuthenticationPolicyState createAuthenticationPolicyState( 240 Entry userEntry) throws DirectoryException 241 { 242 return createAuthenticationPolicyState(userEntry, TimeThread.getTime()); 243 } 244 245 246 247 /** 248 * Returns an authentication policy state object for the provided user using 249 * the specified time as the basis for all time-based state logic (such as 250 * expiring passwords). 251 * 252 * @param userEntry 253 * The user's entry. 254 * @param time 255 * The time since the epoch to use for all time-based state logic 256 * (such as expiring passwords). 257 * @return The authentication policy state object for the provided user. 258 * @throws DirectoryException 259 * If a problem occurs while attempting to initialize the state 260 * object from the provided user entry. 261 */ 262 public abstract AuthenticationPolicyState createAuthenticationPolicyState( 263 Entry userEntry, long time) throws DirectoryException; 264 265 266 267 /** 268 * Performs any necessary work to finalize this authentication policy. 269 * <p> 270 * The default implementation is to do nothing. 271 */ 272 public void finalizeAuthenticationPolicy() 273 { 274 // Do nothing by default. 275 } 276}