//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
//
=== Customization

[CAUTION]
Only Maven projects can be customized: if using Standalone, none of the customizations discussed below can be applied.

Apache Syncope is designed to be as flexible as possible, to best suit the various environments
in which it can be deployed. Besides other aspects, this means that every feature and component can be extended or
replaced.

Once the project has been created from the provided Maven archetype, the generated source tree is available for either
adding new features or replacing existing components.

In general, the Embedded Mode (see the
ifeval::["{backend}" == "html5"]
https://syncope.apache.org/docs/getting-started.html[Apache Syncope Getting Started Guide]
endif::[]
ifeval::["{backend}" == "pdf"]
https://syncope.apache.org/docs/getting-started.pdf[Apache Syncope Getting Started Guide]
endif::[]
for details) allows developers to work comfortably from a single workstation, with no need of additional setup; it is
effectively implemented as the `all`
https://maven.apache.org/guides/introduction/introduction-to-profiles.html[Maven profile^], where the available optional
components and extensions are enabled. +
When deploying the generated artifacts as <<standalone>> or into an external <<javaee-container>> however, the required
components and extensions need to be explicitly selected and enabled, as shown in the following text.

The artifacts are generated by running the Maven command (with reference to the suggested
<<deployment-directories,directory layout>>):

....
$ mvn clean verify
$ cp core/target/classes/*properties /opt/syncope/conf
$ cp console/target/classes/*properties /opt/syncope/conf
$ cp enduser/target/classes/*properties /opt/syncope/conf
$ cp enduser/target/classes/*json /opt/syncope/conf
$ cp wa/target/classes/*properties /opt/syncope/conf
$ cp sra/target/classes/*properties /opt/syncope/conf
....

After downloading all of the dependencies that are needed, three following artifacts will be produced:

. `core/target/syncope.war`
. `console/target/syncope-console.war`
. `enduser/target/syncope-enduser.war`
. `wa/target/syncope-wa.war`
. `sra/target/syncope-sra.jar`

If no failures are encountered, your basic Apache Syncope project is now ready to be deployed.

Do not forget to define the following system properties:

* `-Dsyncope.conf.dir=/opt/syncope/conf` +
(required by Core and WA)
* `-Dsyncope.connid.location=file:/opt/syncope/bundles` +
(required by Core)
* `-Dsyncope.log.dir=/opt/syncope/log` +
(required by all components)

[[embedded-debug]]
[TIP]
.JPDA Debug in Embedded Mode
====
The Java™ Platform Debugger Architecture (https://docs.oracle.com/en/java/javase/11/docs/specs/jpda/jpda.html[JPDA^])
is a collection of APIs aimed to help with debugging Java code.

Enhancing the `embedded` profile of the `fit` module to enable the JPDA socket is quite
straightforward: just add the `<profile>` below to `fit/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<profile>
  <id>debug</id>

  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.cargo</groupId>
        <artifactId>cargo-maven3-plugin</artifactId>
        <inherited>true</inherited>
        <configuration>
          <configuration>
            <properties>
              <cargo.jvmargs>
                -Xdebug
                -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n
                -Dwicket.ioc.useByteBuddy=true
                -Dspring.profiles.active=embedded
                -Xmx1024m -Xms512m
              </cargo.jvmargs>
            </properties>
          </configuration>
        </configuration>
      </plugin>
    </plugins>
  </build>
</profile>
----

Now, from the `fit` subdirectory, execute:

[source,bash]
$ mvn -P embedded,debug

At this point your favourite IDE can be attached to the port `8000`.
====

[[customization-general]]
==== General considerations

===== Override behavior
As a rule of thumb, any file of the local project will take precedence over a file with the same name in the same
package directory of the standard Apache Syncope release.

For example, if you place

 core/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java

in the local project, this file will be picked up instead of
ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/apache/syncope/blob/syncope-{docVersion}/core/spring/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java[UsernamePasswordAuthenticationProvider^].
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/3_0_X/core/spring/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java[UsernamePasswordAuthenticationProvider^].
endif::[]

The same happens with resources as images or HTML files; if you place

 console/src/main/resources/org/apache/syncope/client/console/pages/BasePage.html

in the local project, this file will be picked up instead of
ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/apache/syncope/blob/syncope-{docVersion}/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/BasePage.html[BasePage.html^].
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/3_0_X/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/BasePage.html[BasePage.html^].
endif::[]

===== Extending configuration
Apache Syncope <<architecture,components>> are built on https://spring.io/projects/spring-boot[Spring Boot^],
hence designing and extending Syncope configuration very much comes down to
https://docs.spring.io/spring-boot/docs/2.7.x/reference/html/[their guide^], some aspects of which are briefly
highlighted here.

To design your own configuration class, take inspiration from the following sample:

[source,java]
----
package org.apache.syncope.custom.config;

@Configuration("SomethingConfiguration") // <1>
@EnableConfigurationProperties(LogicProperties.class)
public class SomethingConfiguration {

    @Autowired
    private LogicProperties logicProperties;

    @Autowired
    @Qualifier("someOtherBeanId")
    private SomeBean someOtherBeanId;

    @RefreshScope // <2>
    @Bean
    public MyBean myBean() {
        return new MyBean();
    }
} 
----
<1> `@Configuration` classes can be assigned an order with `@Order(1984)` which would place them in an ordered queue
waiting to be loaded in that sequence; to be more explicit, `@Configuration` classes can also be loaded exactly
before/after another `@Configuration` component with `@AutoConfigureBefore` or `@AutoConfigureAfter` annotations.
<2> The `@Bean` definitions can also be tagged with `@RefreshScope` to become auto-reloadable when the enclosing
Syncope componet context is refreshed as a result of an external property change.

In order to register your own configuration class, create a file named

----
<component>/src/main/resources/META-INF/spring.factories
----

with content

----
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.apache.syncope.custom.config.SomethingConfiguration
----

What if you needed to override the definition of a Syncope-provided bean and replace it entirely with your own? +
Most component/bean definitions are registered with some form of `@Conditional` tag that indicates to the bootstrapping
process to ignore their creation, if a bean definition with the same id is already defined. This means you can create
your own configuration class, register it and the design a `@Bean` definition only to have the context utilize yours
rather than what ships with Syncope by default.

[TIP]
.Bean Names
To correctly define a conditional Bean, you generally need to make sure your own bean definition is created using the
same name or identifier as its original equivalent. It is impractical and certainly overwheling to document all runtime
bean definitions and their identifiers. So, you will need to study the Syncope codebase to find the correct
onfiguration classes and bean defnitions to note their name.

===== Deployment directories
Apache Syncope needs three base directories to be defined:

* bundles - where the <<connector-bundles,connector bundles>> are stored;
* log - where all the system logs are written;
* conf - where configuration files are located.

[WARNING]
The `bundles` directory should only contain connector bundle JAR files. +
The presence of any other file might cause the unavailability of any connector bundle in Apache Syncope.

For reference, the suggested directory layout can be created as follows:

....
$ mkdir /opt/syncope
$ mkdir /opt/syncope/bundles
$ mkdir /opt/syncope/log
$ mkdir /opt/syncope/conf
....

[TIP]
====
The `conf` directory must be configured for deployment, following Spring Boot's
https://docs.spring.io/spring-boot/docs/2.7.x/reference/htmlsingle/#features.external-config.files[Externalized Configuration^]
settings; with above reference:

* <<standalone>>: `--spring.config.additional-location=/opt/syncope/conf/`
* <<javaee-container>>: `-Dspring.config.additional-location=/opt/syncope/conf/`
====

[[customization-core]]
==== Core

[CAUTION]
When providing custom Java classes implementing the defined interfaces or extending the existing
implementations, their package *must* be rooted under `org.apache.syncope.core`, otherwise they will not be available
at runtime.

Besides replacing existing classes as explained <<override-behavior,above>>, new <<implementations,implementations>> can
be provided - in the source tree under `core/src/main/java` when Java or via REST services if Groovy - for the following
components:

* <<propagationactions,propagation>>, <<pushactions,push>>, <<pullactions,pull>> and <<logicactions,logic>> actions
* <<push-correlation-rules,push>> / <<pull-correlation-rules,pull>> correlation rules
* <<pull-mode,reconciliation filter builders>>
* <<commands,commands>>
* <<tasks-macro,macros>>
* <<tasks-scheduled,scheduled tasks>>
* <<reports,reports>>
* <<account-rules,account>> and <<password-rules,password>> rules for policies
* <<plain,plain schema validators>>
* <<mapping,mapping item transformers>>
* <<virtual-attribute-cache,virtual attribute cache>>
* <<workflow-adapters,workflow adapters>>
* <<provisioning-managers,provisioning managers>>
* <<notifications,notification recipient providers>>
* <<jwtssoprovider,JWT SSO providers>>
* <<audit-appenders, audit appenders>>

[discrete]
===== Customize OpenJPA settings

Apache OpenJPA is at the core of the <<persistence,persistence>> layer; its configuration can be tweaked under several
aspects - including https://openjpa.apache.org/builds/3.2.2/apache-openjpa/docs/ref_guide_caching.html[caching^] for
example, to best suit the various environments.

The main configuration classes are:

ifeval::["{snapshotOrRelease}" == "release"]
* https://github.com/apache/syncope/blob/syncope-{docVersion}/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java[PersistenceContext^]
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
* https://github.com/apache/syncope/blob/3_0_X/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java[PersistenceContext^]
endif::[]
ifeval::["{snapshotOrRelease}" == "release"]
* https://github.com/apache/syncope/blob/syncope-{docVersion}/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MasterDomain.java[MasterDomain^]
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
* https://github.com/apache/syncope/blob/3_0_X/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MasterDomain.java[MasterDomain^]
endif::[]
ifeval::["{snapshotOrRelease}" == "release"]
* https://github.com/apache/syncope/blob/syncope-{docVersion}/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/DomainConfFactory.java[DomainConfFactory^]
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
* https://github.com/apache/syncope/blob/3_0_X/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/DomainConfFactory.java[DomainConfFactory^]
endif::[]

The `@Bean` declarations from these classes can be customized as explained <<extending-configuration,above>>.

[discrete]
===== Enable the <<flowable-user-workflow-adapter>>

Add the following dependency to `core/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.flowable</groupId>
  <artifactId>syncope-ext-flowable-rest-cxf</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

[discrete]
===== Enable the <<saml2sp4ui>> extension

Add the following dependencies to `core/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.saml2sp4ui</groupId>
  <artifactId>syncope-ext-saml2sp4ui-rest-cxf</artifactId>
  <version>${syncope.version}</version>
</dependency>
<dependency>
  <groupId>org.apache.syncope.ext.saml2sp4ui</groupId>
  <artifactId>syncope-ext-saml2sp4ui-persistence-jpa</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

Setup a <<keystore,keystore>> and place it under the <<properties-files-location,configuration directory>>, then take
the properties from `core/src/test/resources/core-all.properties` into your configuration and review accordingly.

[discrete]
===== Enable the <<oidcc4ui>> extension

Add the following dependencies to `core/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.oidcc4ui</groupId>
  <artifactId>syncope-ext-oidcc4ui-rest-cxf</artifactId>
  <version>${syncope.version}</version>
</dependency>
<dependency>
  <groupId>org.apache.syncope.ext.oidcc4ui</groupId>
  <artifactId>syncope-ext-oidcc4ui-persistence-jpa</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

[discrete]
===== Enable the <<elasticsearch>> extension

Add the following dependencies to `core/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.elasticsearch</groupId>
  <artifactId>syncope-ext-elasticsearch-logic</artifactId>
  <version>${syncope.version}</version>
</dependency>
<dependency>
  <groupId>org.apache.syncope.ext.elasticsearch</groupId>
  <artifactId>syncope-ext-elasticsearch-provisioning-java</artifactId>
  <version>${syncope.version}</version>
</dependency>
<dependency>
  <groupId>org.apache.syncope.ext.elasticsearch</groupId>
  <artifactId>syncope-ext-elasticsearch-persistence-jpa</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

Create

[source]
....
elasticsearch.hosts[0]=http://localhost:9200
elasticsearch.indexMaxResultWindow=10000
elasticsearch.numberOfShards=1
elasticsearch.numberOfReplicas=1
....

as `core/src/main/resources/core-elasticsearch.properties`.

Do not forget to include `elasticsearch` as 
https://docs.spring.io/spring-boot/docs/2.7.x/reference/html/features.html#features.profiles.adding-active-profiles[Spring Boot profile^]
for the Core application.

If needed, customize the `@Bean` declarations from
ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/apache/syncope/blob/syncope-{docVersion}/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientContext.java[ElasticsearchClientContext^]
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/3_0_X/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientContext.java[ElasticsearchClientContext^]
endif::[]
as explained <<extending-configuration,above>>.

It is also required to initialize the Elasticsearch indexes: add a new Java <<implementations,implementation>> for
`TASKJOB_DELEGATE` and use `org.apache.syncope.core.provisioning.java.job.ElasticsearchReindex` as class. +
Then, create a new <<tasks-scheduled, scheduled task>>, select the implementation just created as job delegate and execute it.

[TIP]
The `org.apache.syncope.core.provisioning.java.job.ElasticsearchReindex` task created above is not meant for
scheduled execution; rather, it can be run every time you want to blank and re-create the Elasticsearch indexes
starting from Syncope's internal storage.

[discrete]
===== Enable the <<opensearch>> extension

Add the following dependencies to `core/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.opensearch</groupId>
  <artifactId>syncope-ext-opensearch-logic</artifactId>
  <version>${syncope.version}</version>
</dependency>
<dependency>
  <groupId>org.apache.syncope.ext.opensearch</groupId>
  <artifactId>syncope-ext-opensearch-provisioning-java</artifactId>
  <version>${syncope.version}</version>
</dependency>
<dependency>
  <groupId>org.apache.syncope.ext.opensearch</groupId>
  <artifactId>syncope-ext-opensearch-persistence-jpa</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

Create

[source]
....
opensearch.hosts[0]=http://localhost:9200
opensearch.indexMaxResultWindow=10000
opensearch.numberOfShards=1
opensearch.numberOfReplicas=1
....

as `core/src/main/resources/core-opensearch.properties`.

Do not forget to include `opensearch` as 
https://docs.spring.io/spring-boot/docs/2.7.x/reference/html/features.html#features.profiles.adding-active-profiles[Spring Boot profile^]
for the Core application.

If needed, customize the `@Bean` declarations from
ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/apache/syncope/blob/syncope-{docVersion}/ext/opensearch/client-opensearch/src/main/java/org/apache/syncope/ext/opensearch/client/OpenSearchClientContext.java[OpenSearchClientContext^]
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/3_0_X/ext/opensearch/client-opensearch/src/main/java/org/apache/syncope/ext/opensearch/client/OpenSearchClientContext.java[OpenSearchClientContext^]
endif::[]
as explained <<extending-configuration,above>>.

It is also required to initialize the OpenSearch indexes: add a new Java <<implementations,implementation>> for
`TASKJOB_DELEGATE` and use `org.apache.syncope.core.provisioning.java.job.OpenSearchReindex` as class. +
Then, create a new <<tasks-scheduled, scheduled task>>, select the implementation just created as job delegate and execute it.

[TIP]
The `org.apache.syncope.core.provisioning.java.job.OpenSearchReindex` task created above is not meant for
scheduled execution; rather, it can be run every time you want to blank and re-create the OpenSearch indexes
starting from Syncope's internal storage.

[discrete]
===== Enable the <<SCIM>> extension

Add the following dependencies to `core/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.scimv2</groupId>
  <artifactId>syncope-ext-scimv2-rest-cxf</artifactId>
  <version>${syncope.version}</version>
</dependency>
<dependency>
  <groupId>org.apache.syncope.ext.scimv2</groupId>
  <artifactId>syncope-ext-scimv2-scim-rest-cxf</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

[discrete]
===== New REST endpoints
Adding a new REST endpoint involves several operations:

. create - in an extension's `rest-api` module or under `common` otherwise - a Java interface with package
`org.apache.syncope.common.rest.api.service` and proper JAX-RS annotations; check
ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/apache/syncope/blob/syncope-{docVersion}/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/BpmnProcessService.java[BpmnProcessService^]
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/3_0_X/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/BpmnProcessService.java[BpmnProcessService^]
endif::[]
for reference;
. if needed, define supporting payload objects - in an extension's `common-lib` module or under `common` otherwise;
check
ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/apache/syncope/blob/syncope-{docVersion}/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/BpmnProcess.java[BpmnProcess^]
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/3_0_X/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/BpmnProcess.java[BpmnProcess^]
endif::[]
for reference;
. implement - in an extension's `rest-cxf` module or under `core` otherwise -  the interface defined above in a Java
class with package `org.apache.syncope.core.rest.cxf.service`; check
ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/apache/syncope/blob/syncope-{docVersion}/ext/flowable/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/BpmnProcessServiceImpl.java[BpmnProcessServiceImpl^]
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/3_0_X/ext/flowable/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/BpmnProcessServiceImpl.java[BpmnProcessServiceImpl^]
endif::[]
for reference.

By following such conventions, the new REST endpoint will be automatically picked up alongside the default services.

[[customization-console]]
==== Console

[CAUTION]
When providing custom Java classes implementing the defined interfaces or extending the existing
implementations, their package *must* be rooted under `org.apache.syncope.client.console`, otherwise they will not be
available at runtime.

[discrete]
===== Enable the <<flowable-user-workflow-adapter>>

Add the following dependency to `console/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.flowable</groupId>
  <artifactId>syncope-ext-flowable-client-console</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

[discrete]
===== Enable the <<saml2sp4ui>> extension

Add the following dependencies to `console/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.saml2sp4ui</groupId>
  <artifactId>syncope-ext-saml2sp4ui-client-console</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

[discrete]
===== Enable the <<oidcc4ui>> extension

Add the following dependencies to `console/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.oidcc4ui</groupId>
  <artifactId>syncope-ext-oidcc4ui-client-console</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

[discrete]
===== Enable the <<SCIM>> extension

Add the following dependencies to `console/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.scimv2</groupId>
  <artifactId>syncope-ext-scimv2-client-console</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

[[customization-enduser]]
==== Enduser

[CAUTION]
When providing custom Java classes implementing the defined interfaces or extending the existing
implementations, their package *must* be rooted under `org.apache.syncope.client.enduser`, otherwise they will not be
available at runtime.

[discrete]
===== Enable the <<flowable-user-workflow-adapter>>

Add the following dependency to `enduser/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.flowable</groupId>
  <artifactId>syncope-ext-flowable-client-enduser</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

[discrete]
===== Enable the <<saml2sp4ui>> extension

Add the following dependencies to `enduser/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.saml2sp4ui</groupId>
  <artifactId>syncope-ext-saml2sp4ui-client-enduser</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

[discrete]
===== Enable the <<oidcc4ui>> extension

Add the following dependencies to `enduser/pom.xml`:

[source,xml,subs="verbatim,attributes"]
----
<dependency>
  <groupId>org.apache.syncope.ext.oidcc4ui</groupId>
  <artifactId>syncope-ext-oidcc4ui-client-enduser</artifactId>
  <version>${syncope.version}</version>
</dependency>
----

[[customization-enduser-form]]
===== Form customization

The <<enduser-application>> allows to customize the form in order to:

* hide / show attributes
* set attributes read-only for users
* provide default value(s)

Under the `enduser/src/main/resources` directory, the `customFormLayout.json` file is available, allowing to configure
form customization.

[[customization-wa]]
==== WA

[CAUTION]
When providing custom Java classes implementing the defined interfaces or extending the existing
implementations, their package *must* be rooted under `org.apache.syncope.wa`, otherwise they will not be
available at runtime.

[[customization-sra]]
==== SRA

[CAUTION]
When providing custom Java classes implementing the defined interfaces or extending the existing
implementations, their package *must* be rooted under `org.apache.syncope.sra`, otherwise they will not be
available at runtime.

[[customization-extensions]]
==== Extensions

<<extensions>> can be part of a local project, to encapsulate special features which are specific to a given deployment.

For example, the http://www.chorevolution.eu/[CHOReVOLUTION^] IdM - based on Apache Syncope - provides
https://gitlab.ow2.org/chorevolution/syncope/tree/3_0_X/ext/choreography[an extension^]
for managing via the <<core>> and visualizing via the <<admin-console-component>> the running choreography instances.
