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 2022-2023 Ping Identity Corporation 026 */ 027package com.unboundid.directory.sdk.sync.api; 028 029 030 031import java.util.Collections; 032import java.util.List; 033import java.util.Map; 034 035import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider; 036import com.unboundid.directory.sdk.common.internal.Reconfigurable; 037import com.unboundid.directory.sdk.common.internal.UnboundIDExtension; 038import com.unboundid.directory.sdk.sync.config.SCIM2AttributeMappingConfig; 039import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension; 040import com.unboundid.directory.sdk.sync.types.SyncServerContext; 041import com.unboundid.ldap.sdk.Entry; 042import com.unboundid.ldap.sdk.LDAPException; 043import com.unboundid.ldap.sdk.ResultCode; 044import com.unboundid.util.Extensible; 045import com.unboundid.util.ThreadSafety; 046import com.unboundid.util.ThreadSafetyLevel; 047import com.unboundid.util.args.ArgumentException; 048import com.unboundid.util.args.ArgumentParser; 049import com.unboundid.util.json.JSONField; 050import com.unboundid.util.json.JSONObject; 051 052 053 054/** 055 * This class defines an API that must be implemented by extensions that may be 056 * used to convert attributes between the mapped LDAP representation of an entry 057 * and the SCIM 2.0 representation of that entry. A SCIM 2.0 mapped attribute 058 * will exist as a single attribute in a SCIM entry, and it may correlate to 059 * zero, one or multiple attributes in the corresponding source entry. That is, 060 * the SCIM attribute may directly correlate to a single source attribute, it 061 * may be generated from multiple attributes in the source entry, or it may be 062 * generated without using any content from the source entry. 063 * 064 * <H2>Configuring SCIM2 Attribute Mappings</H2> 065 * In order to configure a SCIM2 attribute mapping created using this API, use 066 * a command like: 067 * <PRE> 068 * dsconfig create-scim2-attribute-mapping \ 069 * --sync-mapping-name "<I>{name}</I>" \ 070 * --type third-party \ 071 * --set "extension-class:<I>{class-name}</I>" \ 072 * --set "extension-argument:<I>{name=value}</I>" 073 * </PRE> 074 * where "<I>{name}</I>" is the name to use for the SCIM2 attribute mapping 075 * instance, "<I>{class-name}</I>" is the fully-qualified name of the Java class 076 * that extends 077 * {@code com.unboundid.directory.sdk.sync.api.SCIM2AttributeMapping}, 078 * and "<I>{name=value}</I>" represents name-value pairs for any arguments to 079 * provide to the attribute mapping. If multiple arguments should be 080 * provided to extension, then the 081 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be 082 * provided multiple times. 083 */ 084@Extensible() 085@SynchronizationServerExtension(appliesToLocalContent=false, 086 appliesToSynchronizedContent=true) 087@ThreadSafety(level= ThreadSafetyLevel.INTERFACE_THREADSAFE) 088public abstract class SCIM2AttributeMapping 089 implements UnboundIDExtension, 090 Reconfigurable<SCIM2AttributeMappingConfig>, 091 ExampleUsageProvider 092{ 093 /** 094 * Creates a new instance of this SCIM2 attribute mapping. All 095 * implementations must include a default constructor, but any initialization 096 * should generally be done in the {@link #initializeAttributeMapping} method. 097 */ 098 public SCIM2AttributeMapping() 099 { 100 // No implementation required by default. 101 } 102 103 104 105 /** 106 * {@inheritDoc} 107 */ 108 @Override() 109 public abstract String getExtensionName(); 110 111 112 113 /** 114 * {@inheritDoc} 115 */ 116 @Override() 117 public abstract String[] getExtensionDescription(); 118 119 120 121 /** 122 * {@inheritDoc} 123 */ 124 @Override() 125 public Map<List<String>,String> getExamplesArgumentSets() 126 { 127 return Collections.emptyMap(); 128 } 129 130 131 132 /** 133 * {@inheritDoc} 134 */ 135 @Override() 136 public void defineConfigArguments(final ArgumentParser parser) 137 throws ArgumentException 138 { 139 // No arguments will be allowed by default. 140 } 141 142 143 144 /** 145 * Performs any necessary initialization for this SCIM2 attribute mapping. 146 * 147 * @param serverContext A handle to the server context for the server in 148 * which this extension is running. Extensions should 149 * typically store this in a class member. 150 * @param config The general configuration for this object. 151 * @param parser The argument parser which has been initialized from 152 * the configuration for this SCIM2 attribute mapping. 153 * 154 * @throws LDAPException If a problem occurs while initializing this SCIM2 155 * attribute mapping. 156 */ 157 public void initializeAttributeMapping( 158 final SyncServerContext serverContext, 159 final SCIM2AttributeMappingConfig config, 160 final ArgumentParser parser) 161 throws LDAPException 162 { 163 // No initialization will be performed by default. 164 } 165 166 167 168 /** 169 * {@inheritDoc} 170 */ 171 @Override() 172 public boolean isConfigurationAcceptable( 173 final SCIM2AttributeMappingConfig config, 174 final ArgumentParser parser, 175 final List<String> unacceptableReasons) 176 { 177 // No extended validation will be performed by default. 178 return true; 179 } 180 181 182 183 /** 184 * {@inheritDoc} 185 */ 186 @Override() 187 public ResultCode applyConfiguration( 188 final SCIM2AttributeMappingConfig config, 189 final ArgumentParser parser, 190 final List<String> adminActionsRequired, 191 final List<String> messages) 192 { 193 // By default, no configuration changes will be applied. If there are any 194 // arguments, then add an admin action message indicating that the extension 195 // needs to be restarted for any changes to take effect. 196 if (! parser.getNamedArguments().isEmpty()) 197 { 198 adminActionsRequired.add( 199 "No configuration change has actually been applied. The new " + 200 "configuration will not take effect until this SCIM2 " + 201 "attribute mapping is disabled and re-enabled or until the " + 202 "server is restarted."); 203 } 204 205 return ResultCode.SUCCESS; 206 } 207 208 209 210 /** 211 * Performs any necessary cleanup work when this attribute mapping is taken 212 * out of service. 213 */ 214 public void finalizeAttributeMapping() 215 { 216 // No implementation is performed by default. 217 } 218 219 220 221 /** 222 * Indicates whether this SCIM2 attribute mapping targets a single-valued 223 * SCIM 2.0 attribute. 224 * 225 * @return {@code true} if this SCIM2 attribute mapping targets a 226 * single-valued SCIM 2.0 attribute, or {@code false} if it targets 227 * an attribute that may have multiple values or is defined as 228 * multivalued in the schema. 229 */ 230 public abstract boolean isSingleValued(); 231 232 233 234 /** 235 * Constructs the SCIM2 representation of an attribute from the information 236 * in the provided mapped LDAP representation of an entry as created by a 237 * sync class. 238 * 239 * @param mappedLDAPEntry The mapped LDAP representation of an entry as 240 * created by a sync class. It must not be 241 * {@code null}. 242 * 243 * @return A JSON field that represents the SCIM 2.0 representation of the 244 * attribute. It may be {@code null} if the provided entry does not 245 * contain enough information to create the SCIM2 attribute (e.g., 246 * if the associated LDAP attribute does not exist in the entry). 247 * 248 * @throws LDAPException If a problem occurs while attempting to create the 249 * SCIM 2.0 representation of the attribute from the 250 * provided LDAP entry. 251 */ 252 public abstract JSONField createSCIM2Representation( 253 final Entry mappedLDAPEntry) 254 throws LDAPException; 255 256 257 258 /** 259 * Updates the provided LDAP entry with information from the given JSON object 260 * containing the SCIM 2.0 representation of the entry. 261 * 262 * @param scim2EntryID The identifier for the SCIM2 entry. 263 * @param scim2Entry A JSON object containing the SCIM2 264 * representation of the entry. 265 * @param updatableLDAPEntry An LDAP entry that may be updated with content 266 * obtained from the SCIM2 representation of the 267 * entry. 268 * 269 * @throws LDAPException If a problem occurs while attempting to create the 270 * LDAP representation of the attribute from the 271 * provided JSON object. 272 */ 273 public abstract void createLDAPRepresentation( 274 final String scim2EntryID, 275 final JSONObject scim2Entry, 276 final Entry updatableLDAPEntry) 277 throws LDAPException; 278}