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 2010 Sun Microsystems, Inc.
015 * Portions Copyright 2014-2016 ForgeRock AS.
016 */
017package org.forgerock.opendj.ldap.controls;
018
019import static com.forgerock.opendj.ldap.CoreMessages.ERR_PWPOLICYREQ_CONTROL_BAD_OID;
020import static com.forgerock.opendj.ldap.CoreMessages.ERR_PWPOLICYREQ_CONTROL_HAS_VALUE;
021
022import org.forgerock.i18n.LocalizableMessage;
023import org.forgerock.opendj.ldap.ByteString;
024import org.forgerock.opendj.ldap.DecodeException;
025import org.forgerock.opendj.ldap.DecodeOptions;
026
027import org.forgerock.util.Reject;
028
029/**
030 * The password policy request control as defined in
031 * draft-behera-ldap-password-policy.
032 * <p>
033 * This control may be sent with any request in order to convey to the server
034 * that this client is aware of, and can process the password policy response
035 * control. When a server receives this control, it will return the password
036 * policy response control when appropriate and with the proper data.
037 *
038 * <pre>
039 * Connection connection = ...;
040 * String DN = ...;
041 * char[] password = ...;
042 *
043 * try {
044 *     BindRequest request = Requests.newSimpleBindRequest(DN, password)
045 *             .addControl(PasswordPolicyRequestControl.newControl(true));
046 *
047 *     BindResult result = connection.bind(request);
048 *
049 *     PasswordPolicyResponseControl control =
050 *             result.getControl(PasswordPolicyResponseControl.DECODER,
051 *                     new DecodeOptions());
052 *     if (!(control == null) && !(control.getWarningType() == null)) {
053 *         // Password policy warning, use control.getWarningType(),
054 *         // and control.getWarningValue().
055 *     }
056 * } catch (LdapException e) {
057 *     Result result = e.getResult();
058 *     try {
059 *         PasswordPolicyResponseControl control =
060 *                 result.getControl(PasswordPolicyResponseControl.DECODER,
061 *                         new DecodeOptions());
062 *         if (!(control == null)) {
063 *             // Password policy error, use control.getErrorType().
064 *         }
065 *     } catch (DecodeException de) {
066 *         // Failed to decode the response control.
067 *     }
068 * } catch (DecodeException e) {
069 *     // Failed to decode the response control.
070 * }
071 * </pre>
072 *
073 * @see PasswordPolicyResponseControl
074 * @see <a href="http://tools.ietf.org/html/draft-behera-ldap-password-policy">
075 *      draft-behera-ldap-password-policy - Password Policy for LDAP Directories
076 *      </a>
077 */
078public final class PasswordPolicyRequestControl implements Control {
079    /** The OID for the password policy control from draft-behera-ldap-password-policy. */
080    public static final String OID = "1.3.6.1.4.1.42.2.27.8.5.1";
081
082    private final boolean isCritical;
083
084    private static final PasswordPolicyRequestControl CRITICAL_INSTANCE =
085            new PasswordPolicyRequestControl(true);
086    private static final PasswordPolicyRequestControl NONCRITICAL_INSTANCE =
087            new PasswordPolicyRequestControl(false);
088
089    /** A decoder which can be used for decoding the password policy request control. */
090    public static final ControlDecoder<PasswordPolicyRequestControl> DECODER =
091            new ControlDecoder<PasswordPolicyRequestControl>() {
092
093                @Override
094                public PasswordPolicyRequestControl decodeControl(final Control control,
095                        final DecodeOptions options) throws DecodeException {
096                    Reject.ifNull(control);
097
098                    if (control instanceof PasswordPolicyRequestControl) {
099                        return (PasswordPolicyRequestControl) control;
100                    }
101
102                    if (!control.getOID().equals(OID)) {
103                        final LocalizableMessage message =
104                                ERR_PWPOLICYREQ_CONTROL_BAD_OID.get(control.getOID(), OID);
105                        throw DecodeException.error(message);
106                    }
107
108                    if (control.hasValue()) {
109                        final LocalizableMessage message = ERR_PWPOLICYREQ_CONTROL_HAS_VALUE.get();
110                        throw DecodeException.error(message);
111                    }
112
113                    return control.isCritical() ? CRITICAL_INSTANCE : NONCRITICAL_INSTANCE;
114                }
115
116                @Override
117                public String getOID() {
118                    return OID;
119                }
120            };
121
122    /**
123     * Creates a new password policy request control having the provided
124     * criticality.
125     *
126     * @param isCritical
127     *            {@code true} if it is unacceptable to perform the operation
128     *            without applying the semantics of this control, or
129     *            {@code false} if it can be ignored.
130     * @return The new control.
131     */
132    public static PasswordPolicyRequestControl newControl(final boolean isCritical) {
133        return isCritical ? CRITICAL_INSTANCE : NONCRITICAL_INSTANCE;
134    }
135
136    private PasswordPolicyRequestControl(final boolean isCritical) {
137        this.isCritical = isCritical;
138    }
139
140    @Override
141    public String getOID() {
142        return OID;
143    }
144
145    @Override
146    public ByteString getValue() {
147        return null;
148    }
149
150    @Override
151    public boolean hasValue() {
152        return false;
153    }
154
155    @Override
156    public boolean isCritical() {
157        return isCritical;
158    }
159
160    @Override
161    public String toString() {
162        final StringBuilder builder = new StringBuilder();
163        builder.append("PasswordPolicyRequestControl(oid=");
164        builder.append(getOID());
165        builder.append(", criticality=");
166        builder.append(isCritical());
167        builder.append(")");
168        return builder.toString();
169    }
170
171}