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}