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-2024 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}