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 010 * docs/licenses/cddl.txt 011 * or http://www.opensource.org/licenses/cddl1.php. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * docs/licenses/cddl.txt. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2010-2014 UnboundID Corp. 026 */ 027 package com.unboundid.directory.sdk.ds.scripting; 028 029 030 031 import java.util.List; 032 import java.util.Set; 033 034 import com.unboundid.directory.sdk.common.internal.Reconfigurable; 035 import com.unboundid.directory.sdk.common.types.Entry; 036 import com.unboundid.directory.sdk.common.types.OperationContext; 037 import com.unboundid.directory.sdk.ds.config.PasswordValidatorConfig; 038 import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension; 039 import com.unboundid.directory.sdk.ds.types.DirectoryServerContext; 040 import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension; 041 import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension; 042 import com.unboundid.ldap.sdk.LDAPException; 043 import com.unboundid.ldap.sdk.ResultCode; 044 import com.unboundid.util.ByteString; 045 import com.unboundid.util.Extensible; 046 import com.unboundid.util.ThreadSafety; 047 import com.unboundid.util.ThreadSafetyLevel; 048 import com.unboundid.util.args.ArgumentException; 049 import com.unboundid.util.args.ArgumentParser; 050 051 052 053 /** 054 * This class defines an API that must be implemented by scripted extensions 055 * which attempt to determine whether a proposed user password is acceptable. 056 * Each server password policy may be configured with zero or more password 057 * validators, and whenever a user changes his or her password (and optionally 058 * whenever an administrator resets the password for another user), then each of 059 * the password validators configured in the password policy for the target user 060 * will be given access to the clear-text password in order to determine whether 061 * that password will be allowed. Password validators will also have access to 062 * the rest of the user entry, and may also have access to a clear-text version 063 * of the user's current password(s) if they were provided in the request. 064 * <BR> 065 * <H2>Configuring Groovy-Scripted Password Validators</H2> 066 * In order to configure a scripted password validator based on this API and 067 * written in the Groovy scripting language, use a command like: 068 * <PRE> 069 * dsconfig create-password-validator \ 070 * --validator-name "<I>{validator-name}</I>" \ 071 * --type groovy-scripted \ 072 * --set enabled:true \ 073 * --set "script-class:<I>{class-name}</I>" \ 074 * --set "script-argument:<I>{name=value}</I>" 075 * </PRE> 076 * where "<I>{validator-name}</I>" is the name to use for the password validator 077 * instance, "<I>{class-name}</I>" is the fully-qualified name of the Groovy 078 * class written using this API, and "<I>{name=value}</I>" represents name-value 079 * pairs for any arguments to provide to the password validator. If multiple 080 * arguments should be provided to the password validator, then the 081 * "<CODE>--set script-argument:<I>{name=value}</I></CODE>" option should be 082 * provided multiple times. 083 * 084 * @see com.unboundid.directory.sdk.ds.api.PasswordValidator 085 */ 086 @Extensible() 087 @DirectoryServerExtension() 088 @DirectoryProxyServerExtension(appliesToLocalContent=true, 089 appliesToRemoteContent=false) 090 @SynchronizationServerExtension(appliesToLocalContent=true, 091 appliesToSynchronizedContent=false) 092 @ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE) 093 public abstract class ScriptedPasswordValidator 094 implements Reconfigurable<PasswordValidatorConfig> 095 { 096 /** 097 * Creates a new instance of this password validator. All password validator 098 * implementations must include a default constructor, but any initialization 099 * should generally be done in the {@code initializePasswordValidator} method. 100 */ 101 public ScriptedPasswordValidator() 102 { 103 // No implementation is required. 104 } 105 106 107 108 /** 109 * {@inheritDoc} 110 */ 111 public void defineConfigArguments(final ArgumentParser parser) 112 throws ArgumentException 113 { 114 // No arguments will be allowed by default. 115 } 116 117 118 119 /** 120 * Initializes this password validator. 121 * 122 * @param serverContext A handle to the server context for the server in 123 * which this extension is running. 124 * @param config The general configuration for this password 125 * validator. 126 * @param parser The argument parser which has been initialized from 127 * the configuration for this password validator. 128 * 129 * @throws LDAPException If a problem occurs while initializing this 130 * password validator. 131 */ 132 public void initializePasswordValidator( 133 final DirectoryServerContext serverContext, 134 final PasswordValidatorConfig config, 135 final ArgumentParser parser) 136 throws LDAPException 137 { 138 // No initialization will be performed by default. 139 } 140 141 142 143 /** 144 * Performs any cleanup which may be necessary when this password validator is 145 * to be taken out of service. 146 */ 147 public void finalizePasswordValidator() 148 { 149 // No implementation is required. 150 } 151 152 153 154 /** 155 * {@inheritDoc} 156 */ 157 public boolean isConfigurationAcceptable( 158 final PasswordValidatorConfig config, 159 final ArgumentParser parser, 160 final List<String> unacceptableReasons) 161 { 162 // No extended validation will be performed. 163 return true; 164 } 165 166 167 168 /** 169 * {@inheritDoc} 170 */ 171 public ResultCode applyConfiguration(final PasswordValidatorConfig config, 172 final ArgumentParser parser, 173 final List<String> adminActionsRequired, 174 final List<String> messages) 175 { 176 // By default, no configuration changes will be applied. If there are any 177 // arguments, then add an admin action message indicating that the extension 178 // needs to be restarted for any changes to take effect. 179 if (! parser.getNamedArguments().isEmpty()) 180 { 181 adminActionsRequired.add( 182 "No configuration change has actually been applied. The new " + 183 "configuration will not take effect until this password " + 184 "validator is disabled and re-enabled or until the server is " + 185 "restarted."); 186 } 187 188 return ResultCode.SUCCESS; 189 } 190 191 192 193 /** 194 * Indicates whether the proposed password is acceptable for the specified 195 * user. 196 * 197 * @param operationContext The operation context for the associated request. 198 * It may be associated with an add, modify, or 199 * password modify operation. 200 * @param newPassword The proposed new password for the user that 201 * should be validated. It will not be encoded or 202 * obscured in any way. 203 * @param currentPasswords The current set of passwords for the user, if 204 * available. It may be {@code null} if this is 205 * not available. Note that even if one or more 206 * current passwords are available, it may not 207 * constitute the complete set of passwords for the 208 * user. 209 * @param userEntry The entry for the user whose password is being 210 * changed. 211 * @param invalidReason A buffer to which a message may be appended to 212 * indicate why the proposed password is not 213 * acceptable. 214 * 215 * @return {@code true} if the proposed new password is acceptable, or 216 * {@code false} if not. 217 */ 218 public abstract boolean isPasswordAcceptable( 219 final OperationContext operationContext, 220 final ByteString newPassword, 221 final Set<ByteString> currentPasswords, 222 final Entry userEntry, 223 final StringBuilder invalidReason); 224 }