001/*
002 * Copyright 2008-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-2019 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.sdk.unboundidds.controls;
022
023
024
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.Collections;
028import java.util.Iterator;
029import java.util.List;
030
031import com.unboundid.asn1.ASN1Boolean;
032import com.unboundid.asn1.ASN1Element;
033import com.unboundid.asn1.ASN1OctetString;
034import com.unboundid.asn1.ASN1Sequence;
035import com.unboundid.ldap.sdk.Control;
036import com.unboundid.ldap.sdk.LDAPException;
037import com.unboundid.ldap.sdk.ResultCode;
038import com.unboundid.util.Debug;
039import com.unboundid.util.NotMutable;
040import com.unboundid.util.StaticUtils;
041import com.unboundid.util.ThreadSafety;
042import com.unboundid.util.ThreadSafetyLevel;
043
044import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
045
046
047
048/**
049 * This class provides an implementation of an LDAP control that can be included
050 * in a bind request to request that the Directory Server return the
051 * authentication and authorization entries for the user that authenticated.
052 * <BR>
053 * <BLOCKQUOTE>
054 *   <B>NOTE:</B>  This class, and other classes within the
055 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
056 *   supported for use against Ping Identity, UnboundID, and
057 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
058 *   for proprietary functionality or for external specifications that are not
059 *   considered stable or mature enough to be guaranteed to work in an
060 *   interoperable way with other types of LDAP servers.
061 * </BLOCKQUOTE>
062 * <BR>
063 * The value of this control may be absent, but if it is present then will be
064 * encoded as follows:
065 * <PRE>
066 *   GetAuthorizationEntryRequest ::= SEQUENCE {
067 *        includeAuthNEntry     [0] BOOLEAN DEFAULT TRUE,
068 *        includeAuthZEntry     [1] BOOLEAN DEFAULT TRUE,
069 *        attributes            [2] AttributeSelection OPTIONAL }
070 * </PRE>
071 * <BR><BR>
072 * <H2>Example</H2>
073 * The following example demonstrates the process for processing a bind
074 * operation using the get authorization entry request control to return all
075 * user attributes in both the authentication and authorization entries:
076 * <PRE>
077 * ReadOnlyEntry authNEntry = null;
078 * ReadOnlyEntry authZEntry = null;
079 *
080 * BindRequest bindRequest = new SimpleBindRequest(
081 *      "uid=john.doe,ou=People,dc=example,dc=com", "password",
082 *      new GetAuthorizationEntryRequestControl());
083 *
084 * BindResult bindResult = connection.bind(bindRequest);
085 * GetAuthorizationEntryResponseControl c =
086 *      GetAuthorizationEntryResponseControl.get(bindResult);
087 * if (c != null)
088 * {
089 *   authNEntry = c.getAuthNEntry();
090 *   authZEntry = c.getAuthZEntry();
091 * }
092 * </PRE>
093 */
094@NotMutable()
095@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
096public final class GetAuthorizationEntryRequestControl
097       extends Control
098{
099  /**
100   * The OID (1.3.6.1.4.1.30221.2.5.6) for the get authorization entry request
101   * control.
102   */
103  public static final String GET_AUTHORIZATION_ENTRY_REQUEST_OID =
104       "1.3.6.1.4.1.30221.2.5.6";
105
106
107
108  /**
109   * The BER type for the {@code includeAuthNEntry} element.
110   */
111  private static final byte TYPE_INCLUDE_AUTHN_ENTRY = (byte) 0x80;
112
113
114
115  /**
116   * The BER type for the {@code includeAuthZEntry} element.
117   */
118  private static final byte TYPE_INCLUDE_AUTHZ_ENTRY = (byte) 0x81;
119
120
121
122  /**
123   * The BER type for the {@code attributes} element.
124   */
125  private static final byte TYPE_ATTRIBUTES = (byte) 0xA2;
126
127
128
129  /**
130   * The serial version UID for this serializable class.
131   */
132  private static final long serialVersionUID = -5540345171260624216L;
133
134
135
136  // Indicates whether to include the authentication entry in the response.
137  private final boolean includeAuthNEntry;
138
139  // Indicates whether to include the authorization entry in the response.
140  private final boolean includeAuthZEntry;
141
142  // The list of attributes to include in entries that are returned.
143  private final List<String> attributes;
144
145
146
147  /**
148   * Creates a new get authorization entry request control that will request all
149   * user attributes in both the authentication and authorization entries.  It
150   * will not be marked critical.
151   */
152  public GetAuthorizationEntryRequestControl()
153  {
154    this(false, true, true, (List<String>) null);
155  }
156
157
158
159  /**
160   * Creates a new get authorization entry request control with the provided
161   * information.
162   *
163   * @param  includeAuthNEntry  Indicates whether to include the authentication
164   *                            entry in the response.
165   * @param  includeAuthZEntry  Indicates whether to include the authorization
166   *                            entry in the response.
167   * @param  attributes         The attributes to include in the entries in the
168   *                            response.  It may be empty or {@code null} to
169   *                            request all user attributes.
170   */
171  public GetAuthorizationEntryRequestControl(final boolean includeAuthNEntry,
172                                             final boolean includeAuthZEntry,
173                                             final String... attributes)
174  {
175    this(false, includeAuthNEntry, includeAuthZEntry,
176         (attributes == null) ? null : Arrays.asList(attributes));
177  }
178
179
180
181  /**
182   * Creates a new get authorization entry request control with the provided
183   * information.
184   *
185   * @param  includeAuthNEntry  Indicates whether to include the authentication
186   *                            entry in the response.
187   * @param  includeAuthZEntry  Indicates whether to include the authorization
188   *                            entry in the response.
189   * @param  attributes         The attributes to include in the entries in the
190   *                            response.  It may be empty or {@code null} to
191   *                            request all user attributes.
192   */
193  public GetAuthorizationEntryRequestControl(final boolean includeAuthNEntry,
194                                             final boolean includeAuthZEntry,
195                                             final List<String> attributes)
196  {
197    this(false, includeAuthNEntry, includeAuthZEntry, attributes);
198  }
199
200
201
202  /**
203   * Creates a new get authorization entry request control with the provided
204   * information.
205   *
206   * @param  isCritical         Indicates whether the control should be marked
207   *                            critical.
208   * @param  includeAuthNEntry  Indicates whether to include the authentication
209   *                            entry in the response.
210   * @param  includeAuthZEntry  Indicates whether to include the authorization
211   *                            entry in the response.
212   * @param  attributes         The attributes to include in the entries in the
213   *                            response.  It may be empty or {@code null} to
214   *                            request all user attributes.
215   */
216  public GetAuthorizationEntryRequestControl(final boolean isCritical,
217                                             final boolean includeAuthNEntry,
218                                             final boolean includeAuthZEntry,
219                                             final String... attributes)
220  {
221    this(isCritical, includeAuthNEntry, includeAuthZEntry,
222         (attributes == null) ? null : Arrays.asList(attributes));
223  }
224
225
226
227  /**
228   * Creates a new get authorization entry request control with the provided
229   * information.
230   *
231   * @param  isCritical         Indicates whether the control should be marked
232   *                            critical.
233   * @param  includeAuthNEntry  Indicates whether to include the authentication
234   *                            entry in the response.
235   * @param  includeAuthZEntry  Indicates whether to include the authorization
236   *                            entry in the response.
237   * @param  attributes         The attributes to include in the entries in the
238   *                            response.  It may be empty or {@code null} to
239   *                            request all user attributes.
240   */
241  public GetAuthorizationEntryRequestControl(final boolean isCritical,
242                                             final boolean includeAuthNEntry,
243                                             final boolean includeAuthZEntry,
244                                             final List<String> attributes)
245  {
246    super(GET_AUTHORIZATION_ENTRY_REQUEST_OID, isCritical,
247          encodeValue(includeAuthNEntry, includeAuthZEntry, attributes));
248
249    this.includeAuthNEntry = includeAuthNEntry;
250    this.includeAuthZEntry = includeAuthZEntry;
251
252    if ((attributes == null) || attributes.isEmpty())
253    {
254      this.attributes = Collections.emptyList();
255    }
256    else
257    {
258      this.attributes =
259           Collections.unmodifiableList(new ArrayList<>(attributes));
260    }
261  }
262
263
264
265  /**
266   * Creates a new get authorization entry request control which is decoded from
267   * the provided generic control.
268   *
269   * @param  control  The generic control to decode as a get authorization entry
270   *                  request control.
271   *
272   * @throws  LDAPException  If the provided control cannot be decoded as a get
273   *                         authorization entry request control.
274   */
275  public GetAuthorizationEntryRequestControl(final Control control)
276         throws LDAPException
277  {
278    super(control);
279
280    final ASN1OctetString value = control.getValue();
281    if (value == null)
282    {
283      includeAuthNEntry = true;
284      includeAuthZEntry = true;
285      attributes        = Collections.emptyList();
286      return;
287    }
288
289    try
290    {
291      final ArrayList<String> attrs = new ArrayList<>(20);
292      boolean includeAuthN = true;
293      boolean includeAuthZ = true;
294
295      final ASN1Element element = ASN1Element.decode(value.getValue());
296      for (final ASN1Element e :
297           ASN1Sequence.decodeAsSequence(element).elements())
298      {
299        switch (e.getType())
300        {
301          case TYPE_INCLUDE_AUTHN_ENTRY:
302            includeAuthN = ASN1Boolean.decodeAsBoolean(e).booleanValue();
303            break;
304          case TYPE_INCLUDE_AUTHZ_ENTRY:
305            includeAuthZ = ASN1Boolean.decodeAsBoolean(e).booleanValue();
306            break;
307          case TYPE_ATTRIBUTES:
308            for (final ASN1Element ae :
309                 ASN1Sequence.decodeAsSequence(e).elements())
310            {
311              attrs.add(ASN1OctetString.decodeAsOctetString(ae).stringValue());
312            }
313            break;
314          default:
315            throw new LDAPException(ResultCode.DECODING_ERROR,
316                 ERR_GET_AUTHORIZATION_ENTRY_REQUEST_INVALID_SEQUENCE_ELEMENT.
317                      get(StaticUtils.toHex(e.getType())));
318        }
319      }
320
321      includeAuthNEntry = includeAuthN;
322      includeAuthZEntry = includeAuthZ;
323      attributes        = attrs;
324    }
325    catch (final LDAPException le)
326    {
327      throw le;
328    }
329    catch (final Exception e)
330    {
331      Debug.debugException(e);
332      throw new LDAPException(ResultCode.DECODING_ERROR,
333           ERR_GET_AUTHORIZATION_ENTRY_REQUEST_CANNOT_DECODE_VALUE.get(
334                StaticUtils.getExceptionMessage(e)),
335           e);
336    }
337  }
338
339
340
341  /**
342   * Encodes the provided information as appropriate for use as the value of
343   * this control.
344   *
345   * @param  includeAuthNEntry  Indicates whether to include the authentication
346   *                            entry in the response.
347   * @param  includeAuthZEntry  Indicates whether to include the authorization
348   *                            entry in the response.
349   * @param  attributes         The attributes to include in the entries in the
350   *                            response.  It may be empty or {@code null} to
351   *                            request all user attributes.
352   *
353   * @return  An ASN.1 octet string appropriately encoded for use as the control
354   *          value, or {@code null} if no value is needed.
355   */
356  private static ASN1OctetString encodeValue(final boolean includeAuthNEntry,
357                                             final boolean includeAuthZEntry,
358                                             final List<String> attributes)
359  {
360    if (includeAuthNEntry && includeAuthZEntry &&
361        ((attributes == null) || attributes.isEmpty()))
362    {
363      return null;
364    }
365
366    final ArrayList<ASN1Element> elements = new ArrayList<>(3);
367
368    if (! includeAuthNEntry)
369    {
370      elements.add(new ASN1Boolean(TYPE_INCLUDE_AUTHN_ENTRY, false));
371    }
372
373    if (! includeAuthZEntry)
374    {
375      elements.add(new ASN1Boolean(TYPE_INCLUDE_AUTHZ_ENTRY, false));
376    }
377
378    if ((attributes != null) && (! attributes.isEmpty()))
379    {
380      final ArrayList<ASN1Element> attrElements =
381           new ArrayList<>(attributes.size());
382      for (final String s : attributes)
383      {
384        attrElements.add(new ASN1OctetString(s));
385      }
386
387      elements.add(new ASN1Sequence(TYPE_ATTRIBUTES, attrElements));
388    }
389
390    return new ASN1OctetString(new ASN1Sequence(elements).encode());
391  }
392
393
394
395  /**
396   * Indicates whether the entry for the authenticated user should be included
397   * in the response control.
398   *
399   * @return  {@code true} if the entry for the authenticated user should be
400   *          included in the response control, or {@code false} if not.
401   */
402  public boolean includeAuthNEntry()
403  {
404    return includeAuthNEntry;
405  }
406
407
408
409  /**
410   * Indicates whether the entry for the authorized user should be included
411   * in the response control.
412   *
413   * @return  {@code true} if the entry for the authorized user should be
414   *          included in the response control, or {@code false} if not.
415   */
416  public boolean includeAuthZEntry()
417  {
418    return includeAuthZEntry;
419  }
420
421
422
423  /**
424   * Retrieves the attributes that will be requested for the authentication
425   * and/or authorization entries.
426   *
427   * @return  The attributes that will be requested for the authentication
428   *          and/or authorization entries, or an empty list if all user
429   *          attributes should be included.
430   */
431  public List<String> getAttributes()
432  {
433    return attributes;
434  }
435
436
437
438  /**
439   * {@inheritDoc}
440   */
441  @Override()
442  public String getControlName()
443  {
444    return INFO_CONTROL_NAME_GET_AUTHORIZATION_ENTRY_REQUEST.get();
445  }
446
447
448
449  /**
450   * {@inheritDoc}
451   */
452  @Override()
453  public void toString(final StringBuilder buffer)
454  {
455    buffer.append("GetAuthorizationEntryRequestControl(isCritical=");
456    buffer.append(isCritical());
457    buffer.append(", includeAuthNEntry=");
458    buffer.append(includeAuthNEntry);
459    buffer.append(", includeAuthZEntry=");
460    buffer.append(includeAuthZEntry);
461    buffer.append(", attributes={");
462
463    final Iterator<String> iterator = attributes.iterator();
464    while (iterator.hasNext())
465    {
466      buffer.append(iterator.next());
467      if (iterator.hasNext())
468      {
469        buffer.append(", ");
470      }
471    }
472
473    buffer.append("})");
474  }
475}