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