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 2010-2024 Ping Identity Corporation
026 */
027package com.unboundid.directory.sdk.broker.api;
028
029import com.unboundid.directory.sdk.broker.internal.BrokerExtension;
030import com.unboundid.directory.sdk.broker.config.PolicyDecisionLoggerConfig;
031import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
032import com.unboundid.directory.sdk.common.internal.Reconfigurable;
033import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
034import com.unboundid.directory.sdk.broker.types.PolicyMessageType;
035import com.unboundid.directory.sdk.common.types.ServerContext;
036import com.unboundid.ldap.sdk.LDAPException;
037import com.unboundid.ldap.sdk.ResultCode;
038import com.unboundid.util.Extensible;
039import com.unboundid.util.ThreadSafety;
040import com.unboundid.util.ThreadSafetyLevel;
041import com.unboundid.util.args.ArgumentException;
042import com.unboundid.util.args.ArgumentParser;
043
044import java.util.Collections;
045import java.util.List;
046import java.util.Map;
047
048/**
049 * This class defines an API that must be implemented by extensions which
050 * record information about PingAuthorize policy enforcement point
051 * (PEP) and policy decision point (PDP) activity
052 * <BR><BR>
053 * Each policy decision logger may be configured to indicate whether to
054 * include the PDP response or exclude log messages based on their Policy
055 * Message Type.  This is handled automatically by the server, so individual
056 * policy decision logger implementations do not need to attempt to perform that
057 * filtering on their own.  However, they may perform additional processing
058 * if desired to further narrow the set of messages that should be logged.
059 * <BR>
060 * <H2>Configuring Policy Decision Loggers</H2>
061 * To configure a policy decision created using this API, use a command
062 * like:
063 * <PRE>
064 *      dsconfig create-log-publisher \
065 *           --publisher-name "<I>{logger-name}</I>" \
066 *           --type third-party-policy-decision \
067 *           --set enabled:true \
068 *           --set "extension-class:<I>{class-name}</I>" \
069 *           --set "extension-argument:<I>{name=value}</I>"
070 * </PRE>
071 * where "<I>{logger-name}</I>" is the name
072 * to use for the policy decision logger
073 * instance, "<I>{class-name}</I>" is the fully-qualified name of the Java class
074 * that extends {@code com.unboundid.directory.sdk.common.api
075 * .PolicyDecisionLogger},
076 * and "<I>{name=value}</I>" represents name-value pairs for any arguments to
077 * provide to the logger.  If multiple arguments should be provided to the
078 * logger, then the "<CODE>--set extension-argument:<I>{name=value}</I></CODE>"
079 * option should be provided multiple times.
080 *
081 * <p>See ExamplePolicyDecisionLogger for a basic implementation.</p>
082 *
083 * <p><strong>Tips:</strong></p>
084 * <ul>
085 *   <li>Ensure the custom logger handles both DECISION and ADVICE policy
086 *   message types.</li>
087 *   <li>Review the method-level documentation before implementing the log
088 *   method. This documentation contains important information about the data
089 *   being passed to the log method, including the contexts where such data
090 *   is present.</li>
091 * </ul>
092 */
093@Extensible()
094@BrokerExtension()
095@ThreadSafety(level= ThreadSafetyLevel.INTERFACE_THREADSAFE)
096public abstract class PolicyDecisionLogger implements UnboundIDExtension,
097        Reconfigurable<PolicyDecisionLoggerConfig>, ExampleUsageProvider {
098
099    /**
100     * Creates a new instance of this policy decision logger.
101     * All policy decision logger implementations must include a default
102     * constructor, but any initialization should generally be done in
103     * the {@code initializePolicyDecisionLogger} method.
104     */
105    public PolicyDecisionLogger()
106    {
107        // No implementation is required.
108    }
109
110    /**
111     * {@inheritDoc}
112     */
113    public void defineConfigArguments(final ArgumentParser parser)
114            throws ArgumentException
115    {
116        // No arguments will be allowed by default.
117    }
118
119    /**
120     * Initializes this policy decision logger.
121     *
122     * @param  serverContext  A handle to the server context for the server in
123     *                        which this extension is running.
124     * @param  config         The general configuration for
125     *                        this policy decision logger.
126     * @param  parser         The argument parser which
127     *                        has been initialized from the configuration
128     *                        for this policy decision logger.
129     *
130     * @throws LDAPException  If a problem occurs while initializing
131     * this policy decision logger.
132     */
133    public void initializePolicyDecisionLogger(
134            final ServerContext serverContext,
135                                      final PolicyDecisionLoggerConfig config,
136                                      final ArgumentParser parser)
137            throws LDAPException
138    {
139        // No initialization will be performed by default.
140    }
141
142    /**
143     * Performs any cleanup which may be necessary when this
144     * policy decision logger is to be taken out of service.
145     */
146    public void finalizePolicyDecisionLogger()
147    {
148        // No implementation is required.
149    }
150
151    /**
152     * Logs a message.
153     *
154     * @param messageType The {@link PolicyMessageType} indicates the stage
155     *                    being logged in the decision processing lifecycle.
156     *                    The provided message type can be either
157     *                    {@link PolicyMessageType#ADVICE} or
158     *                    {@link PolicyMessageType#DECISION}.
159     * <p>
160     * @param logContext  A set of key/value pairs summarizing and providing
161     *                    context for the policy decision or advice.
162     *                    <p>
163     *                    <strong>Shared Keys:</strong> These keys are included
164     *                    in the logContext for both DECISION and ADVICE message
165     *                    types:
166     *                    </p>
167     * <table>
168     * <caption>Shared Keys</caption>
169     * <tr><td>{@code requestID}</td>
170     * <td>The unique identifier for the request.</td></tr>
171     * <tr><td>{@code correlationID}</td>
172     * <td>The correlation identifier for tracking requests.</td></tr>
173     * <tr><td>{@code product}</td>
174     * <td>The product name generating the log
175     *                    (e.g. Ping Identity PingAuthorize Server).</td></tr>
176     * <tr><td>{@code instanceName}</td>
177     * <td>The name of the server instance.</td></tr>
178     * <tr><td>{@code clusterName}</td>
179     * <td>The cluster name the instance belongs to.</td></tr>
180     * <tr><td>{@code startupID}</td>
181     * <td>A compact identifier that was assigned to the
182     *                    server instance at startup.</td></tr>
183     * <tr><td>{@code threadID}</td>
184     * <td>The identifier for the thread handling the request.</td></tr>
185     * <tr><td>{@code from}</td>
186     * <td>The source IP address and port of the request.</td></tr>
187     * <tr><td>{@code method}</td>
188     * <td>The HTTP method used in the request.</td></tr>
189     * <tr><td>{@code url}</td>
190     * <td>The request URL. Note that the logged value might differ slightly
191     *                    from the client-requested URL in minor ways.</td></tr>
192     * <tr><td>{@code clientId}</td>
193     * <td>The client ID. The logged value depends on the
194     *                    Access Token Validator handling the request.</td></tr>
195     * <tr><td>{@code tokenOwner}</td>
196     * <td>The value depends on the Token Resource Lookup Method performed by a
197     *                    handling Access Token Validator, and might not be
198     *                    present if token owner lookup is skipped.</td>
199     * <tr><td>{@code action}</td>
200     * <td>The policy request action (e.g., "inbound-GET").</td></tr>
201     * <tr><td>{@code service}</td>
202     * <td>The service name provided in the decision request. This value will
203     vary depending on whether the client request is a SCIM2, Gateway/Sideband,
204     or a direct request to the policy decision engine
205     (like a JSON PDP API request).</td></tr>
206     * <tr><td>{@code resourcePath}</td>
207     * <td>The path to the resource involved in the request.
208     This value will vary depending on whether the client request is a SCIM2,
209     Gateway/Sideband, or a direct request to the policy decision engine
210     (like a JSON PDP API request).</td></tr>
211     * <tr><td>{@code deploymentPackageId}</td>
212     * <td>The unique identifier for the deployment package used
213     *                    during policy evaluation.</td></tr>
214     * <tr><td>{@code decisionId}</td>
215     * <td>The unique identifier for the decision.</td></tr>
216     * <tr><td>{@code authorized}</td>
217     * <td>Indicates if the request was authorized (true or false).</td></tr>
218     * <tr><td>{@code decisionStatusCode}</td>
219     * <td>The policy decision status code. Commonly "OKAY", but values such as
220     *                    "MISSING_ATTRIBUTE" in case of an error.</td></tr>
221     * <tr><td>{@code decision}</td>
222     * <td>The result of the decision (e.g., PERMIT, DENY).</td></tr>
223     * </table>
224     * <p>
225     * <strong>DECISION Only Keys:</strong> These keys are included in the
226     *                    logContext only for DECISION message types:
227     * <table>
228     * <caption>DECISION Only Keys</caption>
229     * <tr><td>{@code snapshotId}</td>
230     * <td>The ID of the policy snapshot.</td></tr>
231     * <tr><td>{@code decisionNodeId}</td>
232     * <td>The ID of the deployment package's root decision node.</td></tr>
233     * <tr><td>{@code adviceIds}</td>
234     * <td>A comma-separated list of advice identifiers included
235     *                    in the decision response.</td></tr>
236     * <tr><td>{@code adviceNames}</td>
237     * <td>A comma-separated list of advice names included in the decision
238     *                    response. These are the names that policy writers
239     *                    used when constructing the policies.</td></tr>
240     * <tr><td>{@code domain}</td>
241     * <td>The decision request domain, if provided.</td></tr>
242     * <tr><td>{@code identityProvider}</td>
243     * <td>The identity provider included on the decision request. For client
244     *                    requests triggering Access Token Validation
245     *                    (e.g. Gateway, Sideband, etc.), this will be the
246     *                    name of the used Access Token Validator.</td></tr>
247     * </table>
248     * <p>
249     * <strong>ADVICE Only Keys:</strong>
250     *                    These keys are included in the logContext only for
251     *                    ADVICE message types:
252     * <table>
253     * <caption>ADVICE Only Keys</caption>
254     * <tr><td>{@code adviceImplId}</td>
255     * <td>The advice implementation ID, corresponding to an advice ID in the
256     * configuration (e.g., "denied-reason").</td></tr>
257     * <tr><td>{@code adviceImplName}</td>
258     * <td>The advice implementation name, corresponding to an advice name in
259     *                   the configuration (e.g., "Denied Reason Advice").
260     *                   </td></tr>
261     * <tr><td>{@code obligatory}</td>
262     * <td>Indicates if the advice is obligatory (true or false).</td></tr>
263     * <tr><td>{@code resourceModified}</td>
264     * <td>Indicates whether the advice processing resulted in a modification
265     *                    to the HTTP request (for inbound advice processing)
266     *                    or the HTTP response (for outbound advice processing).
267     *                    </td></tr>
268     * </table>
269     * <p>
270     * @param message     The full response returned by the PDP in
271     *                    JSON format, which is logged if the DECISION policy
272     *                    message type is passed.
273     *                    If the include-pdp-response property is disabled or
274     *                    the message type does not record a decision response,
275     *                    the PDP response is null.
276     *
277     * <p>
278     *                    The PDP message object is always null when the
279     *                    ADVICE policy message type is provided.
280     * </p>
281     */
282    public abstract void log(
283            final PolicyMessageType messageType,
284            final Map<String, String> logContext,
285            final String message);
286
287    /**
288     * {@inheritDoc}
289     */
290    public boolean isConfigurationAcceptable(
291            final PolicyDecisionLoggerConfig config,
292            final ArgumentParser parser,
293            final List<String> unacceptableReasons)
294    {
295        // No extended validation will be performed by default.
296        return true;
297    }
298
299    /**
300     * {@inheritDoc}
301     */
302    public ResultCode applyConfiguration(
303            final PolicyDecisionLoggerConfig config,
304            final ArgumentParser parser,
305            final List<String> adminActionsRequired,
306            final List<String> messages)
307    {
308        // By default, no configuration changes will be applied.
309        // If there are any arguments,
310        // then add an admin action message indicating that the extension
311        // needs to be restarted for any changes to take effect.
312        if (! parser.getNamedArguments().isEmpty())
313        {
314            adminActionsRequired.add(
315                    "No configuration change has actually been applied." +
316                            " The new configuration will not take effect" +
317                            " until this Policy Decision Logger is disabled" +
318                            " and re-enabled or until the server" +
319                            " is restarted.");
320        }
321        return ResultCode.SUCCESS;
322    }
323
324    /**
325     * {@inheritDoc}
326     */
327    public abstract String getExtensionName();
328
329    /**
330     * {@inheritDoc}
331     */
332    public abstract String[] getExtensionDescription();
333
334    /**
335     * {@inheritDoc}
336     */
337    public Map<List<String>,String> getExamplesArgumentSets()
338    {
339        return Collections.emptyMap();
340    }
341}