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