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 *      Copyright 2017-2018 Ping Identity Corporation
026 */
027
028
029package com.unboundid.directory.sdk.broker.api;
030
031import com.unboundid.directory.sdk.broker.config.PolicyAdviceConfig;
032import com.unboundid.directory.sdk.broker.internal.BrokerExtension;
033import com.unboundid.directory.sdk.broker.types.AdviceContext;
034import com.unboundid.directory.sdk.broker.types.BrokerContext;
035import com.unboundid.directory.sdk.broker.types.OpenBankingErrorResponse;
036import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
037import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
038import com.unboundid.scim2.common.exceptions.ScimException;
039import com.unboundid.scim2.common.messages.ErrorResponse;
040import com.unboundid.util.Extensible;
041import com.unboundid.util.ThreadSafety;
042import com.unboundid.util.ThreadSafetyLevel;
043import com.unboundid.util.args.ArgumentException;
044import com.unboundid.util.args.ArgumentParser;
045
046import java.util.Collections;
047import java.util.List;
048import java.util.Map;
049
050
051/**
052 * This class defines an API that must be implemented by extensions that
053 * implement custom Policy Advice.  This advice is invoked when policy
054 * evaluation results in this type of advice being returned to the Policy
055 * Enforcement Point.
056 *
057 * <H2>Configuring Policy Advice</H2>
058 * In order to configure policy advice created using this API,
059 * use a command like:
060 *
061 * <PRE>
062 *      dsconfig create-policy-advice \
063 *           --advice-name "<I>{name}</I>" \
064 *           --type third-party \
065 *           --policy-name "<I>{policy-name}</I>" \
066 *           --rule-name "<I>{rule-name}</I>" \
067 *           --set "extension-class:<I>{class-name}</I>" \
068 *           --set "extension-argument:<I>{name=value}</I>" \
069 *           --set "advice-arguments:<I>{name=jexlExpression}</I>" \
070 *           --set "evaluation-order-index:<I>{index}</I>
071 * </PRE>
072 * where "<I>{name}</I>" is the name to use for the policy advice
073 * instance, "<I>{class-name}</I>" is the fully-qualified name of the Java
074 * class that extends
075 * {@code com.unboundid.directory.sdk.broker.api.PolicyAdvice},
076 * "<I>{policy-name}</I>" and <I>{rule-name}</I>" identify the policy and
077 * policy rule, respectively, that will contain this advice expression.
078 * <I>{name=jexlExpression}</I> pairs specified using advice-arguments identify
079 * arguments whose value may be specified or computed during policy evaluation
080 * before being passed into this advice implementation.
081 * "<I>{index}</I>" is an integer from 1 to 9999 that is used to determine the
082 * order in which this type of advice should be invoked relative to other advice
083 * that may be returned from the same policy evaluation.
084 * "<I>{name=value}</I>" pairs specified using extension-arguments are values
085 * that are provided to the advice implementation at initialization time.
086 * If multiple advice arguments should be provided at each invocation, then the
087 * "<CODE>--set advice-argument:<I>{name=jexlExpression}</I></CODE>" option
088 * should be provided multiple times.
089 * If multiple initialization arguments should be provided to the extension,
090 * then the "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option
091 * should be provided multiple times.
092 */
093@Extensible()
094@BrokerExtension
095@ThreadSafety(level= ThreadSafetyLevel.INTERFACE_THREADSAFE)
096public abstract class PolicyAdvice
097    implements UnboundIDExtension,
098    ExampleUsageProvider {
099
100  /**
101   * No-args constructor.
102   */
103  public PolicyAdvice() {
104    // no implementation required.
105  }
106
107
108  /**
109   * {@inheritDoc}
110   */
111  @Override
112  public abstract String getExtensionName();
113
114
115  /**
116   * {@inheritDoc}
117   */
118  @Override
119  public abstract String[] getExtensionDescription();
120
121
122  /**
123   * {@inheritDoc}
124   */
125  @Override
126  public Map<List<String>,String> getExamplesArgumentSets() {
127    return Collections.emptyMap();
128  }
129
130
131  /**
132   * {@inheritDoc}
133   */
134  @Override
135  public void defineConfigArguments(final ArgumentParser parser)
136      throws ArgumentException {
137    // No arguments will be allowed by default.
138  }
139
140
141  /**
142   * Initializes this Policy Advice implementation.
143   * @param  serverContext  A handle to the server context for the server in
144   *                        which this extension is running.
145   * @param  adviceCfg      The general configuration for this policy advice.
146   * @param  parser         The argument parser which has been initialized from
147   *                        the configuration for this policy advice.
148   *
149   * @throws Exception      If a problem occurs while initializing this
150   *                        policy advice.
151   */
152  public void initializePolicyAdvice(
153      final BrokerContext serverContext,
154      final PolicyAdviceConfig adviceCfg,
155      final ArgumentParser parser) throws Exception {
156    // No initialization will be performed by default.
157  }
158
159
160  /**
161   * Performs any cleanup which may be necessary when this policy advice
162   * is to be taken out of service.
163   */
164  public void finalizePolicyAdvice() {
165    // no implementation is required
166  }
167
168
169  /**
170   * This method is called if this advice is returned by policy when a SCIM
171   * request is denied.
172   * @param context     AdviceContext containing any arguments passed
173   *                    from policy.
174   * @param errorInfo   the default error information that will be
175   *                    returned if nothing is done by the advice
176   *                    implementation.
177   * @return            the modified error info
178   * @throws ScimException if an internal error occurs trying to generate
179   *                    the advice.
180   */
181  public ErrorResponse onScimDeny(
182      final AdviceContext context,
183      final ErrorResponse errorInfo) throws ScimException {
184
185    // default implementation:  no-op.
186    return errorInfo;
187  }
188
189  /**
190   * This method is called if this advice is returned by policy when an Open
191   * Banking API request is denied.
192   * @param context     AdviceContext containing any arguments passed
193   *                    from policy.
194   * @param errorInfo   the default error information that will be
195   *                    returned if nothing is done by the advice
196   *                    implementation.
197   * @return            the modified error info
198   */
199  public OpenBankingErrorResponse onOpenBankingDeny(
200      final AdviceContext context,
201      final OpenBankingErrorResponse errorInfo)  {
202
203    // default implementation: no-op
204    return errorInfo;
205  }
206}