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 2012-2016 ForgeRock AS.
016 */
017package org.forgerock.opendj.ldap.controls;
018
019import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage;
020import static com.forgerock.opendj.ldap.CoreMessages.*;
021
022import java.io.IOException;
023
024import org.forgerock.i18n.LocalizableMessage;
025import org.forgerock.i18n.LocalizedIllegalArgumentException;
026import org.forgerock.i18n.slf4j.LocalizedLogger;
027import org.forgerock.opendj.io.ASN1;
028import org.forgerock.opendj.io.ASN1Reader;
029import org.forgerock.opendj.io.ASN1Writer;
030import org.forgerock.opendj.ldap.ByteString;
031import org.forgerock.opendj.ldap.ByteStringBuilder;
032import org.forgerock.opendj.ldap.DN;
033import org.forgerock.opendj.ldap.DecodeException;
034import org.forgerock.opendj.ldap.DecodeOptions;
035import org.forgerock.opendj.ldap.schema.Schema;
036import org.forgerock.util.Reject;
037
038/**
039 * The proxy authorization v1 request control as defined in
040 * draft-weltman-ldapv3-proxy-04. This control allows a user to request that an
041 * operation be performed using the authorization of another user. The target
042 * user is specified as a DN in the control value, which distinguishes it from
043 * later versions of the control (which used a different OID) in which the
044 * target user was specified using an authorization ID.
045 * <p>
046 * This control implementation is based on version 1 of the proxied
047 * authorization control as defined in early versions of
048 * draft-weltman-ldapv3-proxy (this implementation is based on the "-04"
049 * revision) and is intended for use in legacy applications. New applications
050 * should use the v2 version of this control in preference.
051 *
052 * @see <a href="http://tools.ietf.org/html/draft-weltman-ldapv3-proxy-04">
053 *      draft-weltman-ldapv3-proxy-04 - LDAP Proxied Authorization Control </a>
054 */
055public final class ProxiedAuthV1RequestControl implements Control {
056
057    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
058    /** The OID for the proxied authorization v1 control. */
059    public static final String OID = "2.16.840.1.113730.3.4.12";
060
061    /** A decoder which can be used for decoding the proxied authorization v1 request control. */
062    public static final ControlDecoder<ProxiedAuthV1RequestControl> DECODER =
063            new ControlDecoder<ProxiedAuthV1RequestControl>() {
064
065                @Override
066                public ProxiedAuthV1RequestControl decodeControl(final Control control,
067                        final DecodeOptions options) throws DecodeException {
068                    Reject.ifNull(control);
069
070                    if (control instanceof ProxiedAuthV1RequestControl) {
071                        return (ProxiedAuthV1RequestControl) control;
072                    }
073
074                    if (!control.getOID().equals(OID)) {
075                        final LocalizableMessage message =
076                                ERR_PROXYAUTH1_CONTROL_BAD_OID.get(control.getOID(), OID);
077                        throw DecodeException.error(message);
078                    }
079
080                    if (!control.isCritical()) {
081                        final LocalizableMessage message =
082                                ERR_PROXYAUTH1_CONTROL_NOT_CRITICAL.get();
083                        throw DecodeException.error(message);
084                    }
085
086                    if (!control.hasValue()) {
087                        // The response control must always have a value.
088                        final LocalizableMessage message = ERR_PROXYAUTH1_NO_CONTROL_VALUE.get();
089                        throw DecodeException.error(message);
090                    }
091
092                    final ASN1Reader reader = ASN1.getReader(control.getValue());
093                    String authorizationDNString;
094                    try {
095                        reader.readStartSequence();
096                        authorizationDNString = reader.readOctetStringAsString();
097                        reader.readEndSequence();
098                    } catch (final IOException e) {
099                        logger.debug(LocalizableMessage.raw("Unable to read sequence", e));
100
101                        final LocalizableMessage message =
102                                ERR_PROXYAUTH1_CANNOT_DECODE_VALUE.get(getExceptionMessage(e));
103                        throw DecodeException.error(message, e);
104                    }
105
106                    final Schema schema =
107                            options.getSchemaResolver().resolveSchema(authorizationDNString);
108                    DN authorizationDN;
109                    try {
110                        authorizationDN = DN.valueOf(authorizationDNString, schema);
111                    } catch (final LocalizedIllegalArgumentException e) {
112                        final LocalizableMessage message =
113                                ERR_PROXYAUTH1_INVALID_AUTHZIDDN.get(getExceptionMessage(e));
114                        throw DecodeException.error(message, e);
115                    }
116
117                    return new ProxiedAuthV1RequestControl(authorizationDN);
118                }
119
120                @Override
121                public String getOID() {
122                    return OID;
123                }
124            };
125
126    /**
127     * Creates a new proxy authorization v1 request control with the provided
128     * authorization name.
129     *
130     * @param authorizationName
131     *            The distinguished name of the user whose authorization is to
132     *            be used when performing the operation.
133     * @return The new control.
134     * @throws NullPointerException
135     *             If {@code authorizationName} was {@code null}.
136     */
137    public static ProxiedAuthV1RequestControl newControl(final DN authorizationName) {
138        Reject.ifNull(authorizationName);
139        return new ProxiedAuthV1RequestControl(authorizationName);
140    }
141
142    /**
143     * Creates a new proxy authorization v1 request control with the provided
144     * authorization name decoded using the default schema.
145     *
146     * @param authorizationName
147     *            The distinguished name of the user whose authorization is to
148     *            be used when performing the operation.
149     * @return The new control.
150     * @throws LocalizedIllegalArgumentException
151     *             If {@code authorizationName} is not a valid LDAP string
152     *             representation of a DN.
153     * @throws NullPointerException
154     *             If {@code authorizationName} was {@code null}.
155     */
156    public static ProxiedAuthV1RequestControl newControl(final String authorizationName) {
157        Reject.ifNull(authorizationName);
158        return new ProxiedAuthV1RequestControl(DN.valueOf(authorizationName));
159    }
160
161    private final DN authorizationName;
162
163    private ProxiedAuthV1RequestControl(final DN authorizationName) {
164        this.authorizationName = authorizationName;
165    }
166
167    /**
168     * Returns the distinguished name of the user whose authorization is to be
169     * used when performing the operation.
170     *
171     * @return The distinguished name of the user whose authorization is to be
172     *         used when performing the operation.
173     */
174    public DN getAuthorizationDNName() {
175        return authorizationName;
176    }
177
178    @Override
179    public String getOID() {
180        return OID;
181    }
182
183    @Override
184    public ByteString getValue() {
185        final ByteStringBuilder buffer = new ByteStringBuilder();
186        final ASN1Writer writer = ASN1.getWriter(buffer);
187        try {
188            writer.writeStartSequence();
189            writer.writeOctetString(authorizationName.toString());
190            writer.writeEndSequence();
191            return buffer.toByteString();
192        } catch (final IOException ioe) {
193            // This should never happen unless there is a bug somewhere.
194            throw new RuntimeException(ioe);
195        }
196    }
197
198    @Override
199    public boolean hasValue() {
200        return true;
201    }
202
203    @Override
204    public boolean isCritical() {
205        return true;
206    }
207
208    @Override
209    public String toString() {
210        final StringBuilder buffer = new StringBuilder();
211        buffer.append("ProxiedAuthorizationV1Control(oid=");
212        buffer.append(getOID());
213        buffer.append(", criticality=");
214        buffer.append(isCritical());
215        buffer.append(", proxyDN=\"");
216        buffer.append(authorizationName);
217        buffer.append("\")");
218        return buffer.toString();
219    }
220}