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