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 * docs/licenses/cddl.txt
011 * or http://www.opensource.org/licenses/cddl1.php.
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 * docs/licenses/cddl.txt.  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 *      Portions Copyright 2018-2023 Ping Identity Corporation
026 */
027
028package com.unboundid.directory.sdk.broker.api;
029
030import com.unboundid.directory.sdk.broker.config.AdviceConfig;
031import com.unboundid.directory.sdk.broker.internal.BrokerExtension;
032import com.unboundid.directory.sdk.broker.types.AdviceStatement;
033import com.unboundid.directory.sdk.broker.types.BrokerContext;
034import com.unboundid.directory.sdk.broker.types.HttpRequestResponse;
035import com.unboundid.directory.sdk.broker.types.NotFulfilledException;
036import com.unboundid.directory.sdk.broker.types.PolicyRequestDetails;
037import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
038import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
039import com.unboundid.util.Extensible;
040import com.unboundid.util.ThreadSafety;
041import com.unboundid.util.ThreadSafetyLevel;
042import com.unboundid.util.args.ArgumentException;
043import com.unboundid.util.args.ArgumentParser;
044
045import java.util.Collections;
046import java.util.List;
047import java.util.Map;
048
049
050/**
051 * This class defines an API that must be implemented by extensions that
052 * implement custom Advice for policies.  This advice is invoked when policy
053 * evaluation results in this type of advice being returned to the Policy
054 * Enforcement Point.
055 *
056 * <H2>Configuring Advice</H2>
057 * In order to configure policy advice created using this API,
058 * use a command like:
059 *
060 * <PRE>
061 *      dsconfig create-advice \
062 *           --advice-name "<I>{name}</I>" \
063 *           --type third-party \
064 *           --set "advice-id:<I>{advice-code}</I>
065 *           --set "extension-class:<I>{class-name}</I>" \
066 *           --set "extension-argument:<I>{name=value}</I>" \
067 *           --set "evaluation-order-index:<I>{index}</I>
068 * </PRE>
069 * where "<I>{name}</I>" is a user-friendly name to use for the advice
070 * type, "<I>{advice-code}</I>" is a unique advice code that  must match the
071 * "code" string returned from a policy invocation, and "<I>{class-name}</I>"
072 * is the fully-qualified name of the Java class that extends
073 * {@code com.unboundid.directory.sdk.broker.api.Advice}.
074 *
075 * "<I>{index}</I>" is an integer from 1 to 9999 that is used to determine the
076 * order in which this type of advice should be invoked relative to other advice
077 * that may be returned from the same policy evaluation.
078 * "<I>{name=value}</I>" pairs specified using extension-arguments are values
079 * that are provided to the advice implementation at initialization time.
080 * If multiple initialization arguments should be provided to the extension,
081 * then the "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option
082 * should be provided multiple times.
083 */
084@Extensible()
085@BrokerExtension
086@ThreadSafety(level= ThreadSafetyLevel.INTERFACE_THREADSAFE)
087public abstract class Advice
088    implements UnboundIDExtension, ExampleUsageProvider {
089
090  /**
091   * No-args constructor.
092   */
093  public Advice() {
094    // no implementation is required.
095  }
096
097  /**
098   * {@inheritDoc}
099   */
100  @Override
101  public abstract String getExtensionName();
102
103
104  /**
105   * {@inheritDoc}
106   */
107  @Override
108  public abstract String[] getExtensionDescription();
109
110
111  /**
112   * {@inheritDoc}
113   */
114  @Override
115  public Map<List<String>,String> getExamplesArgumentSets() {
116    return Collections.emptyMap();
117  }
118
119
120  /**
121   * {@inheritDoc}
122   */
123  @Override
124  public void defineConfigArguments(final ArgumentParser parser)
125      throws ArgumentException {
126    // No arguments will be allowed by default.
127  }
128
129  /**
130   * Initializes this Advice implementation.
131   * @param  serverContext  A handle to the server context for the server in
132   *                        which this extension is running.
133   * @param  config         The general configuration for this Advice.
134   * @param  parser         The argument parser which has been initialized from
135   *                        the configuration for this Advice.
136   * @throws Exception      If a problem occurs while initializing this Advice.
137   */
138  public void initializeAdvice(
139      final BrokerContext serverContext,
140      final AdviceConfig config,
141      final ArgumentParser parser) throws Exception {
142    // No initialization performed by default.
143  }
144
145
146  /**
147   * Performs any cleanup which may be necessary when this advice
148   * is to be taken out of service.
149   */
150  public void finalizeAdvice() {
151    // No implementation is required.
152  }
153
154
155  /**
156   * This method is invoked when the specified Advice type is returned by
157   * policy evaluation.
158   * @param requestDetails  Details of the authorization request that
159   *                        triggered the advice.
160   * @param statements      List of advice instances with payload and
161   *                        attributes containing advice details.
162   * @param requestResponse HTTP Request or Response that may be examined or
163   *                        manipulated by the advice implementation.  The
164   *                        resource details are dependent upon the API method
165   *                        that requested authorization and the type of request
166   *                        being authorized. As an example, for a create
167   *                        operation the request body may represent the
168   *                        resource the client is attempting to create, while
169   *                        for a retrieve operation the response body may
170   *                        represent the resource to be returned to the client.
171   * @return the (possibly modified) request or response.
172   * @throws NotFulfilledException if the advice cannot be successfully
173   * applied.  If the advice is defined to be obligatory, throwing this
174   * exception will cause the requested operation to fail.  If the advice is
175   * not obligatory, throwing this exception will cause an error to be logged,
176   * however the requested operation will not otherwise be impacted.
177   */
178  public abstract HttpRequestResponse apply(
179      final PolicyRequestDetails requestDetails,
180      List<? extends AdviceStatement> statements,
181      HttpRequestResponse requestResponse) throws NotFulfilledException;
182}