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}