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     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
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     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.controls;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.util.ArrayList;
033    import java.util.concurrent.locks.Lock;
034    
035    import org.opends.server.core.DirectoryServer;
036    import org.opends.server.core.PasswordPolicyState;
037    import org.opends.server.protocols.asn1.ASN1Element;
038    import org.opends.server.protocols.asn1.ASN1OctetString;
039    import org.opends.server.protocols.asn1.ASN1Sequence;
040    import org.opends.server.protocols.ldap.LDAPResultCode;
041    import org.opends.server.types.Control;
042    import org.opends.server.types.DirectoryException;
043    import org.opends.server.types.DN;
044    import org.opends.server.types.Entry;
045    import org.opends.server.types.LDAPException;
046    import org.opends.server.types.LockManager;
047    import org.opends.server.types.ResultCode;
048    
049    import static org.opends.server.loggers.debug.DebugLogger.*;
050    import org.opends.server.loggers.debug.DebugTracer;
051    import org.opends.server.types.DebugLogLevel;
052    import static org.opends.messages.ProtocolMessages.*;
053    import static org.opends.server.util.ServerConstants.*;
054    import static org.opends.server.util.StaticUtils.*;
055    import static org.opends.server.util.Validator.*;
056    
057    
058    
059    /**
060     * This class implements version 1 of the proxied authorization control as
061     * defined in early versions of draft-weltman-ldapv3-proxy (this implementation
062     * is based on the "-04" revision).  It makes it possible for one user to
063     * request that an operation be performed under the authorization of another.
064     * The target user is specified as a DN in the control value, which
065     * distinguishes it from later versions of the control (which used a different
066     * OID) in which the target user was specified using an authorization ID.
067     */
068    public class ProxiedAuthV1Control
069           extends Control
070    {
071      /**
072       * The tracer object for the debug logger.
073       */
074      private static final DebugTracer TRACER = getTracer();
075    
076    
077    
078    
079      // The raw, unprocessed authorization DN from the control value.
080      private ASN1OctetString rawAuthorizationDN;
081    
082      // The processed authorization DN from the control value.
083      private DN authorizationDN;
084    
085    
086    
087      /**
088       * Creates a new instance of the proxied authorization v1 control with the
089       * provided information.
090       *
091       * @param  rawAuthorizationDN  The raw, unprocessed authorization DN from the
092       *                             control value.  It must not be {@code null}.
093       */
094      public ProxiedAuthV1Control(ASN1OctetString rawAuthorizationDN)
095      {
096        super(OID_PROXIED_AUTH_V1, true, encodeValue(rawAuthorizationDN));
097    
098    
099        this.rawAuthorizationDN = rawAuthorizationDN;
100    
101        authorizationDN = null;
102      }
103    
104    
105    
106      /**
107       * Creates a new instance of the proxied authorization v1 control with the
108       * provided information.
109       *
110       * @param  authorizationDN  The authorization DN from the control value.  It
111       *                          must not be {@code null}.
112       */
113      public ProxiedAuthV1Control(DN authorizationDN)
114      {
115        super(OID_PROXIED_AUTH_V1, true,
116              encodeValue(new ASN1OctetString(authorizationDN.toString())));
117    
118    
119        this.authorizationDN = authorizationDN;
120    
121        rawAuthorizationDN = new ASN1OctetString(authorizationDN.toString());
122      }
123    
124    
125    
126      /**
127       * Creates a new instance of the proxied authorization v1 control with the
128       * provided information.
129       *
130       * @param  oid                 The OID to use for this control.
131       * @param  isCritical          Indicates whether support for this control
132       *                             should be considered a critical part of the
133       *                             server processing.
134       * @param  controlValue        The encoded value for this control.
135       * @param  rawAuthorizationDN  The raw, unprocessed authorization DN from the
136       *                             control value.
137       */
138      private ProxiedAuthV1Control(String oid, boolean isCritical,
139                                 ASN1OctetString controlValue,
140                                 ASN1OctetString rawAuthorizationDN)
141      {
142        super(oid, isCritical, controlValue);
143    
144    
145        this.rawAuthorizationDN = rawAuthorizationDN;
146    
147        authorizationDN = null;
148      }
149    
150    
151    
152      /**
153       * Generates an encoded value for this control containing the provided raw
154       * authorization DN.
155       *
156       * @param  rawAuthorizationDN  The raw, unprocessed authorization DN to use in
157       *                             the control value.  It must not be
158       *                             {@code null}.
159       *
160       * @return  The encoded control value.
161       */
162      private static ASN1OctetString encodeValue(ASN1OctetString rawAuthorizationDN)
163      {
164        ensureNotNull(rawAuthorizationDN);
165    
166        ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(1);
167        elements.add(rawAuthorizationDN);
168    
169        return new ASN1OctetString(new ASN1Sequence(elements).encode());
170      }
171    
172    
173    
174      /**
175       * Creates a new proxied authorization v1 control from the contents of the
176       * provided control.
177       *
178       * @param  control  The generic control containing the information to use to
179       *                  create this proxied authorization v1 control.  It must not
180       *                  be {@code null}.
181       *
182       * @return  The proxied authorization v1 control decoded from the provided
183       *          control.
184       *
185       * @throws  LDAPException  If this control cannot be decoded as a valid
186       *                         proxied authorization v1 control.
187       */
188      public static ProxiedAuthV1Control decodeControl(Control control)
189             throws LDAPException
190      {
191        ensureNotNull(control);
192    
193        if (! control.isCritical())
194        {
195          Message message = ERR_PROXYAUTH1_CONTROL_NOT_CRITICAL.get();
196          throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
197        }
198    
199        if (! control.hasValue())
200        {
201          Message message = ERR_PROXYAUTH1_NO_CONTROL_VALUE.get();
202          throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
203        }
204    
205    
206        ASN1OctetString rawAuthorizationDN;
207        try
208        {
209          ArrayList<ASN1Element> elements =
210               ASN1Sequence.decodeAsSequence(control.getValue().value()).elements();
211          if (elements.size() != 1)
212          {
213            Message message =
214                ERR_PROXYAUTH1_INVALID_ELEMENT_COUNT.get(elements.size());
215            throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
216          }
217    
218          rawAuthorizationDN = elements.get(0).decodeAsOctetString();
219        }
220        catch (LDAPException le)
221        {
222          throw le;
223        }
224        catch (Exception e)
225        {
226          if (debugEnabled())
227          {
228            TRACER.debugCaught(DebugLogLevel.ERROR, e);
229          }
230    
231          Message message =
232              ERR_PROXYAUTH1_CANNOT_DECODE_VALUE.get(getExceptionMessage(e));
233          throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message, e);
234        }
235    
236        return new ProxiedAuthV1Control(control.getOID(), control.isCritical(),
237                                        control.getValue(), rawAuthorizationDN);
238      }
239    
240    
241    
242      /**
243       * Retrieves the raw, unprocessed authorization DN from the control value.
244       *
245       * @return  The raw, unprocessed authorization DN from the control value.
246       */
247      public ASN1OctetString getRawAuthorizationDN()
248      {
249        return rawAuthorizationDN;
250      }
251    
252    
253    
254      /**
255       * Specifies the raw, unprocessed authorization DN for this proxied auth
256       * control.
257       *
258       * @param  rawAuthorizationDN  The raw, unprocessed authorization DN for this
259       *                             proxied auth control.
260       */
261      public void setRawAuthorizationDN(ASN1OctetString rawAuthorizationDN)
262      {
263        this.rawAuthorizationDN = rawAuthorizationDN;
264    
265        setValue(encodeValue(rawAuthorizationDN));
266        authorizationDN = null;
267      }
268    
269    
270    
271      /**
272       * Retrieves the authorization DN from the control value.
273       *
274       * @return  The authorization DN from the control value.
275       *
276       * @throws  DirectoryException  If a problem occurs while attempting to decode
277       *                              the raw authorization DN as a DN.
278       */
279      public DN getAuthorizationDN()
280             throws DirectoryException
281      {
282        if (authorizationDN == null)
283        {
284          authorizationDN = DN.decode(rawAuthorizationDN);
285        }
286    
287        return authorizationDN;
288      }
289    
290    
291    
292      /**
293       * Specifies the authorization DN for this proxied auth control.
294       *
295       * @param  authorizationDN  The authorizationDN for this proxied auth control.
296       *                          It must not be {@code null}.
297       */
298      public void setAuthorizationDN(DN authorizationDN)
299      {
300        ensureNotNull(authorizationDN);
301    
302        this.authorizationDN = authorizationDN;
303    
304        rawAuthorizationDN = new ASN1OctetString(authorizationDN.toString());
305        setValue(encodeValue(rawAuthorizationDN));
306      }
307    
308    
309    
310      /**
311       * Retrieves the authorization entry for this proxied authorization V1
312       * control.  It will also perform any necessary password policy checks to
313       * ensure that the associated user account is suitable for use in performing
314       * this processing.
315       *
316       * @return  The entry for user specified as the authorization identity in this
317       *          proxied authorization V1 control, or {@code null} if the
318       *          authorization DN is the null DN.
319       *
320       * @throws  DirectoryException  If the target user does not exist or is not
321       *                              available for use, or if a problem occurs
322       *                              while making the determination.
323       */
324      public Entry getAuthorizationEntry()
325             throws DirectoryException
326      {
327        DN authzDN = getAuthorizationDN();
328        if (authzDN.isNullDN())
329        {
330          return null;
331        }
332    
333    
334        // See if the authorization DN is one of the alternate bind DNs for one of
335        // the root users and if so then map it accordingly.
336        DN actualDN = DirectoryServer.getActualRootBindDN(authzDN);
337        if (actualDN != null)
338        {
339          authzDN = actualDN;
340        }
341    
342    
343        Lock entryLock = null;
344        for (int i=0; i < 3; i++)
345        {
346          entryLock = LockManager.lockRead(authzDN);
347          if (entryLock != null)
348          {
349            break;
350          }
351        }
352    
353        if (entryLock == null)
354        {
355          Message message =
356              ERR_PROXYAUTH1_CANNOT_LOCK_USER.get(String.valueOf(authzDN));
357          throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, message);
358        }
359    
360        try
361        {
362          Entry userEntry = DirectoryServer.getEntry(authzDN);
363          if (userEntry == null)
364          {
365            // The requested user does not exist.
366            Message message =
367                ERR_PROXYAUTH1_NO_SUCH_USER.get(String.valueOf(authzDN));
368            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, message);
369          }
370    
371    
372          // FIXME -- We should provide some mechanism for enabling debug
373          // processing.
374          PasswordPolicyState pwpState = new PasswordPolicyState(userEntry, false);
375          if (pwpState.isDisabled() || pwpState.isAccountExpired() ||
376              pwpState.lockedDueToFailures() ||
377              pwpState.lockedDueToIdleInterval() ||
378              pwpState.lockedDueToMaximumResetAge() ||
379              pwpState.isPasswordExpired())
380          {
381            Message message =
382                ERR_PROXYAUTH1_UNUSABLE_ACCOUNT.get(String.valueOf(authzDN));
383            throw new DirectoryException(ResultCode.AUTHORIZATION_DENIED, message);
384          }
385    
386    
387          // If we've made it here, then the user is acceptable.
388          return userEntry;
389        }
390        finally
391        {
392          LockManager.unlock(authzDN, entryLock);
393        }
394      }
395    
396    
397    
398      /**
399       * Retrieves a string representation of this proxied auth v1 control.
400       *
401       * @return  A string representation of this proxied auth v1 control.
402       */
403      public String toString()
404      {
405        StringBuilder buffer = new StringBuilder();
406        toString(buffer);
407        return buffer.toString();
408      }
409    
410    
411    
412      /**
413       * Appends a string representation of this proxied auth v1 control to the
414       * provided buffer.
415       *
416       * @param  buffer  The buffer to which the information should be appended.
417       */
418      public void toString(StringBuilder buffer)
419      {
420        buffer.append("ProxiedAuthorizationV1Control(authorizationDN=\"");
421        rawAuthorizationDN.toString(buffer);
422        buffer.append("\")");
423      }
424    }
425