diff --git a/.github/workflows/crosschecks.yml b/.github/workflows/crosschecks.yml
new file mode 100644
index 00000000000..b211476044c
--- /dev/null
+++ b/.github/workflows/crosschecks.yml
@@ -0,0 +1,60 @@
+# 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
+name: "Cross Checks"
+
+on:
+ push:
+ branches: [2_1_X]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [2_1_X]
+ schedule:
+ - cron: '0 13 * * 4'
+
+jobs:
+ crosschecks:
+ runs-on: ${{ matrix.os }}
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: ['java']
+ java: [ '8' ]
+ os: [ubuntu-latest, windows-latest, macos-latest]
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+ - name: Setup Java JDK
+ uses: actions/setup-java@v1.4.3
+ with:
+ java-version: ${{ matrix.java }}
+ - name: Setup Maven
+ uses: stCarolas/setup-maven@v4
+ with:
+ maven-version: 3.6.3
+ - uses: actions/cache@v2.1.1
+ with:
+ path: ~/.m2
+ key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
+ restore-keys: ${{ runner.os }}-m2
+ - name: Build
+ run: mvn -U -T 1C -P 'skipTests,all,without-eclipse'
+ - name: Validate
+ run: mvn -T 1C -P 'all,without-eclipse' checkstyle:check tools:verify-legal-files modernizer:modernizer apache-rat:check
+ - name: Test
+ run: mvn -T 1C -P 'all,without-eclipse' clean test '-Dinvoker.streamLogs=true' '-Dmodernizer.skip=true' '-Dianal.phase=none' '-Drat.skip=true' '-Dcheckstyle.skip=true' '-Dsass.skip=true'
diff --git a/.github/workflows/fit_Elasticsearch.yml b/.github/workflows/fit_Elasticsearch.yml
new file mode 100644
index 00000000000..6015b0d4247
--- /dev/null
+++ b/.github/workflows/fit_Elasticsearch.yml
@@ -0,0 +1,47 @@
+# 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
+name: "FIT Elasticsearch"
+
+on:
+ push:
+ branches: [2_1_X]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [2_1_X]
+ schedule:
+ - cron: '0 13 * * 4'
+
+jobs:
+ fit_Elasticsearch:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+ - name: Setup Java JDK
+ uses: actions/setup-java@v1.4.3
+ with:
+ java-version: 8
+ - uses: actions/cache@v2.1.1
+ with:
+ path: ~/.m2
+ key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
+ restore-keys: ${{ runner.os }}-m2
+ - name: Build
+ run: mvn -U -T 1C -P 'skipTests,all,without-eclipse'
+ - name: 'Elasticsearch / H2 / JSON'
+ run: mvn -f fit/core-reference/pom.xml -P elasticsearch-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
diff --git a/.github/workflows/fit_Tomcat_H2_JSON.yml b/.github/workflows/fit_Tomcat_H2_JSON.yml
new file mode 100644
index 00000000000..08a5e05663e
--- /dev/null
+++ b/.github/workflows/fit_Tomcat_H2_JSON.yml
@@ -0,0 +1,51 @@
+# 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
+name: "FIT Tomcat H2 JSON"
+
+on:
+ push:
+ branches: [2_1_X]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [2_1_X]
+ schedule:
+ - cron: '0 13 * * 4'
+
+jobs:
+ fit_Tomcat_H2_JSON:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+ - name: Setup Java JDK
+ uses: actions/setup-java@v1.4.3
+ with:
+ java-version: 8
+ - name: Setup Maven
+ uses: stCarolas/setup-maven@v4
+ with:
+ maven-version: 3.6.3
+ - uses: actions/cache@v2.1.1
+ with:
+ path: ~/.m2
+ key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
+ restore-keys: ${{ runner.os }}-m2
+ - name: Build
+ run: mvn -U -T 1C -P 'skipTests,all,without-eclipse'
+ - name: 'Tomcat / H2 / JSON'
+ run: mvn -f fit/core-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
diff --git a/.github/workflows/jpajson.yml b/.github/workflows/jpajson.yml
new file mode 100644
index 00000000000..48da9c466dd
--- /dev/null
+++ b/.github/workflows/jpajson.yml
@@ -0,0 +1,49 @@
+# 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
+name: "JPA JSON"
+
+on:
+ push:
+ branches: [2_1_X]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [2_1_X]
+ schedule:
+ - cron: '0 13 * * 4'
+
+jobs:
+ JPAJSON:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+ - name: Setup Java JDK
+ uses: actions/setup-java@v1.4.3
+ with:
+ java-version: 8
+ - uses: actions/cache@v2.1.1
+ with:
+ path: ~/.m2
+ key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
+ restore-keys: ${{ runner.os }}-m2
+ - name: Build
+ run: mvn -U -T 1C -P 'skipTests,all,without-eclipse'
+ - name: 'Unit Tests: PostgreSQL JPA JSON'
+ run: mvn -f core/persistence-jpa-json/pom.xml -P pgjsonb -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
+ - name: 'Unit Tests: MySQL JPA JSON'
+ run: mvn -f core/persistence-jpa-json/pom.xml -P myjson -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true
diff --git a/.gitignore b/.gitignore
index e21571ffb16..d479649a1e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,4 +13,5 @@ ide/eclipse/bundles/org.apache.syncope.ide.eclipse.plugin/bin/
ide/eclipse/bundles/org.apache.syncope.ide.eclipse.plugin/lib/
ide/eclipse/bundles/org.apache.syncope.ide.eclipse.tests/bin/
ide/eclipse/bundles/org.apache.syncope.ide.eclipse.tests/screenshots/
-fit/core-reference/nb-configuration.xml
+*nb-configuration.xml
+node_modules
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 6e008aa5eb2..00000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-# 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.
-
-sudo: false
-addons:
- apt:
- packages:
- - oracle-java8-installer
-language: java
-jdk:
- - oraclejdk8
-before_install:
- - echo 'MAVEN_OPTS="-Xms512m -Xmx1024m -XX:+TieredCompilation -XX:TieredStopAtLevel=1"' >~/.mavenrc
-# default install is mvn install --quiet -DskipTests=true
-install: travis_wait mvn --show-version --quiet -T 1C -P all,skipTests
-before_script:
- - cd fit/core-reference
-script:
- # invoker.streamLogs: we cannot access to log files through Travis web ui, so display everything in the console
- - mvn --show-version verify -Dinvoker.streamLogs=true
-after_failure:
- - cat target/log/*
- - cat target/failsafe-reports/org.apache.syncope.fit.*-output.txt
-notifications:
- email:
- - dev@syncope.apache.org
diff --git a/CHANGES b/CHANGES
index 7babeeaef0e..ac54ebf8b26 100644
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,387 @@ Apache Syncope - CHANGES
Licensed under Apache License 2.0 - http://www.apache.org/licenses/LICENSE-2.0
--------------------------------------------------------------------------------
+Release Notes - Syncope - Version 2.1.14
+================================================================================
+
+** Bug
+ * [SYNCOPE-1731] - Performance issue with multiple any type classes
+ * [SYNCOPE-1734] - Elasticsearch not updated for uidOnCreate
+ * [SYNCOPE-1749] - Incorrect Dynamic Group Membership Condition save from Console
+ * [SYNCOPE-1750] - Password policy not enforced if password is not stored in Syncope
+ * [SYNCOPE-1755] - NullPointer exception during PULL delete operation in case of NO_MATCH
+ * [SYNCOPE-1756] - Add an id_token_hint parameter to the logout provider url for OIDC
+
+** Improvement
+ * [SYNCOPE-1720] - Switch persistence identifiers to UUID version 7
+
+Release Notes - Syncope - Version 2.1.13
+================================================================================
+
+** Bug
+ * [SYNCOPE-1693] - Must change password submit on console leads to errors
+ * [SYNCOPE-1704] - Policy update not affecting External Resources
+ * [SYNCOPE-1706] - Notification task not created with event category PROPAGATION
+
+** Improvement
+ * [SYNCOPE-1694] - Optimize creation of Implementation instances
+ * [SYNCOPE-1696] - Audit Elasticsearch persistence
+ * [SYNCOPE-1709] - Persist Jobs' current status in the database to support multi-node deployments
+ * [SYNCOPE-1713] - Support time-based conditions for Audit, Exec and Remediation queries
+
+Release Notes - Syncope - Version 2.1.12
+================================================================================
+
+** Bug
+ * [SYNCOPE-1671] - Wrong JobDelegate column name in scheduled task table
+ * [SYNCOPE-1683] - Show connector overridden properties in resource wizard in tabular topology during create
+ * [SYNCOPE-1691] - Schema labels not used for attribute column headers
+
+** Improvement
+ * [SYNCOPE-1669] - Create pull results for remediations
+ * [SYNCOPE-1670] - Support Graceful shutdown
+ * [SYNCOPE-1674] - Optimize User, Group and Any Object lifecycle events management
+
+Release Notes - Syncope - Version 2.1.11
+================================================================================
+
+** Bug
+ * [SYNCOPE-1646] - Linked Account status set to wrong value on propagation
+ * [SYNCOPE-1648] - Search with PostgreSQL JSONB fails for FIQL like 'username!=value'
+ * [SYNCOPE-1649] - Reports: XML character escaping applied to CSV output
+ * [SYNCOPE-1650] - Default Account Rule: pattern is ignored
+ * [SYNCOPE-1651] - Invalid users can be specified in X-Syncope-Delegated-By
+ * [SYNCOPE-1654] - Inconsistent Realm search FIQL expressions between JPA and Elasticsearch engines
+ * [SYNCOPE-1656] - Remediations are not created on update while pulling
+ * [SYNCOPE-1657] - Unable to define a new name for a cloned resource
+ * [SYNCOPE-1659] - Read-only flag not working in console on virtual attributes
+ * [SYNCOPE-1660] - Anonymous requests does not store domain and delegatedBy information in the auth context
+ * [SYNCOPE-1663] - Value errors in FIQL expressions lead to empty result rather than error messages
+ * [SYNCOPE-1664] - JSONB: Inconsistent search query when is used a pull correlation rule
+
+** Improvement
+ * [SYNCOPE-1658] - Allow to view the topology in table format
+ * [SYNCOPE-1666] - Security Answer encryption
+ * [SYNCOPE-1667] - Propagation Policy
+ * [SYNCOPE-1668] - Provide Entity Cache report and management
+
+Release Notes - Syncope - Version 2.1.10
+================================================================================
+
+** Bug
+ * [SYNCOPE-1628] - Console goes NPE when Connector fails to initialize
+ * [SYNCOPE-1629] - JPA JSON: Date conversion pattern including slashes leads to incorrect search results
+ * [SYNCOPE-1634] - Group Owner update/delete action doesn't trigger propagation action
+ * [SYNCOPE-1635] - Create Rules with configurations for each domain, make creation thread safe
+ * [SYNCOPE-1640] - Uncaught exception when creating Enum schema
+ * [SYNCOPE-1643] - Update of Realm doesn't trigger provisioning for users
+ * [SYNCOPE-1644] - Task run failure with multi-node deployments
+ * [SYNCOPE-1645] - Case insensitive search with Elasticsearch extension returns wrong results
+
+** New Feature
+ * [SYNCOPE-129] - Delegation
+
+** Improvement
+ * [SYNCOPE-1630] - Use Group owners to extend Delegated Administration
+ * [SYNCOPE-1631] - Pass ConnId ObjectClass to ReconFilterBuilder
+ * [SYNCOPE-1633] - Give the possibility to add a custom message to the confirm dialog
+ * [SYNCOPE-1639] - Provide ordering of attributes in the diff view on the history management
+ * [SYNCOPE-1641] - Allow to purge Propagation Tasks
+
+Release Notes - Syncope - Version 2.1.9
+================================================================================
+
+** Bug
+ * [SYNCOPE-1606] - Syncope returns an exception when doing two sequential operations for the same user from the toggle panel
+ * [SYNCOPE-1607] - Console Page preferences not working
+ * [SYNCOPE-1613] - startAt date is set to start field for SCHEDULED, PULL and PUSH TaskTOs
+ * [SYNCOPE-1616] - CSV and single push / pull concurrency issues
+ * [SYNCOPE-1619] - SearchPanel should display the input field based on the type of the selected property
+ * [SYNCOPE-1620] - JWT validation requires exp and nbf claims
+ * [SYNCOPE-1622] - ConnId Connectors not pooled with Resource override
+
+** Improvement
+ * [SYNCOPE-1608] - Allow wildcard group membership search
+ * [SYNCOPE-1609] - Reduce the number of table joins into PostgreSQL JSONB persistence implementation
+ * [SYNCOPE-1610] - Set Reconciliation to work with Pull and Push Correlation Rules if available
+ * [SYNCOPE-1611] - Caffeine Cache for Virtual Attribute Cache
+ * [SYNCOPE-1624] - Toggle panel improvements
+
+Release Notes - Syncope - Version 2.1.8
+================================================================================
+
+** Bug
+ * [SYNCOPE-1590] - Error when adding i18n labels to schemas
+ * [SYNCOPE-1596] - Console: read-only attributes not rendered as disabled
+ * [SYNCOPE-1598] - Create or update user with two+ memberships for the same group are not prevented
+ * [SYNCOPE-1601] - Propagation not always triggered after form submit in User Requests
+ * [SYNCOPE-1602] - ConnObjectKey attribute values not included with DefaultPushCorrelationRule
+ * [SYNCOPE-1603] - PushCorrelationRule not used for DELETE on External Resources
+ * [SYNCOPE-1604] - AjaxDateTimePicker doesn't handle some 1900 dates the right way
+ * [SYNCOPE-1605] - Propagation task not generated if update involves only ConnObjectLink
+
+** Improvement
+ * [SYNCOPE-1591] - Support fetching data from internal storage for XML content loader
+ * [SYNCOPE-1594] - Allow to filter user requests and forms by username
+ * [SYNCOPE-1597] - Enable default customization of console layout
+ * [SYNCOPE-1600] - Flowable: support password form property type
+
+Release Notes - Syncope - Version 2.1.7
+================================================================================
+
+** Bug
+ * [SYNCOPE-1549] - Groups select opens a popup when removing a group
+ * [SYNCOPE-1560] - File upload component: missing translations
+ * [SYNCOPE-1561] - Batch: missing support for custom TLSClientParameters
+ * [SYNCOPE-1563] - User approval update should send the password only when requested
+ * [SYNCOPE-1564] - Integration tests run with YAML payloads are failing
+ * [SYNCOPE-1565] - Integration tests run with XML payloads are failing
+ * [SYNCOPE-1567] - Mapping does not allow relationships
+ * [SYNCOPE-1569] - Console: cannot save Reportlet search conditions
+ * [SYNCOPE-1573] - Logout forced from Console when editing user with many memberships
+ * [SYNCOPE-1581] - When enabling Swagger UI or Flowable extensions Installer generates invalid POM files
+ * [SYNCOPE-1583] - For members part of a Dynamic Group, but cannot access group attributes in member mapping
+ * [SYNCOPE-1586] - Startup failures with sample docker-compose and MySQL
+
+** Improvement
+ * [SYNCOPE-1568] - Render custom wizard on user request
+ * [SYNCOPE-1575] - Provide the ability to specify on which resources the user's status should be propagated
+
+Release Notes - Syncope - Version 2.1.6
+================================================================================
+
+** Bug
+ * [SYNCOPE-1461] - MySQL: Segmentation fault with query using JSON_TABLE
+ * [SYNCOPE-1503] - Cannot remove provisioning information from Resource in Admin Console
+ * [SYNCOPE-1504] - Error setting uidOnCreate attribute during a pull task with multiple provisions
+ * [SYNCOPE-1505] - Changes to "AjaxPalettePanel" components in Console are not saved when the previous step button is pressed before submitting the wizard form
+ * [SYNCOPE-1512] - Error while saving a unique plain attribute value with single quote when using JPA JSON
+ * [SYNCOPE-1520] - Exception when updating Group unique attribute with JPA JSON
+ * [SYNCOPE-1524] - Social registration does not redirect to self registration page
+ * [SYNCOPE-1525] - Documentation indicates sharing private key, hiding public key
+ * [SYNCOPE-1526] - Broken link to issues from reference documentation
+ * [SYNCOPE-1533] - Broken backward compatibilty because of changes in Equals and HashCode methods in TOs
+ * [SYNCOPE-1536] - Enduser UI does not clean up file alteration monitors on shutdown
+ * [SYNCOPE-1537] - Password of LinkedAccounts not saved properly from Admin Console
+ * [SYNCOPE-1538] - Admin Console: users / groups management slow to access with high number of realms
+ * [SYNCOPE-1539] - AjaxPalettePanel does not support setRequired
+ * [SYNCOPE-1542] - Search panel issues in Admin Console
+ * [SYNCOPE-1544] - 'Override?' flag not properly set for password and username fields of LinkedAccounts
+ * [SYNCOPE-1546] - PriorityPropagationTaskExecutor: rejected tasks not stored
+ * [SYNCOPE-1554] - Generated default admin role layout doesn't work
+
+** New Feature
+ * [SYNCOPE-957] - Multiaccount
+ * [SYNCOPE-1506] - Merge Users
+ * [SYNCOPE-1511] - Configure audit events create/update/etc of users, groups, etc
+ * [SYNCOPE-1529] - French (CA) translation for Admin Console
+
+** Improvement
+ * [SYNCOPE-1498] - Allow variable resolution in Content.xml
+ * [SYNCOPE-1499] - Add support for READ correlation rule
+ * [SYNCOPE-1500] - Allow single import from External Resource
+ * [SYNCOPE-1501] - Allow filtering for explore resource
+ * [SYNCOPE-1502] - Find Anys using FIQL: SQL improvements
+ * [SYNCOPE-1508] - Allow to extend the set of attributes requested from External Resources
+ * [SYNCOPE-1509] - Auto-select language from Accept-Language HTTP header
+ * [SYNCOPE-1510] - Allow to store encrypted schema's secret key externally
+ * [SYNCOPE-1513] - Allow to customize security headers
+ * [SYNCOPE-1515] - Adapt realm selector to actual number of realms
+ * [SYNCOPE-1517] - Audit appender should be configurable
+ * [SYNCOPE-1518] - Allow X-Forwarded-For and X-Forwarded-Proto HTTP headers integration
+ * [SYNCOPE-1521] - Allow filtering for Role assignment
+ * [SYNCOPE-1522] - Realm behaviors for Delegated Administration
+ * [SYNCOPE-1523] - JPAConnInstanceDAO should be marked as Transactional
+ * [SYNCOPE-1527] - Allow for custom search conditions
+ * [SYNCOPE-1530] - Add parameters at User Requests start
+ * [SYNCOPE-1531] - Easier bulk upload from / download to CSV
+ * [SYNCOPE-1532] - Allow tilde for key values and Realms name
+ * [SYNCOPE-1534] - Display friendly error messages in Admin Console
+ * [SYNCOPE-1535] - Customize the order of the provisions of a resource according to the object classes
+ * [SYNCOPE-1540] - Make internal storage export DBMS independent
+ * [SYNCOPE-1541] - XML response message timestamps missing millisecs component if "0 msecs"
+ * [SYNCOPE-1547] - Allow the possibility to customize the roles to be displayed
+ * [SYNCOPE-1548] - Allow the possibility to customize the Groups wizard step
+ * [SYNCOPE-1551] - Allow for info notifications in Admin Console
+
+** Task
+ * [SYNCOPE-1514] - Report CLI deprecation
+
+Release Notes - Syncope - Version 2.1.5
+================================================================================
+
+** Bug
+ * [SYNCOPE-1462] - Requests list is not refreshed if empty
+ * [SYNCOPE-1467] - RDN not allowed when an attribute of the group present also in the DN is changed
+ * [SYNCOPE-1469] - MySQL with JSON support: errors during startup
+ * [SYNCOPE-1470] - Flowable extension not working with MariaDB
+ * [SYNCOPE-1472] - Resource association is duplicated on database after update, assign or link operations
+ * [SYNCOPE-1474] - Resource is duplicated after batch operation
+ * [SYNCOPE-1476] - Error while creating Enum schema from Admin Console
+ * [SYNCOPE-1477] - jQuery UI's spinner not rendered
+ * [SYNCOPE-1478] - Unable to remove value of "Schema to hold values for identifiers generated upon Create by the external Identity Store" from provisioning
+ * [SYNCOPE-1480] - Elasticsearch:dynrealm assignment not updated on condition change
+ * [SYNCOPE-1481] - Invalid values when saving a membership attribute of type date
+ * [SYNCOPE-1484] - syncope-ide-netbeans submodule fails to find netbeans dependency
+ * [SYNCOPE-1485] - Reindex of elasticsearch ends with memory error in case of huge amount of data
+ * [SYNCOPE-1487] - Build Instructions do not say that the "patch" program is needed
+ * [SYNCOPE-1488] - Change to MVM Env for JDK > 8.00
+ * [SYNCOPE-1492] - Build Instructions are missing an EVN (DOCKER_HOST) needed for mvn -Ppostgres-it
+ * [SYNCOPE-1493] - Mapping unique schema as remote key never matches internal objects
+
+** Improvement
+ * [SYNCOPE-1463] - Improve UX enduser User Request management
+ * [SYNCOPE-1468] - Allow for configurable org.quartz.jobStore.misfireThreshold
+ * [SYNCOPE-1473] - Provide a PropagationActions to maintain a conservative membership policy management
+
+** Task
+ * [SYNCOPE-1464] - Upgrade to Apache Netbeans Maven dependencies
+
+Release Notes - Syncope - Version 2.1.4
+================================================================================
+
+** Bug
+ * [SYNCOPE-1428] - APIs to read by key return 404 instead of 401 for not authenticated calls
+ * [SYNCOPE-1429] - Wildcard case-insesitive queries do not work with Elasticsearch
+ * [SYNCOPE-1430] - ItemTransformer for Date schemas throws NPE
+ * [SYNCOPE-1431] - Connector and Resource history compare does not work
+ * [SYNCOPE-1432] - After creating new connector / resource, Topology does not show it
+ * [SYNCOPE-1437] - Error while searching for users / groups / any objects with Elasticsearch when no data are present
+ * [SYNCOPE-1438] - "changePwdDate" field is not initialized when create a new user with the specified password
+ * [SYNCOPE-1439] - User membership attributes not updated
+ * [SYNCOPE-1440] - Pagination of Users/Groups doesn't work as expected with Elasticsearch
+ * [SYNCOPE-1442] - Inactive Job with cron expression set is executed anyway
+ * [SYNCOPE-1443] - Changing Display Rows number in Reconciliation Resource Panel doesn't work
+ * [SYNCOPE-1446] - Persistence exception on PostgreSQL when AUDIT is enabled on propagation tasks
+ * [SYNCOPE-1447] - NPE while deleting a privilege from admin console
+ * [SYNCOPE-1448] - Bean loading/register section not threadsafe
+ * [SYNCOPE-1450] - Audit: sensitive information not masked by default during update
+ * [SYNCOPE-1452] - Notification about is not deleted after update
+ * [SYNCOPE-1453] - MappingItem with "mustChangePassword" field cannot be provisioned and updated during import
+ * [SYNCOPE-1454] - Avoid duplicated Propagation Tasks
+ * [SYNCOPE-1457] - NonAlphaNumeric policy pattern matches the "Not word" character class
+
+** New Feature
+ * [SYNCOPE-1401] - Leverage MySQL JSON type
+
+** Improvement
+ * [SYNCOPE-1433] - Unflag/flag uniqueness shouldn't be permitted
+ * [SYNCOPE-1436] - Remove pullPolicy EAGER fetchType from JPAExternalResource
+ * [SYNCOPE-1441] - Perform in-memory match for dynamic conditions
+ * [SYNCOPE-1444] - Pull correlation rules: allow to discriminate ongoing event
+ * [SYNCOPE-1445] - Docker: support pgjsonb as DBMS option
+ * [SYNCOPE-1449] - Support multi-value attributes in JEXL expressions
+
+** Task
+ * [SYNCOPE-1400] - Support MySQL 8
+
+Release Notes - Syncope - Version 2.1.3
+================================================================================
+
+** Bug
+ * [SYNCOPE-1391] - Check template for confirmPasswordReset and mustChangePassword
+ * [SYNCOPE-1393] - jexl function fullPath2Dn return invalid value for ROOT realm
+ * [SYNCOPE-1399] - Error while executing the custom task to initialize indices with Elasticsearch v6.x
+ * [SYNCOPE-1404] - Dialog not closing in Netbeans ide plugin when creating a new element
+ * [SYNCOPE-1405] - Error during db initialization: views.xml always set for PostgreSQL
+ * [SYNCOPE-1406] - Error during startup because of missing property 'historyLevel'
+ * [SYNCOPE-1407] - Date pattern ignored by widget
+ * [SYNCOPE-1408] - Partial user edit via Role layout implies removing all unmanaged attributes
+ * [SYNCOPE-1411] - User/Any object updates generate attributes with null owner in case of patches involving membership attributes
+ * [SYNCOPE-1417] - Search with order by two plain attributes gives no results
+ * [SYNCOPE-1419] - User and AnyObject search fails in case of not leaf conditions given on multivalue fields
+ * [SYNCOPE-1420] - Expired Access Tokens might impede successful authentication
+ * [SYNCOPE-1425] - Mapping item transformers do not work for non-string values
+
+** New Feature
+ * [SYNCOPE-1368] - Add some accessibility features to Console
+ * [SYNCOPE-1395] - Leverage PostgreSQL's jsonb type
+
+** Improvement
+ * [SYNCOPE-1392] - Reduce usage of Reflection to improve overall performance
+ * [SYNCOPE-1394] - Add un-claim capability for requests
+ * [SYNCOPE-1396] - Give the possibility to configure TLS client parameters
+ * [SYNCOPE-1397] - No Such element exception while editing USER update approval
+ * [SYNCOPE-1409] - Avoid double round-trip to External Resource during Push
+ * [SYNCOPE-1412] - Serch for identities with null attributes can be improved
+ * [SYNCOPE-1416] - remove user_search_null_attr view
+ * [SYNCOPE-1422] - Permit to provide custom implementation of NotificationManager and AuditManager
+ * [SYNCOPE-1424] - Improve Propagation task ordered search
+
+** Task
+ * [SYNCOPE-1381] - Support user request from Enduser UI
+ * [SYNCOPE-1402] - Upgrade to Validation API 2.0
+
+Release Notes - Syncope - Version 2.1.2
+================================================================================
+
+** Bug
+ * [SYNCOPE-1360] - Delegated administration to Dynamic Realms not possible
+ * [SYNCOPE-1361] - Custom audit appender does not work after a restart
+ * [SYNCOPE-1362] - Sorting users by creation date raises RuntimeException
+ * [SYNCOPE-1363] - Deleting multiple users at once reports "Operation delete not supported"
+ * [SYNCOPE-1364] - Upgrade tool from 2.0 script error
+ * [SYNCOPE-1365] - Erorr during retrieve candidate groups for approval process
+ * [SYNCOPE-1366] - Audit events ownership always set to admin user
+ * [SYNCOPE-1370] - Password reset succeeds also on wrong captcha
+ * [SYNCOPE-1371] - After upgrade from 2.0, error when updating Realm: ClassCastException: Expected LOGIC_ACTIONS, got PULL_ACTIONS
+ * [SYNCOPE-1372] - Password history checks not effective
+ * [SYNCOPE-1373] - Custom task schedule is reset after update
+ * [SYNCOPE-1374] - Concurrent propagation tasks for non-Master domains not saved
+ * [SYNCOPE-1375] - The existence of a membership attribute mapping implies membership creation during pull
+ * [SYNCOPE-1376] - swagger-ui server URL incorrect behind ssl reverse proxy
+ * [SYNCOPE-1377] - Wrong X-Syncope-Domain header does not throw an error
+ * [SYNCOPE-1380] - During Push or Pull, if policy with conflict resolution IGNORE is set, the process is interrupted as soon as such setting applies
+ * [SYNCOPE-1383] - Exception during "getObject" from external resource
+ * [SYNCOPE-1387] - ClassCast exception when pull realms
+ * [SYNCOPE-1388] - mustChangePassword flag does not prevent user from invoking actions
+ * [SYNCOPE-1389] - In case of virtual attribute mapping, propagation is always set as UPDATE also in case of CREATE
+ * [SYNCOPE-1390] - Pull Realms: pull task with Unmatching Rules: PROVISION shouldn't create propagation task
+
+** New Feature
+ * [SYNCOPE-1019] - Template mechanism for Enduser UI
+ * [SYNCOPE-1220] - Support Groovy implementations in the Netbeans IDE plugin
+ * [SYNCOPE-1367] - Add some accessibility features to Enduser
+ * [SYNCOPE-1369] - User requests
+
+** Improvement
+ * [SYNCOPE-1379] - Make configurable resource check timeout
+ * [SYNCOPE-1382] - Failure specifying push task filters including db column mapped as integer
+ * [SYNCOPE-1384] - SAML 2.0: Allow to customize RequestedAuthnContext for a given Service Provider
+ * [SYNCOPE-1385] - Priority propagation timeout hard coded into PriorityPropagationTaskExecutor
+
+Release Notes - Syncope - Version 2.1.1
+================================================================================
+
+** Bug
+ * [SYNCOPE-1331] - ExternalResourcePropagationAction is too long name for a table in Oracle DB
+ * [SYNCOPE-1333] - Missing virtual attribute value in case of type extension
+ * [SYNCOPE-1334] - Maven install problem with Apache Syncope 2.1.0
+ * [SYNCOPE-1335] - Missing SQL statements when upgrading from 2.0 Jazz
+ * [SYNCOPE-1337] - Password history policy is not enforced on salted passwords
+ * [SYNCOPE-1338] - Double type conversion applied during pull leads to errors
+ * [SYNCOPE-1339] - Enduser spinner does not apply to the whole page
+ * [SYNCOPE-1340] - Cannot update membership attribute
+ * [SYNCOPE-1342] - console UI login form ignores Domain selection
+ * [SYNCOPE-1343] - Attributes are not reset after pull of null values
+ * [SYNCOPE-1344] - CORE_SCHEME not being updated in enduser.properties
+ * [SYNCOPE-1346] - Adding a new task while re-executing a propagation task
+ * [SYNCOPE-1347] - Invocation Problem calling org.apache.syncope.installer.processes.ArchetypeProcess
+ * [SYNCOPE-1350] - Date values not formatted according to the conversion pattern
+ * [SYNCOPE-1352] - Group wizard doesn't update the plain attributes
+ * [SYNCOPE-1353] - DBPasswordPropagationActions link in the reference guide is wrong
+ * [SYNCOPE-1354] - Push Tasks do not send status onto External Resources
+ * [SYNCOPE-1356] - LDAPMembershipPullActions does not remove memberships
+ * [SYNCOPE-1357] - MemoryVirAttrCache not working
+ * [SYNCOPE-1358] - Search by boolean value does not work from Admin Console
+
+** New Feature
+ * [SYNCOPE-1348] - REST: replace bulk operations with batch requests
+
+** Improvement
+ * [SYNCOPE-1336] - Add pagination for approvals forms
+ * [SYNCOPE-1341] - Domain should be configurable parameter for syncope-enduser docker image
+
Release Notes - Syncope - Version 2.1.0
================================================================================
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 7bc7e4b1c36..00000000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,22 +0,0 @@
-
-**Apache Syncope** is an Open Source system for managing digital identities in enterprise environments,
-implemented in Java EE technology and released under Apache 2.0 license.
-
-Would you like to contribute? Visit http://syncope.apache.org/contributing.html
diff --git a/NOTICE b/NOTICE
index f9f015bf388..d7aa1399613 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,5 +1,5 @@
Apache Syncope
-Copyright 2012-2018 The Apache Software Foundation
+Copyright 2012-2023 The Apache Software Foundation
This product includes software developed by:
The Apache Software Foundation (http://www.apache.org/).
diff --git a/README.md b/README.md
deleted file mode 100644
index c4468af4354..00000000000
--- a/README.md
+++ /dev/null
@@ -1,33 +0,0 @@
-
-**Apache Syncope** is an Open Source system for managing digital identities in enterprise environments,
-implemented in Java EE technology and released under Apache 2.0 license.
-
-More information at http://syncope.apache.org
-
-
-
-
-
-
-
-
-
-
-
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 5fc0c02eb89..00000000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,40 +0,0 @@
-# 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.
-version: '{build}'
-os: Windows Server 2012
-init:
- - git config --global core.autocrlf true
-install:
- - cinst git
- - ps: |
- Add-Type -AssemblyName System.IO.Compression.FileSystem
- if (!(Test-Path -Path "C:\maven" )) {
- (new-object System.Net.WebClient).DownloadFile('https://www.apache.org/dist/maven/maven-3/3.5.3/binaries/apache-maven-3.5.3-bin.zip', 'C:\maven-bin.zip')
- [System.IO.Compression.ZipFile]::ExtractToDirectory("C:\maven-bin.zip", "C:\maven")
- }
- - cmd: SET PATH=C:\maven\apache-maven-3.5.3\bin;%JAVA_HOME%\bin;=%;
- - cmd: SET JAVA_HOME=C:\Program Files\Java\jdk1.8.0
- - cmd: SET M2_HOME=C:\maven\apache-maven-3.5.3
- - cmd: SET MAVEN_OPTS=-Xmx4g
- - cmd: SET JAVA_OPTS=-Xmx4g
-build_script:
- - mvn --show-version --quiet -T 1C -PskipTests,all --batch-mode
-test_script:
- - mvn -T 1C clean install --batch-mode
-cache:
- - C:\maven\
- - C:\Users\appveyor\.m2
diff --git a/archetype/pom.xml b/archetype/pom.xml
index 0c41b566524..66921a00df2 100644
--- a/archetype/pom.xml
+++ b/archetype/pom.xml
@@ -24,7 +24,7 @@ under the License.
org.apache.syncope
syncope
- 2.1.1-SNAPSHOT
+ 2.1.15-SNAPSHOT
Apache Syncope Archetype
@@ -42,7 +42,7 @@ under the License.
org.apache.maven.archetype
archetype-packaging
- 3.0.1
+ 3.2.0
@@ -51,7 +51,7 @@ under the License.
org.apache.maven.plugins
maven-archetype-plugin
- 3.0.1
+ 3.2.0
true
@@ -142,6 +142,14 @@ under the License.
${project.build.outputDirectory}/archetype-resources/core/src/main/resources
+
+ ../core/persistence-jpa-json/src/main/resources/
+
+ META-INF/*
+ audit/*
+
+ ${project.build.outputDirectory}/archetype-resources/core/src/main/resources
+
../core/persistence-jpa/src/test/resources/domains
${project.build.outputDirectory}/archetype-resources/core/src/test/resources/domains
@@ -182,6 +190,13 @@ under the License.
provisioning.properties
+
+ ../fit/core-reference/src/test/resources
+ ${project.build.outputDirectory}/archetype-resources/core/src/test/resources
+
+ mail.properties
+
+
../fit/core-reference/src/test/resources/scriptedsql
${project.build.outputDirectory}/archetype-resources/core/src/test/resources/scriptedsql
@@ -213,7 +228,6 @@ under the License.
../fit/core-reference/src/test/resources
${project.build.outputDirectory}/archetype-resources/core/src/test/resources
- addFlowableToContent.xsl
keystore
@@ -244,10 +258,6 @@ under the License.
${project.build.outputDirectory}/archetype-resources/console/src/main/resources
log4j2.xml
- save-model.html
- app-cfg.js
- url-config.js
- toolbar.js
@@ -276,7 +286,8 @@ under the License.
${project.build.outputDirectory}/archetype-resources/enduser/src/main/resources
enduser.properties
- customForm.json
+ customFormAttributes.json
+ customTemplate.json
@@ -309,17 +320,10 @@ under the License.
${project.build.outputDirectory}/archetype-resources/enduser/src/test/resources
enduser.properties
+ customFormAttributes.json
+ customTemplate.json
saml2sp-agent.properties
- customForm.json
-
-
-
- ../fit/enduser-reference/src/main/resources
- ${project.build.outputDirectory}/archetype-resources/enduser/src/test/resources
-
- enduser.properties
oidcclient-agent.properties
- customForm.json
diff --git a/archetype/src/main/resources/archetype-resources/console/pom.xml b/archetype/src/main/resources/archetype-resources/console/pom.xml
index 479126c6925..9f472c65d32 100644
--- a/archetype/src/main/resources/archetype-resources/console/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/console/pom.xml
@@ -43,8 +43,8 @@ under the License.
javax.servlet.jsp-api
- javax.servlet
- jstl
+ org.apache.taglibs
+ taglibs-standard-impl
@@ -62,22 +62,6 @@ under the License.
org.slf4j
slf4j-api
-
- org.apache.logging.log4j
- log4j-api
-
-
- org.apache.logging.log4j
- log4j-core
-
-
- com.lmax
- disruptor
-
-
- org.apache.logging.log4j
- log4j-slf4j-impl
-
commons-logging
commons-logging
@@ -96,13 +80,6 @@ under the License.
org.webjars
codemirror
-
-
-
- junit
- junit
- test
-
@@ -128,6 +105,12 @@ under the License.
all
+
+ org.apache.syncope.ext.flowable
+ syncope-ext-flowable-client-console
+ ${syncope.version}
+
+
org.apache.syncope.ext.camel
syncope-ext-camel-client-console
@@ -151,90 +134,9 @@ under the License.
syncope-ext-scimv2-client-console
${syncope.version}
-
-
- org.flowable
- flowable-ui-modeler-app
- war
- test
-
-
-
- org.apache.maven.plugins
- maven-antrun-plugin
- true
-
-
- setupFlowableModeler
- process-resources
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- run
-
-
-
-
-
-
src/main/resources
diff --git a/archetype/src/main/resources/archetype-resources/core/pom.xml b/archetype/src/main/resources/archetype-resources/core/pom.xml
index f248d9a79a2..ed72c53dba5 100644
--- a/archetype/src/main/resources/archetype-resources/core/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/core/pom.xml
@@ -42,8 +42,8 @@ under the License.
javax.servlet.jsp-api
- javax.servlet
- jstl
+ org.apache.taglibs
+ taglibs-standard-impl
@@ -71,20 +71,16 @@ under the License.
org.apache.logging.log4j
- log4j-api
+ log4j-core
org.apache.logging.log4j
- log4j-core
+ log4j-slf4j-impl
com.lmax
disruptor
-
- org.apache.logging.log4j
- log4j-slf4j-impl
-
commons-logging
commons-logging
@@ -160,8 +156,8 @@ under the License.
- org.apache.syncope.core
- syncope-core-workflow-flowable
+ org.apache.syncope.ext.flowable
+ syncope-ext-flowable-rest-cxf
${syncope.version}
diff --git a/archetype/src/main/resources/archetype-resources/core/src/test/resources/addFlowableToContent.xsl b/archetype/src/main/resources/archetype-resources/core/src/test/resources/addFlowableToContent.xsl
new file mode 100644
index 00000000000..70aa21bcbb8
--- /dev/null
+++ b/archetype/src/main/resources/archetype-resources/core/src/test/resources/addFlowableToContent.xsl
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/archetype/src/main/resources/archetype-resources/enduser/pom.xml b/archetype/src/main/resources/archetype-resources/enduser/pom.xml
index 5f98d31f920..442a5a5fb03 100644
--- a/archetype/src/main/resources/archetype-resources/enduser/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/enduser/pom.xml
@@ -43,8 +43,8 @@ under the License.
javax.servlet.jsp-api
- javax.servlet
- jstl
+ org.apache.taglibs
+ taglibs-standard-impl
@@ -64,20 +64,16 @@ under the License.
org.apache.logging.log4j
- log4j-api
+ log4j-core
org.apache.logging.log4j
- log4j-core
+ log4j-slf4j-impl
com.lmax
disruptor
-
- org.apache.logging.log4j
- log4j-slf4j-impl
-
commons-logging
commons-logging
@@ -247,7 +243,11 @@ under the License.
todir="${project.build.directory}/${project.build.finalName}/WEB-INF/classes"
overwrite="true"/>
-
+
+
@@ -338,6 +338,12 @@ under the License.
syncope-ext-oidcclient-client-enduser
${syncope.version}
+
+
+ org.apache.syncope.ext.flowable
+ syncope-ext-flowable-client-enduser
+ ${syncope.version}
+
diff --git a/archetype/src/main/resources/meta-pom.xml b/archetype/src/main/resources/meta-pom.xml
index 0b163aec703..af7861f758b 100644
--- a/archetype/src/main/resources/meta-pom.xml
+++ b/archetype/src/main/resources/meta-pom.xml
@@ -36,6 +36,7 @@ under the License.
${jwsKey}
${adminPassword}
+ none
true
true
@@ -69,12 +70,6 @@ under the License.
syncope-core-persistence-jpa
${syncope.version}
-
-
- org.apache.syncope.core
- syncope-core-workflow-flowable
- ${syncope.version}
-
org.apache.syncope.client
@@ -90,24 +85,6 @@ under the License.
-
-
-
- org.codehaus.mojo
- ianal-maven-plugin
- true
-
-
-
- verify-legal-files
-
- none
-
-
-
-
-
-
common
core
diff --git a/client/cli/LICENSE b/client/cli/LICENSE
index eeb76fe47f6..eb607cf5d97 100644
--- a/client/cli/LICENSE
+++ b/client/cli/LICENSE
@@ -213,7 +213,231 @@ This is licensed under the AL 2.0, see above.
==
-For JAX-B (http://jaxb.java.net/):
+For Swagger (http://swagger.io/):
+This is licensed under the AL 2.0, see above.
+
+==
+
+For Jakarta Activation (https://eclipse-ee4j.github.io/jaf/):
+This is licensed under the EPL 1.0:
+
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
+LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
+CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and documentation
+ distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+ i) changes to the Program, and
+ ii) additions to the Program;
+
+ where such changes and/or additions to the Program originate from and are
+ distributed by that particular Contributor. A Contribution 'originates'
+ from a Contributor if it was added to the Program by such Contributor
+ itself or anyone acting on such Contributor's behalf. Contributions do not
+ include additions to the Program which: (i) are separate modules of
+ software distributed in conjunction with the Program under their own
+ license agreement, and (ii) are not derivative works of the Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents" mean patent claims licensable by a Contributor which are
+necessarily infringed by the use or sale of its Contribution alone or when
+combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this
+Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement,
+including all Contributors.
+
+2. GRANT OF RIGHTS
+ a) Subject to the terms of this Agreement, each Contributor hereby grants
+ Recipient a non-exclusive, worldwide, royalty-free copyright license to
+ reproduce, prepare derivative works of, publicly display, publicly
+ perform, distribute and sublicense the Contribution of such Contributor,
+ if any, and such derivative works, in source code and object code form.
+ b) Subject to the terms of this Agreement, each Contributor hereby grants
+ Recipient a non-exclusive, worldwide, royalty-free patent license under
+ Licensed Patents to make, use, sell, offer to sell, import and otherwise
+ transfer the Contribution of such Contributor, if any, in source code and
+ object code form. This patent license shall apply to the combination of
+ the Contribution and the Program if, at the time the Contribution is
+ added by the Contributor, such addition of the Contribution causes such
+ combination to be covered by the Licensed Patents. The patent license
+ shall not apply to any other combinations which include the Contribution.
+ No hardware per se is licensed hereunder.
+ c) Recipient understands that although each Contributor grants the licenses
+ to its Contributions set forth herein, no assurances are provided by any
+ Contributor that the Program does not infringe the patent or other
+ intellectual property rights of any other entity. Each Contributor
+ disclaims any liability to Recipient for claims brought by any other
+ entity based on infringement of intellectual property rights or
+ otherwise. As a condition to exercising the rights and licenses granted
+ hereunder, each Recipient hereby assumes sole responsibility to secure
+ any other intellectual property rights needed, if any. For example, if a
+ third party patent license is required to allow Recipient to distribute
+ the Program, it is Recipient's responsibility to acquire that license
+ before distributing the Program.
+ d) Each Contributor represents that to its knowledge it has sufficient
+ copyright rights in its Contribution, if any, to grant the copyright
+ license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under
+its own license agreement, provided that:
+
+ a) it complies with the terms and conditions of this Agreement; and
+ b) its license agreement:
+ i) effectively disclaims on behalf of all Contributors all warranties
+ and conditions, express and implied, including warranties or
+ conditions of title and non-infringement, and implied warranties or
+ conditions of merchantability and fitness for a particular purpose;
+ ii) effectively excludes on behalf of all Contributors all liability for
+ damages, including direct, indirect, special, incidental and
+ consequential damages, such as lost profits;
+ iii) states that any provisions which differ from this Agreement are
+ offered by that Contributor alone and not by any other party; and
+ iv) states that source code for the Program is available from such
+ Contributor, and informs licensees how to obtain it in a reasonable
+ manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+ a) it must be made available under this Agreement; and
+ b) a copy of this Agreement must be included with each copy of the Program.
+ Contributors may not remove or alter any copyright notices contained
+ within the Program.
+
+Each Contributor must identify itself as the originator of its Contribution,
+if
+any, in a manner that reasonably allows subsequent Recipients to identify the
+originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities with
+respect to end users, business partners and the like. While this license is
+intended to facilitate the commercial use of the Program, the Contributor who
+includes the Program in a commercial product offering should do so in a manner
+which does not create potential liability for other Contributors. Therefore,
+if a Contributor includes the Program in a commercial product offering, such
+Contributor ("Commercial Contributor") hereby agrees to defend and indemnify
+every other Contributor ("Indemnified Contributor") against any losses,
+damages and costs (collectively "Losses") arising from claims, lawsuits and
+other legal actions brought by a third party against the Indemnified
+Contributor to the extent caused by the acts or omissions of such Commercial
+Contributor in connection with its distribution of the Program in a commercial
+product offering. The obligations in this section do not apply to any claims
+or Losses relating to any actual or alleged intellectual property
+infringement. In order to qualify, an Indemnified Contributor must:
+a) promptly notify the Commercial Contributor in writing of such claim, and
+b) allow the Commercial Contributor to control, and cooperate with the
+Commercial Contributor in, the defense and any related settlement
+negotiations. The Indemnified Contributor may participate in any such claim at
+its own expense.
+
+For example, a Contributor might include the Program in a commercial product
+offering, Product X. That Contributor is then a Commercial Contributor. If
+that Commercial Contributor then makes performance claims, or offers
+warranties related to Product X, those performance claims and warranties are
+such Commercial Contributor's responsibility alone. Under this section, the
+Commercial Contributor would have to defend claims against the other
+Contributors related to those performance claims and warranties, and if a
+court requires any other Contributor to pay any damages as a result, the
+Commercial Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
+IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each
+Recipient is solely responsible for determining the appropriateness of using
+and distributing the Program and assumes all risks associated with its
+exercise of rights under this Agreement , including but not limited to the
+risks and costs of program errors, compliance with applicable laws, damage to
+or loss of data, programs or equipment, and unavailability or interruption of
+operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
+CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION
+LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
+EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY
+OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of the
+remainder of the terms of this Agreement, and without further action by the
+parties hereto, such provision shall be reformed to the minimum extent
+necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Program itself
+(excluding combinations of the Program with other software or hardware)
+infringes such Recipient's patent(s), then such Recipient's rights granted
+under Section 2(b) shall terminate as of the date such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails to
+comply with any of the material terms or conditions of this Agreement and does
+not cure such failure in a reasonable period of time after becoming aware of
+such noncompliance. If all Recipient's rights under this Agreement terminate,
+Recipient agrees to cease use and distribution of the Program as soon as
+reasonably practicable. However, Recipient's obligations under this Agreement
+and any licenses granted by Recipient relating to the Program shall continue
+and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in
+order to avoid inconsistency the Agreement is copyrighted and may only be
+modified in the following manner. The Agreement Steward reserves the right to
+publish new versions (including revisions) of this Agreement from time to
+time. No one other than the Agreement Steward has the right to modify this
+Agreement. The Eclipse Foundation is the initial Agreement Steward. The
+Eclipse Foundation may assign the responsibility to serve as the Agreement
+Steward to a suitable separate entity. Each new version of the Agreement will
+be given a distinguishing version number. The Program (including
+Contributions) may always be distributed subject to the version of the
+Agreement under which it was received. In addition, after a new version of the
+Agreement is published, Contributor may elect to distribute the Program
+(including its Contributions) under the new version. Except as expressly
+stated in Sections 2(a) and 2(b) above, Recipient receives no rights or
+licenses to the intellectual property of any Contributor under this Agreement,
+whether expressly, by implication, estoppel or otherwise. All rights in the
+Program not expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the
+intellectual property laws of the United States of America. No party to this
+Agreement will bring a legal action under this Agreement more than one year
+after the cause of action arose. Each party waives its rights to a jury trial in
+any resulting litigation.
+
+==
+
+For Eclipse Project for JAX-RS (https://projects.eclipse.org/projects/ee4j.jaxrs):
+This is licensed under the EPL 1.0, see above.
+
+==
+
+For Jakarta XML Binding (https://eclipse-ee4j.github.io/jaxb-ri/):
+This is licensed under the EPL 1.0, see above.
+
+==
+
+For javax.annotation-api (https://jcp.org/en/jsr/detail?id=250):
This is licensed under the CDDL 1.0:
COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
@@ -599,26 +823,11 @@ of liability.
==
-For javax.annotation-api (https://jcp.org/en/jsr/detail?id=250):
-This is licensed under the CDDL 1.0, see above.
-
-==
-
For Bean Validation API (http://beanvalidation.org/):
This is licensed under the AL 2.0, see above.
==
-For javax.ws.rs-api (https://jax-rs-spec.java.net/):
-This is licensed under the CDDL 1.0, see above.
-
-==
-
-For Joda Time (http://www.joda.org/joda-time/):
-This is licensed under the AL 2.0, see above.
-
-==
-
For StAX2 API (http://wiki.fasterxml.com/WoodstoxStax2):
This is licensed under the BSD license:
@@ -685,3 +894,8 @@ This is licensed under the MIT license:
For Spring Framework (http://projects.spring.io/spring-framework/):
This is licensed under the AL 2.0, see above.
+
+==
+
+For SnakeYAML (http://www.snakeyaml.org/):
+This is licensed under the AL 2.0, see above.
diff --git a/client/cli/NOTICE b/client/cli/NOTICE
index 3171f8e46e5..45b43871349 100644
--- a/client/cli/NOTICE
+++ b/client/cli/NOTICE
@@ -1,5 +1,5 @@
Apache Syncope
-Copyright 2012-2018 The Apache Software Foundation
+Copyright 2012-2023 The Apache Software Foundation
This product includes software developed by:
The Apache Software Foundation (http://www.apache.org/).
@@ -13,28 +13,34 @@ This product includes software developed by the Jackson project.
==
-This product includes software developed by the JAXB project.
-Copyright (c) 2013-2016 The JAXB project.
+This product includes software developed by the Swagger project.
+Copyright 2016 SmartBear Software
==
-This product includs software developed by Oracle.
-Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
+This product includes software developed by the Eclipse Foundation.
+Copyright (c) 2019, 2020 Eclipse Foundation. All rights reserved.
==
-This product includes software developed by the Bean Validation Project (http://beanvalidation.org).
-Copyright (c) Red Hat, Inc., Emmanuel Bernard
+This product includes software developed by the Eclipse Project for JAX-RS.
+Eclipse Project for JAX-RS is a trademark of the Eclipse Foundation.
+All content is the property of the respective authors or their employers. For more information regarding authorship of content, please consult the listed source code repository logs.
+
+==
+
+This product includes software developed by the Eclipse Foundation.
+Copyright (c) 2019, 2020 Eclipse Foundation. All rights reserved.
==
-This product includes software developed by the JAX-RS project.
-Copyright (c) 2014, Oracle Corporation and/or its affiliates. All rights reserved.
+This product includs software developed by Oracle.
+Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
==
-This product includes software developed by the Joda Time project.
-Copyright (c) 2002-2016 Joda.org. All Rights Reserved.
+This product includes software developed by the Bean Validation Project (http://beanvalidation.org).
+Copyright (c) Red Hat, Inc., Emmanuel Bernard
==
@@ -59,3 +65,7 @@ Copyright (c) 2004-2016 QOS.ch.
This product includes software developed by SpringSource.
Copyright (c) 2004-2016 SpringSource
All rights reserved.
+
+==
+
+This product includes software developed by the SnakeYAML project.
diff --git a/client/cli/pom.xml b/client/cli/pom.xml
index 0cfaedf3f24..bf308b536db 100644
--- a/client/cli/pom.xml
+++ b/client/cli/pom.xml
@@ -24,7 +24,7 @@ under the License.
org.apache.syncope
syncope-client
- 2.1.1-SNAPSHOT
+ 2.1.15-SNAPSHOT
Apache Syncope Client CLI
@@ -98,7 +98,7 @@ under the License.
org.junit.jupiter
- junit-jupiter-engine
+ junit-jupiter
test
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeServices.java b/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeServices.java
index b2606e4e2e5..29c6feb6382 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeServices.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/SyncopeServices.java
@@ -28,6 +28,7 @@
import org.apache.syncope.client.cli.util.JasyptUtils;
import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
+import org.apache.syncope.client.lib.batch.BatchRequest;
import org.apache.syncope.common.rest.api.service.SyncopeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,7 +39,7 @@ public final class SyncopeServices {
private static String SYNCOPE_ADDRESS;
- public static T get(final Class clazz) {
+ private static SyncopeClient client() {
final Properties properties = new Properties();
try (InputStream is = Files.newInputStream(Paths.get(InstallConfigFileTemplate.configurationFilePath()))) {
properties.load(is);
@@ -49,13 +50,19 @@ public static T get(final Class clazz) {
String syncopeAdminPassword = JasyptUtils.get().decrypt(properties.getProperty("syncope.admin.password"));
SYNCOPE_ADDRESS = properties.getProperty("syncope.rest.services");
String useGZIPCompression = properties.getProperty("useGZIPCompression");
- SyncopeClient syncopeClient = new SyncopeClientFactoryBean().
+ return new SyncopeClientFactoryBean().
setAddress(SYNCOPE_ADDRESS).
setUseCompression(BooleanUtils.toBoolean(useGZIPCompression)).
create(properties.getProperty("syncope.admin.user"), syncopeAdminPassword);
+ }
+ public static T get(final Class clazz) {
LOG.debug("Creating service for {}", clazz.getName());
- return syncopeClient.getService(clazz);
+ return client().getService(clazz);
+ }
+
+ public static BatchRequest batch() {
+ return client().batch();
}
public static String getAddress() {
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/Info.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/Info.java
index 8167cc595a8..e277b9da02f 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/Info.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/Info.java
@@ -73,21 +73,9 @@ public void selfRegistrationAllowed() {
public void provisioningManager() {
try {
infoResultManager.printProvisioningManager(
- platformInfo.getAnyObjectProvisioningManager(),
- platformInfo.getUserProvisioningManager(),
- platformInfo.getGroupProvisioningManager());
- } catch (final Exception ex) {
- LOG.error("Information error", ex);
- infoResultManager.genericError(ex.getMessage());
- }
- }
-
- public void workflowAdapter() {
- try {
- infoResultManager.printWorkflowAdapter(
- platformInfo.getAnyObjectWorkflowAdapter(),
- platformInfo.getUserWorkflowAdapter(),
- platformInfo.getGroupWorkflowAdapter());
+ platformInfo.getProvisioningInfo().getAnyObjectProvisioningManager(),
+ platformInfo.getProvisioningInfo().getUserProvisioningManager(),
+ platformInfo.getProvisioningInfo().getGroupProvisioningManager());
} catch (final Exception ex) {
LOG.error("Information error", ex);
infoResultManager.genericError(ex.getMessage());
@@ -246,7 +234,7 @@ public void passwordGenerator() {
public void virAttrCache() {
try {
- infoResultManager.printVirtualAttributeCacheClass(platformInfo.getVirAttrCache());
+ infoResultManager.printVirtualAttributeCacheClass(platformInfo.getProvisioningInfo().getVirAttrCache());
} catch (final Exception ex) {
LOG.error("Information error", ex);
infoResultManager.genericError(ex.getMessage());
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/InfoCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/InfoCommand.java
index 28559be44fe..460d54a3990 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/InfoCommand.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/InfoCommand.java
@@ -52,9 +52,6 @@ public void execute(final Input input) {
case PROVISIONING_MANAGER:
info.provisioningManager();
break;
- case WORKFLOW_ADAPTER:
- info.workflowAdapter();
- break;
case ACCOUNT_RULES:
info.accountRules();
break;
@@ -122,7 +119,6 @@ private enum Options {
PWD_RESET_WITH_SECURITY_QUESTION("--pwd-reset-with-question"),
SELF_REG_ALLOWED("--self-reg-allowed"),
PROVISIONING_MANAGER("--provisioning-manager-classes"),
- WORKFLOW_ADAPTER("--workflow-adapter-classes"),
ACCOUNT_RULES("--account-rules-classes"),
CONNID_LOCATION("--connid-locations"),
RECON_FILTER_BUILDERS("--reconciliation-filter-builders"),
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/InfoResultManager.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/InfoResultManager.java
index d19b0aeddae..65ba2063360 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/InfoResultManager.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/InfoResultManager.java
@@ -48,15 +48,6 @@ public void printProvisioningManager(final String anyObjectProvisioningManager,
"Group provisioning manager class: " + getGroupProvisioningManager);
}
- public void printWorkflowAdapter(final String anyObjectWorkflowAdapter,
- final String userWorkflowAdapter,
- final String groupWorkflowAdapter) {
- genericMessage(
- "Any object workflow adapter class: " + anyObjectWorkflowAdapter,
- "User workflow adapter class: " + userWorkflowAdapter,
- "Group workflow adapter class: " + groupWorkflowAdapter);
- }
-
public void printAccountRules(final Collection rules) {
rules.forEach(accountRule -> {
genericMessage("Account rule: " + accountRule);
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerResultManager.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerResultManager.java
index 327f8c08209..2865b9209f6 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerResultManager.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerResultManager.java
@@ -26,7 +26,7 @@
import java.util.Map;
import org.apache.syncope.client.cli.commands.CommonsResultManager;
import org.apache.syncope.client.cli.view.Table;
-import org.apache.syncope.common.lib.log.LogStatementTO;
+import org.apache.syncope.common.lib.log.LogStatement;
import org.apache.syncope.common.lib.log.LoggerTO;
public class LoggerResultManager extends CommonsResultManager {
@@ -41,9 +41,9 @@ public void fromListMemoryAppenders(final List memoryAppenders) {
tableBuilder.build().print();
}
- public void fromGetLastLogStatements(final List statements) throws JsonProcessingException {
+ public void fromGetLastLogStatements(final List statements) throws JsonProcessingException {
final Table.TableBuilder tableBuilder = new Table.TableBuilder("last statements").header("statement");
- for (final LogStatementTO statement : statements) {
+ for (final LogStatement statement : statements) {
tableBuilder.rowValues(Collections.singletonList(
MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(statement)));
}
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerSyncopeOperations.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerSyncopeOperations.java
index b1d52bfb23f..8b44c37aa55 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerSyncopeOperations.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/logger/LoggerSyncopeOperations.java
@@ -22,7 +22,7 @@
import java.util.stream.Collectors;
import org.apache.syncope.client.cli.SyncopeServices;
import org.apache.syncope.common.lib.log.LogAppender;
-import org.apache.syncope.common.lib.log.LogStatementTO;
+import org.apache.syncope.common.lib.log.LogStatement;
import org.apache.syncope.common.lib.log.LoggerTO;
import org.apache.syncope.common.lib.types.LoggerType;
import org.apache.syncope.common.rest.api.service.LoggerService;
@@ -35,7 +35,7 @@ public List listMemoryAppenders() {
return loggerService.memoryAppenders().stream().map(LogAppender::getName).collect(Collectors.toList());
}
- public List getLastLogStatements(final String appender) {
+ public List getLastLogStatements(final String appender) {
return loggerService.getLastLogStatements(appender);
}
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/realm/RealmSyncopeOperations.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/realm/RealmSyncopeOperations.java
index 19af12f7709..75c07fec862 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/realm/RealmSyncopeOperations.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/realm/RealmSyncopeOperations.java
@@ -20,6 +20,7 @@
import java.util.List;
import org.apache.syncope.client.cli.SyncopeServices;
+import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.to.RealmTO;
import org.apache.syncope.common.rest.api.service.RealmService;
@@ -28,7 +29,7 @@ public class RealmSyncopeOperations {
private final RealmService realmService = SyncopeServices.get(RealmService.class);
public List list() {
- return realmService.list();
+ return realmService.list(SyncopeConstants.ROOT_REALM);
}
public boolean exists(final String realm) {
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/report/ReportSyncopeOperations.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/report/ReportSyncopeOperations.java
index 17c6efad48d..da1d267f4ad 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/report/ReportSyncopeOperations.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/report/ReportSyncopeOperations.java
@@ -29,7 +29,7 @@
import org.apache.syncope.common.lib.to.JobTO;
import org.apache.syncope.common.lib.to.ReportTO;
import org.apache.syncope.common.lib.types.ReportExecExportFormat;
-import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
+import org.apache.syncope.common.rest.api.beans.ExecSpecs;
import org.apache.syncope.common.rest.api.service.ReportService;
public class ReportSyncopeOperations {
@@ -98,7 +98,7 @@ public String exportExecutionResult(final String executionKey, final String repo
}
public void execute(final String reportKey) {
- reportService.execute(new ExecuteQuery.Builder().key(reportKey).build());
+ reportService.execute(new ExecSpecs.Builder().key(reportKey).build());
}
public void deleteExecution(final String executionKey) {
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/self/AbstractWorkflowCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/self/AbstractWorkflowCommand.java
deleted file mode 100644
index b21abf39bcc..00000000000
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/self/AbstractWorkflowCommand.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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.
- */
-package org.apache.syncope.client.cli.commands.self;
-
-public abstract class AbstractWorkflowCommand {
-
- protected final WorkflowResultManager workflowResultManager = new WorkflowResultManager();
-
- protected final WorkflowSyncopeOperations workflowSyncopeOperations = new WorkflowSyncopeOperations();
-
-}
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/self/WorkflowCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/self/WorkflowCommand.java
deleted file mode 100644
index 215c8659132..00000000000
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/self/WorkflowCommand.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.
- */
-package org.apache.syncope.client.cli.commands.self;
-
-import java.util.ArrayList;
-import java.util.List;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.cli.Command;
-import org.apache.syncope.client.cli.Input;
-import org.apache.syncope.client.cli.commands.AbstractCommand;
-
-@Command(name = "workflow")
-public class WorkflowCommand extends AbstractCommand {
-
- private final WorkflowResultManager workflowResultManager = new WorkflowResultManager();
-
- @Override
- public void execute(final Input input) {
- if (StringUtils.isBlank(input.getOption())) {
- input.setOption(UserWorkflowOptions.HELP.getOptionName());
- }
-
- switch (UserWorkflowOptions.fromName(input.getOption())) {
- case EXPORT_DIAGRAM:
- new WorkflowExportDiagram(input).export();
- break;
- case EXPORT_DEFINITION:
- new WorkflowExportDefinition(input).export();
- break;
- case HELP:
- System.out.println(getHelpMessage());
- break;
- default:
- workflowResultManager.defaultOptionMessage(input.getOption(), getHelpMessage());
- }
- }
-
- @Override
- public String getHelpMessage() {
- return workflowResultManager.commandHelpMessage(getClass());
- }
-
- private enum UserWorkflowOptions {
-
- HELP("--help"),
- EXPORT_DIAGRAM("--export-diagram"),
- EXPORT_DEFINITION("--export-definition");
-
- private final String optionName;
-
- UserWorkflowOptions(final String optionName) {
- this.optionName = optionName;
- }
-
- public String getOptionName() {
- return optionName;
- }
-
- public boolean equalsOptionName(final String otherName) {
- return (otherName == null) ? false : optionName.equals(otherName);
- }
-
- public static UserWorkflowOptions fromName(final String name) {
- UserWorkflowOptions optionToReturn = HELP;
- for (final UserWorkflowOptions option : UserWorkflowOptions.values()) {
- if (option.equalsOptionName(name)) {
- optionToReturn = option;
- }
- }
- return optionToReturn;
- }
-
- public static List toList() {
- final List options = new ArrayList<>();
- for (final UserWorkflowOptions value : values()) {
- options.add(value.getOptionName());
- }
- return options;
- }
- }
-}
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/self/WorkflowExportDefinition.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/self/WorkflowExportDefinition.java
deleted file mode 100644
index b855ae0c861..00000000000
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/self/WorkflowExportDefinition.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.
- */
-package org.apache.syncope.client.cli.commands.self;
-
-import javax.xml.ws.WebServiceException;
-import org.apache.syncope.client.cli.Input;
-import org.apache.syncope.client.cli.util.CommandUtils;
-import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class WorkflowExportDefinition extends AbstractWorkflowCommand {
-
- private static final Logger LOG = LoggerFactory.getLogger(WorkflowExportDefinition.class);
-
- private static final String EXPORT_HELP_MESSAGE = "workflow --export-definition {ANY-TYPE-KIND}\n";
-
- private final Input input;
-
- public WorkflowExportDefinition(final Input input) {
- this.input = input;
- }
-
- public void export() {
- if (input.parameterNumber() == 1) {
- try {
- workflowSyncopeOperations.exportDefinition(input.firstParameter());
- } catch (final SyncopeClientException | WebServiceException ex) {
- LOG.error("Error", ex);
- if (ex.getMessage().startsWith("NotFound")) {
- workflowResultManager.notFoundError("Workflow", input.firstParameter());
- } else {
- workflowResultManager.genericError(ex.getMessage());
- }
- } catch (final IllegalArgumentException ex) {
- LOG.error("Error", ex);
- workflowResultManager.typeNotValidError(
- "workflow", input.firstParameter(), CommandUtils.fromEnumToArray(AnyTypeKind.class));
- }
- } else {
- workflowResultManager.commandOptionError(EXPORT_HELP_MESSAGE);
- }
- }
-}
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/self/WorkflowExportDiagram.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/self/WorkflowExportDiagram.java
deleted file mode 100644
index 80b6e417875..00000000000
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/self/WorkflowExportDiagram.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.
- */
-package org.apache.syncope.client.cli.commands.self;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import javax.ws.rs.core.Response;
-import javax.xml.ws.WebServiceException;
-import org.apache.cxf.helpers.IOUtils;
-import org.apache.syncope.client.cli.Input;
-import org.apache.syncope.client.cli.util.CommandUtils;
-import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
-
-public class WorkflowExportDiagram extends AbstractWorkflowCommand {
-
- private static final String EXPORT_HELP_MESSAGE = "workflow --export-diagram {ANY-TYPE-KIND}\n";
-
- private final Input input;
-
- public WorkflowExportDiagram(final Input input) {
- this.input = input;
- }
-
- public void export() {
- if (input.parameterNumber() == 1) {
- try {
- final Response response = workflowSyncopeOperations.exportDiagram(input.firstParameter());
- final byte[] diagram = IOUtils.readBytesFromStream((InputStream) response.getEntity());
- try (final OutputStream os = Files.newOutputStream(Paths.get("/tmp/diagram.png"))) {
- os.write(diagram);
- os.close();
- }
- workflowResultManager.genericMessage("Diagram created: /tmp/diagram.png");
- } catch (final SyncopeClientException | WebServiceException ex) {
- if (ex.getMessage().startsWith("NotFound")) {
- workflowResultManager.notFoundError("Workflow", input.firstParameter());
- } else {
- workflowResultManager.genericError(ex.getMessage());
- }
- } catch (final IllegalArgumentException ex) {
- workflowResultManager.typeNotValidError(
- "workflow", input.firstParameter(), CommandUtils.fromEnumToArray(AnyTypeKind.class));
- } catch (final IOException ex) {
- workflowResultManager.genericError(ex.getMessage());
- }
- } else {
- workflowResultManager.commandOptionError(EXPORT_HELP_MESSAGE);
- }
- }
-}
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/self/WorkflowResultManager.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/self/WorkflowResultManager.java
deleted file mode 100644
index f46be0786c9..00000000000
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/self/WorkflowResultManager.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.
- */
-package org.apache.syncope.client.cli.commands.self;
-
-import org.apache.syncope.client.cli.commands.CommonsResultManager;
-
-public class WorkflowResultManager extends CommonsResultManager {
-
-}
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/self/WorkflowSyncopeOperations.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/self/WorkflowSyncopeOperations.java
deleted file mode 100644
index 45025d4e9be..00000000000
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/self/WorkflowSyncopeOperations.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-package org.apache.syncope.client.cli.commands.self;
-
-import javax.ws.rs.core.Response;
-import org.apache.cxf.jaxrs.client.WebClient;
-import org.apache.syncope.client.cli.SyncopeServices;
-import org.apache.syncope.common.rest.api.RESTHeaders;
-import org.apache.syncope.common.rest.api.service.WorkflowService;
-
-public class WorkflowSyncopeOperations {
-
- private static final String DEFAULT = "userWorkflow";
-
- private final WorkflowService workflowService = SyncopeServices.get(WorkflowService.class);
-
- public Response exportDiagram(final String anyType) {
- WebClient.client(workflowService).accept(RESTHeaders.MEDIATYPE_IMAGE_PNG);
- return workflowService.exportDiagram(anyType, DEFAULT);
- }
-
- public Response exportDefinition(final String anyType) {
- return workflowService.get(anyType, DEFAULT);
- }
-}
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/task/TaskSyncopeOperations.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/task/TaskSyncopeOperations.java
index d577968292c..2314c044842 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/task/TaskSyncopeOperations.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/task/TaskSyncopeOperations.java
@@ -24,7 +24,7 @@
import org.apache.syncope.common.lib.to.ExecTO;
import org.apache.syncope.common.lib.to.JobTO;
import org.apache.syncope.common.lib.types.TaskType;
-import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
+import org.apache.syncope.common.rest.api.beans.ExecSpecs;
import org.apache.syncope.common.rest.api.beans.TaskQuery;
import org.apache.syncope.common.rest.api.service.TaskService;
@@ -58,6 +58,6 @@ public void deleteExecution(final String executionKey) {
public ExecTO execute(final String executionKey, final boolean dryRun) {
return taskService.execute(
- new ExecuteQuery.Builder().key(executionKey).dryRun(dryRun).build());
+ new ExecSpecs.Builder().key(executionKey).dryRun(dryRun).build());
}
}
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserDeleteAll.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserDeleteAll.java
index 30d09530a57..e54812dbe21 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserDeleteAll.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserDeleteAll.java
@@ -18,12 +18,20 @@
*/
package org.apache.syncope.client.cli.commands.user;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Scanner;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.ws.rs.core.Response;
import org.apache.syncope.client.cli.Input;
-import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.to.ProvisioningResult;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.batch.BatchResponseItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,6 +39,8 @@ public class UserDeleteAll extends AbstractUserCommand {
private static final Logger LOG = LoggerFactory.getLogger(UserDeleteAll.class);
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
private static final String DELETE_ALL_HELP_MESSAGE = "user --delete-all {REALM}";
private final Input input;
@@ -59,21 +69,31 @@ public void delete() {
userResultManager.notFoundError("Realm", realm);
return;
}
- final Map results = userSyncopeOperations.deleteAll(realm);
- final Map users = new HashMap<>();
- int deletedUsers = 0;
- for (final Map.Entry entrySet : results.entrySet()) {
- final String userId = entrySet.getKey();
- final BulkActionResult.Status status = entrySet.getValue();
- if (!BulkActionResult.Status.SUCCESS.equals(status)) {
- users.put(userId, status.name());
+ List results = userSyncopeOperations.deleteAll(realm);
+
+ Map failedUsers = new HashMap<>();
+ AtomicReference deletedUsers = new AtomicReference<>(0);
+
+ results.forEach(item -> {
+ if (item.getStatus() == Response.Status.OK.getStatusCode()) {
+ deletedUsers.getAndSet(deletedUsers.get() + 1);
} else {
- deletedUsers++;
+ try {
+ ProvisioningResult user = MAPPER.readValue(item.getContent(),
+ new TypeReference>() {
+ });
+ failedUsers.put(
+ user.getEntity().getUsername(),
+ item.getHeaders().get(RESTHeaders.ERROR_CODE).toString());
+ } catch (IOException ioe) {
+ LOG.error("Error reading {}", item.getContent(), ioe);
+ }
}
- }
+ });
+
userResultManager.genericMessage("Deleted users: " + deletedUsers);
- if (!users.isEmpty()) {
- userResultManager.printFailedUsers(users);
+ if (!failedUsers.isEmpty()) {
+ userResultManager.printFailedUsers(failedUsers);
}
} else {
userResultManager.genericError("Authentication error");
@@ -83,9 +103,9 @@ public void delete() {
} else {
userResultManager.genericError("Invalid parameter, please use [yes/no]");
}
- } catch (final SyncopeClientException ex) {
- LOG.error("Error deleting user", ex);
- userResultManager.genericError(ex.getMessage());
+ } catch (Exception e) {
+ LOG.error("Error deleting user", e);
+ userResultManager.genericError(e.getMessage());
}
} else {
userResultManager.commandOptionError(DELETE_ALL_HELP_MESSAGE);
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserDeleteByAttribute.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserDeleteByAttribute.java
index 3759656781a..f5e735e3185 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserDeleteByAttribute.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserDeleteByAttribute.java
@@ -18,13 +18,20 @@
*/
package org.apache.syncope.client.cli.commands.user;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
-import javax.xml.ws.WebServiceException;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.ws.rs.core.Response;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.client.cli.Input;
-import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.to.ProvisioningResult;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.batch.BatchResponseItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,6 +39,8 @@ public class UserDeleteByAttribute extends AbstractUserCommand {
private static final Logger LOG = LoggerFactory.getLogger(UserDeleteByAttribute.class);
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
private static final String SEARCH_HELP_MESSAGE = "user --delete-by-attribute {REALM} {ATTR-NAME}={ATTR-VALUE}";
private final Input input;
@@ -49,34 +58,40 @@ public void delete() {
userResultManager.notFoundError("Realm", realm);
return;
}
- Map results = userSyncopeOperations.deleteByAttribute(
+ List results = userSyncopeOperations.deleteByAttribute(
realm, pairParameter.getKey(), pairParameter.getValue());
+
Map failedUsers = new HashMap<>();
- int deletedUsers = 0;
- for (final Map.Entry entrySet : results.entrySet()) {
- String userId = entrySet.getKey();
- BulkActionResult.Status status = entrySet.getValue();
- if (BulkActionResult.Status.SUCCESS == status) {
- deletedUsers++;
+ AtomicReference deletedUsers = new AtomicReference<>(0);
+
+ results.forEach(item -> {
+ if (item.getStatus() == Response.Status.OK.getStatusCode()) {
+ deletedUsers.getAndSet(deletedUsers.get() + 1);
} else {
- failedUsers.put(userId, status.name());
+ try {
+ ProvisioningResult user = MAPPER.readValue(item.getContent(),
+ new TypeReference>() {
+ });
+ failedUsers.put(
+ user.getEntity().getUsername(),
+ item.getHeaders().get(RESTHeaders.ERROR_CODE).toString());
+ } catch (IOException ioe) {
+ LOG.error("Error reading {}", item.getContent(), ioe);
+ }
}
- }
+ });
+
userResultManager.genericMessage("Deleted users: " + deletedUsers);
if (!failedUsers.isEmpty()) {
userResultManager.printFailedUsers(failedUsers);
}
- } catch (WebServiceException | SyncopeClientException ex) {
- LOG.error("Error searching user", ex);
- if (ex.getMessage().startsWith("NotFound")) {
+ } catch (Exception e) {
+ LOG.error("Error searching user", e);
+ if (e.getMessage().startsWith("NotFound")) {
userResultManager.notFoundError("User with " + pairParameter.getKey(), pairParameter.getValue());
} else {
- userResultManager.genericError(ex.getMessage());
+ userResultManager.genericError(e.getMessage());
}
- } catch (IllegalArgumentException ex) {
- LOG.error("Error searching user", ex);
- userResultManager.genericError(ex.getMessage());
- userResultManager.genericError(SEARCH_HELP_MESSAGE);
}
} else {
userResultManager.commandOptionError(SEARCH_HELP_MESSAGE);
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserResultManager.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserResultManager.java
index 450ff7e8296..9c235751f89 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserResultManager.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserResultManager.java
@@ -32,9 +32,9 @@ public class UserResultManager extends CommonsResultManager {
public void printUsers(final List userTOs) {
System.out.println("");
- for (final UserTO userTO : userTOs) {
+ userTOs.forEach(userTO -> {
printUser(userTO);
- }
+ });
}
private void printUser(final UserTO userTO) {
@@ -69,40 +69,40 @@ private void printUser(final UserTO userTO) {
}
private void printResource(final Set resources) {
- for (final String resource : resources) {
+ resources.forEach(resource -> {
System.out.println(" - " + resource);
- }
+ });
}
private void printRole(final List roles) {
- for (final String role : roles) {
+ roles.forEach((role) -> {
System.out.println(" - " + role);
- }
+ });
}
private void printAttributes(final Set derAttrTOs) {
- for (final AttrTO attrTO : derAttrTOs) {
+ derAttrTOs.forEach(attrTO -> {
final StringBuilder attributeSentence = new StringBuilder();
attributeSentence.append(" ")
.append(attrTO.getSchema())
.append(": ")
.append(attrTO.getValues());
System.out.println(attributeSentence);
- }
+ });
}
private void printRelationships(final List relationshipTOs) {
- for (final RelationshipTO relationshipTO : relationshipTOs) {
+ relationshipTOs.forEach(relationshipTO -> {
System.out.println(" type: " + relationshipTO.getType());
- }
+ });
}
public void printFailedUsers(final Map users) {
- final Table.TableBuilder tableBuilder =
+ Table.TableBuilder tableBuilder =
new Table.TableBuilder("Users not deleted").header("user key").header("cause");
- for (final Map.Entry entrySet : users.entrySet()) {
- tableBuilder.rowValues(Arrays.asList(entrySet.getKey(), entrySet.getValue()));
- }
+ users.forEach((key, value) -> {
+ tableBuilder.rowValues(Arrays.asList(key, value));
+ });
tableBuilder.build().print();
}
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSyncopeOperations.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSyncopeOperations.java
index 42fb5a318a2..2c712ba28e1 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSyncopeOperations.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/user/UserSyncopeOperations.java
@@ -18,16 +18,17 @@
*/
package org.apache.syncope.client.cli.commands.user;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
+import javax.ws.rs.core.Response;
import org.apache.syncope.client.cli.SyncopeServices;
import org.apache.syncope.client.lib.SyncopeClient;
-import org.apache.syncope.common.lib.to.BulkAction;
-import org.apache.syncope.common.lib.to.BulkActionResult;
-import org.apache.syncope.common.lib.to.EntityTO;
+import org.apache.syncope.client.lib.batch.BatchRequest;
import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.rest.api.batch.BatchPayloadParser;
+import org.apache.syncope.common.rest.api.batch.BatchResponseItem;
import org.apache.syncope.common.rest.api.beans.AnyQuery;
import org.apache.syncope.common.rest.api.service.UserService;
@@ -91,34 +92,35 @@ public void delete(final String userKey) {
userService.delete(userKey);
}
- public Map deleteByAttribute(
- final String realm, final String attributeName, final String attributeValue) {
+ public List deleteByAttribute(
+ final String realm, final String attributeName, final String attributeValue) throws IOException {
- return bulkDelete(new AnyQuery.Builder().realm(realm).fiql(
+ return batchDelete(new AnyQuery.Builder().realm(realm).fiql(
SyncopeClient.getUserSearchConditionBuilder().is(attributeName).equalTo(attributeValue).query()).
build());
}
- public Map deleteAll(final String realm) {
- return bulkDelete(new AnyQuery.Builder().realm(realm).details(false).build());
+ public List deleteAll(final String realm) throws IOException {
+ return batchDelete(new AnyQuery.Builder().realm(realm).details(false).build());
}
- private Map bulkDelete(final AnyQuery query) {
+ private List batchDelete(final AnyQuery query) throws IOException {
query.setPage(0);
query.setSize(0);
int count = userService.search(query).getTotalCount();
- BulkAction bulkAction = new BulkAction();
- bulkAction.setType(BulkAction.Type.DELETE);
+ BatchRequest batchRequest = SyncopeServices.batch();
+ UserService batchUserService = batchRequest.getService(UserService.class);
query.setSize(PAGE_SIZE);
for (int page = 1; page <= (count / PAGE_SIZE) + 1; page++) {
query.setPage(page);
- bulkAction.getTargets().addAll(userService.search(query).getResult().stream().
- map(EntityTO::getKey).collect(Collectors.toList()));
+ userService.search(query).getResult().forEach(user -> batchUserService.delete(user.getKey()));
}
- return userService.bulk(bulkAction).readEntity(BulkActionResult.class).getResults();
+ Response response = batchRequest.commit().getResponse();
+ return BatchPayloadParser.parse(
+ (InputStream) response.getEntity(), response.getMediaType(), new BatchResponseItem());
}
}
diff --git a/client/cli/src/main/resources/messages.properties b/client/cli/src/main/resources/messages.properties
index 48cdc159696..7bd62fa2766 100644
--- a/client/cli/src/main/resources/messages.properties
+++ b/client/cli/src/main/resources/messages.properties
@@ -31,5 +31,4 @@ role.help.message=\nUsage: role [options]\n Options:\n --help \n --detail
schema.help.message=\nUsage: schema [options]\n Options:\n --help \n --details \n --list-all\n --list-plain\n --list-derived\n --list-virtual\n --read {SCHEMA-TYPE} {SCHEMA-KEY}\n Schema type: PLAIN / DERIVED / VIRTUAL\n --delete {SCHEMA-TYPE} {SCHEMA-KEY}\n Schema type: PLAIN / DERIVED / VIRTUAL\n
task.help.message=\nUsage: task [options]\n Options:\n --help \n --details\n --list\n Syntax: --list {TASK-TYPE} \n Task type: NOTIFICATION / PROPAGATION / PUSH / SCHEDULED / PULL\n --list-running-jobs \n --list-scheduled-jobs \n --read \n Syntax: --read {TASK-TYPE} {TASK-KEY}\n Task type: PROPAGATION / NOTIFICATION / SCHEDULED / PULL / PUSH\n --delete \n Syntax: --delete {TASK-KEY} {TASK-KEY} [...]\n --delete-all-prop\n --delete-execution \n Syntax: --delete-execution {TASK-EXEC-KEY} {TASK-EXEC-KEY} [...]\n --execute \n Syntax: --execute {TASK-KEY} {DRY-RUN}\n Dry run: true / false\n
user.help.message=\nUsage: user [options]\n Options:\n --help \n --details \n --list \n --get-user-key\n Syntax: --get-user-key {USERNAME}\n --get-username\n Syntax: --get-username {USER-KEY}\n --read-by-userkey \n Syntax: --read-by-userkey {USER-KEY} {USER-KEY} [...]\n --read-by-username\n Syntax: --read-by-username {USERNAME} {USERNAME} [...]\n --search-by-attribute \n Syntax: --search-by-attribute {REALM} {ATTR-NAME}={ATTR-VALUE}\n --search-by-role \n Syntax: --search-by-role {REALM} {ROLE-KEY}\n --search-by-resource \n Syntax: --search-by-resource {REALM} {RESOURCE-KEY}\n --delete \n Syntax: --delete {USER-KEY} {USER-KEY} [...]\n --delete-all \n Syntax: --delete-all {REALM}\n --delete-by-attribute \n Syntax: --delete-by-attribute {REALM} {ATTR-NAME}={ATTR-VALUE}\n
-workflow.help.message=\nUsage: workflow [options]\n Options:\n --help \n --export-diagram {ANY-TYPE-KIND}\n Any type kind: ANY_OBJECT / USER / GROUP\n --export-definition {ANY-TYPE-KIND}\n Any type kind: ANY_OBJECT / USER / GROUP\n
migrate.help.message=\nUsage: migrate [options]\n Options:\n --help \n --conf {SRC} {DST}\n Syncope 1.2.X content.xml SRC absolute path\n Syncope 2.0.X *Content.xml DST absolute path\n
diff --git a/client/cli/src/test/java/org/apache/syncope/client/cli/util/CommandUtilsTest.java b/client/cli/src/test/java/org/apache/syncope/client/cli/util/CommandUtilsTest.java
index 2b3c2ae9e3f..c75273875b0 100644
--- a/client/cli/src/test/java/org/apache/syncope/client/cli/util/CommandUtilsTest.java
+++ b/client/cli/src/test/java/org/apache/syncope/client/cli/util/CommandUtilsTest.java
@@ -49,6 +49,6 @@ public void fromArgs() throws Exception {
public void commands() throws Exception {
List commands = CommandUtils.commands();
assertFalse(commands.isEmpty());
- assertEquals(21, commands.size());
+ assertEquals(20, commands.size());
}
}
diff --git a/client/console/NOTICE b/client/console/NOTICE
index 527bc386648..a9a10ab5afa 100644
--- a/client/console/NOTICE
+++ b/client/console/NOTICE
@@ -1,5 +1,5 @@
Apache Syncope
-Copyright 2012-2018 The Apache Software Foundation
+Copyright 2012-2023 The Apache Software Foundation
This product includes software developed by:
The Apache Software Foundation (http://www.apache.org/).
diff --git a/client/console/pom.xml b/client/console/pom.xml
index 5ba1538de61..94e50102cfc 100644
--- a/client/console/pom.xml
+++ b/client/console/pom.xml
@@ -24,7 +24,7 @@ under the License.
org.apache.syncope
syncope-client
- 2.1.1-SNAPSHOT
+ 2.1.15-SNAPSHOT
Apache Syncope Client Console
@@ -58,10 +58,6 @@ under the License.
org.apache.wicket
wicket-extensions
-
- org.wicketstuff
- wicket-datetime
-
org.apache.wicket
wicket-auth-roles
@@ -70,6 +66,10 @@ under the License.
com.googlecode.wicket-jquery-ui
wicket-jquery-ui
+
+ com.googlecode.wicket-jquery-ui
+ wicket-jquery-ui-theme-base
+
com.googlecode.wicket-jquery-ui
wicket-kendo-ui
@@ -94,7 +94,11 @@ under the License.
de.agilecoders.wicket
wicket-bootstrap-extensions
-
+
+ com.google.code.gson
+ gson
+
+
org.apache.syncope.client
syncope-client-lib
@@ -156,6 +160,33 @@ under the License.
org.apache.logging.log4j
log4j-core
+
+ org.apache.logging.log4j
+ log4j-slf4j-impl
+
+
+ com.lmax
+ disruptor
+
+
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/PreferenceManager.java b/client/console/src/main/java/org/apache/syncope/client/console/PreferenceManager.java
index 96374a1aca3..f5addfce7ad 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/PreferenceManager.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/PreferenceManager.java
@@ -29,6 +29,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.wicket.request.Request;
@@ -135,9 +136,7 @@ public void set(final Request request, final Response response, final Map {
- current.put(key, StringUtils.join(values, ";"));
- });
+ prefs.forEach((key, values) -> current.put(key, values.stream().collect(Collectors.joining(";"))));
try {
COOKIE_UTILS.save(COOKIE_NAME, Base64.getEncoder().encodeToString(setPrefs(current).getBytes()));
@@ -165,7 +164,7 @@ public void set(final Request request, final Response response, final String key
}
public void setList(final Request request, final Response response, final String key, final List values) {
- set(request, response, key, StringUtils.join(values, ";"));
+ set(request, response, key, values.stream().collect(Collectors.joining(";")));
}
public void setList(final Request request, final Response response, final Map> prefs) {
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java b/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java
index 288c130d1c0..15e96d5f143 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.client.console;
+import com.google.common.net.HttpHeaders;
import de.agilecoders.wicket.core.Bootstrap;
import de.agilecoders.wicket.core.settings.BootstrapSettings;
import de.agilecoders.wicket.core.settings.IBootstrapSettings;
@@ -35,6 +36,7 @@
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.annotations.Resource;
import org.apache.syncope.client.console.commons.Constants;
import org.apache.syncope.client.console.init.ClassPathScanImplementationLookup;
import org.apache.syncope.client.console.init.ConsoleInitializer;
@@ -42,16 +44,14 @@
import org.apache.syncope.client.console.pages.Dashboard;
import org.apache.syncope.client.console.pages.MustChangePassword;
import org.apache.syncope.client.console.pages.Login;
-import org.apache.syncope.client.console.resources.FilesystemResource;
-import org.apache.syncope.client.console.resources.WorkflowDefGETResource;
-import org.apache.syncope.client.console.resources.WorkflowDefPUTResource;
+import org.apache.syncope.client.console.panels.AnyPanel;
import org.apache.syncope.client.console.themes.AdminLTE;
+import org.apache.syncope.client.console.topology.Topology;
import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
import org.apache.syncope.common.lib.PropertyUtils;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.to.EntityTO;
-import org.apache.syncope.common.lib.types.StandardEntitlement;
import org.apache.syncope.common.rest.api.service.DomainService;
import org.apache.wicket.Page;
import org.apache.wicket.authroles.authentication.AbstractAuthenticatedWebSession;
@@ -59,11 +59,17 @@
import org.apache.wicket.authroles.authentication.AuthenticatedWebSession;
import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
import org.apache.wicket.markup.html.WebPage;
-import org.apache.wicket.protocol.http.CsrfPreventionRequestCycleListener;
import org.apache.wicket.protocol.http.WebApplication;
+import org.apache.wicket.protocol.http.servlet.XForwardedRequestWrapperFactory;
+import org.apache.wicket.protocol.ws.WebSocketAwareCsrfPreventionRequestCycleListener;
+import org.apache.wicket.request.cycle.IRequestCycleListener;
+import org.apache.wicket.protocol.ws.api.WebSocketResponse;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.http.WebResponse;
+import org.apache.wicket.request.resource.AbstractResource;
import org.apache.wicket.request.resource.IResource;
import org.apache.wicket.request.resource.ResourceReference;
-import org.apache.wicket.resource.DynamicJQueryResourceReference;
+import org.apache.wicket.resource.JQueryResourceReference;
import org.apache.wicket.util.lang.Args;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -75,24 +81,18 @@ public class SyncopeConsoleApplication extends AuthenticatedWebApplication {
private static final String CONSOLE_PROPERTIES = "console.properties";
public static final List SUPPORTED_LOCALES = Collections.unmodifiableList(Arrays.asList(
- new Locale[] {
- Locale.ENGLISH, Locale.ITALIAN, new Locale("pt", "BR"), new Locale("ru"), Locale.JAPANESE
- }));
-
- private static final String FLOWABLE_MODELER_CONTEXT = "flowable-modeler";
+ Locale.ENGLISH, Locale.CANADA_FRENCH, Locale.ITALIAN, Locale.JAPANESE, new Locale("pt", "BR"),
+ new Locale("ru")
+ ));
public static SyncopeConsoleApplication get() {
return (SyncopeConsoleApplication) WebApplication.get();
}
- private String site;
-
private String anonymousUser;
private String anonymousKey;
- private String flowableModelerDirectory;
-
private String reconciliationReportKey;
private String scheme;
@@ -109,31 +109,52 @@ public static SyncopeConsoleApplication get() {
private Integer maxWaitTime;
+ private Integer corePoolSize;
+
+ private Integer maxPoolSize;
+
+ private Integer queueCapacity;
+
private List domains;
private Map> pageClasses;
+ private String defaultAnyLayoutClass;
+
+ private String defaultTopologyClass;
+
@SuppressWarnings("unchecked")
protected void populatePageClasses(final Properties props) {
Enumeration propNames = (Enumeration) props.propertyNames();
while (propNames.hasMoreElements()) {
- String name = propNames.nextElement();
- if (name.startsWith("page.")) {
+ String className = propNames.nextElement();
+ if (className.startsWith("page.")) {
try {
- Class> clazz = ClassUtils.getClass(props.getProperty(name));
+ Class> clazz = ClassUtils.getClass(props.getProperty(className));
if (BasePage.class.isAssignableFrom(clazz)) {
pageClasses.put(
- StringUtils.substringAfter("page.", name), (Class extends BasePage>) clazz);
+ StringUtils.substringAfter(className, "page."), (Class extends BasePage>) clazz);
} else {
LOG.warn("{} does not extend {}, ignoring...", clazz.getName(), BasePage.class.getName());
}
} catch (ClassNotFoundException e) {
- LOG.error("While looking for class identified by property '{}'", name, e);
+ LOG.error("While looking for class identified by property '{}'", className, e);
}
}
}
}
+ protected void setSecurityHeaders(final Properties props, final WebResponse response) {
+ @SuppressWarnings("unchecked")
+ Enumeration propNames = (Enumeration) props.propertyNames();
+ while (propNames.hasMoreElements()) {
+ String name = propNames.nextElement();
+ if (name.startsWith("security.headers.")) {
+ response.setHeader(StringUtils.substringAfter(name, "security.headers."), props.getProperty(name));
+ }
+ }
+ }
+
@Override
protected void init() {
super.init();
@@ -141,8 +162,6 @@ protected void init() {
// read console.properties
Properties props = PropertyUtils.read(getClass(), CONSOLE_PROPERTIES, "console.directory").getLeft();
- site = props.getProperty("site");
- Args.notNull(site, "");
anonymousUser = props.getProperty("anonymousUser");
Args.notNull(anonymousUser, "");
anonymousKey = props.getProperty("anonymousKey");
@@ -164,13 +183,21 @@ protected void init() {
maxWaitTime = Integer.valueOf(props.getProperty("maxWaitTimeOnApplyChanges", "30"));
- String csrf = props.getProperty("csrf");
+ // Resource connections check thread pool size
+ corePoolSize = Integer.valueOf(props.getProperty("topology.corePoolSize", "5"));
+ maxPoolSize = Integer.valueOf(props.getProperty("topology.maxPoolSize", "10"));
+ queueCapacity = Integer.valueOf(props.getProperty("topology.queueCapacity", "50"));
+ // topology page
+ defaultTopologyClass = props.getProperty("page.topology", Topology.class.getName());
+
// process page properties
pageClasses = new HashMap<>();
populatePageClasses(props);
pageClasses = Collections.unmodifiableMap(pageClasses);
+ defaultAnyLayoutClass = props.getProperty("default.any.panel.class", AnyPanel.class.getName());
+
// Application settings
IBootstrapSettings settings = new BootstrapSettings();
@@ -181,10 +208,10 @@ protected void init() {
Bootstrap.install(this, settings);
getResourceSettings().setUseMinifiedResources(true);
+ getResourceSettings().setUseDefaultOnMissingResource(true);
+ getResourceSettings().setThrowExceptionOnMissingResource(false);
- getResourceSettings().setThrowExceptionOnMissingResource(true);
-
- getJavaScriptLibrarySettings().setJQueryReference(new DynamicJQueryResourceReference());
+ getJavaScriptLibrarySettings().setJQueryReference(JQueryResourceReference.getV2());
getSecuritySettings().setAuthorizationStrategy(new MetaDataRoleAuthorizationStrategy(this));
@@ -196,15 +223,32 @@ protected void init() {
getMarkupSettings().setStripWicketTags(true);
getMarkupSettings().setCompressWhitespace(true);
- if (BooleanUtils.toBoolean(csrf)) {
- getRequestCycleListeners().add(new CsrfPreventionRequestCycleListener());
+ if (BooleanUtils.toBoolean(props.getProperty("x-forward"))) {
+ XForwardedRequestWrapperFactory.Config config = new XForwardedRequestWrapperFactory.Config();
+ config.setProtocolHeader(props.getProperty("x-forward.protocol.header", HttpHeaders.X_FORWARDED_PROTO));
+ config.setHttpServerPort(Integer.valueOf(props.getProperty("x-forward.http.port", "80")));
+ config.setHttpsServerPort(Integer.valueOf(props.getProperty("x-forward.https.port", "443")));
+
+ XForwardedRequestWrapperFactory factory = new XForwardedRequestWrapperFactory();
+ factory.setConfig(config);
+ getFilterFactoryManager().add(factory);
+ }
+
+ if (BooleanUtils.toBoolean(props.getProperty("csrf"))) {
+ getRequestCycleListeners().add(new WebSocketAwareCsrfPreventionRequestCycleListener());
}
getRequestCycleListeners().add(new SyncopeConsoleRequestCycleListener());
+ getRequestCycleListeners().add(new IRequestCycleListener() {
- mountPage("/login", getSignInPageClass());
+ @Override
+ public void onEndRequest(final RequestCycle cycle) {
+ if (cycle.getResponse() instanceof WebResponse && !(cycle.getResponse() instanceof WebSocketResponse)) {
+ setSecurityHeaders(props, (WebResponse) cycle.getResponse());
+ }
+ }
+ });
- flowableModelerDirectory = props.getProperty("flowableModelerDirectory");
- Args.notNull(flowableModelerDirectory, "");
+ mountPage("/login", getSignInPageClass());
try {
reconciliationReportKey = props.getProperty("reconciliationReportKey");
@@ -213,34 +257,24 @@ protected void init() {
}
Args.notNull(reconciliationReportKey, "");
- mountResource("/" + FLOWABLE_MODELER_CONTEXT, new ResourceReference(FLOWABLE_MODELER_CONTEXT) {
+ for (Class extends AbstractResource> resource : lookup.getResources()) {
+ Resource annotation = resource.getAnnotation(Resource.class);
+ try {
+ AbstractResource instance = resource.getDeclaredConstructor().newInstance();
- private static final long serialVersionUID = -128426276529456602L;
+ mountResource(annotation.path(), new ResourceReference(annotation.key()) {
- @Override
- public IResource getResource() {
- return new FilesystemResource(FLOWABLE_MODELER_CONTEXT, flowableModelerDirectory);
- }
-
- });
- mountResource("/workflowDefGET", new ResourceReference("workflowDefGET") {
-
- private static final long serialVersionUID = -128426276529456602L;
-
- @Override
- public IResource getResource() {
- return new WorkflowDefGETResource();
- }
- });
- mountResource("/workflowDefPUT", new ResourceReference("workflowDefPUT") {
-
- private static final long serialVersionUID = -128426276529456602L;
+ private static final long serialVersionUID = -128426276529456602L;
- @Override
- public IResource getResource() {
- return new WorkflowDefPUTResource();
+ @Override
+ public IResource getResource() {
+ return instance;
+ }
+ });
+ } catch (Exception e) {
+ LOG.error("Could not instantiate {}", resource.getName(), e);
}
- });
+ }
// enable component path
if (getDebugSettings().isAjaxDebugModeEnabled()) {
@@ -261,7 +295,7 @@ protected Class extends WebPage> getSignInPageClass() {
@Override
public Class extends Page> getHomePage() {
return AuthenticatedWebSession.get().isSignedIn()
- && SyncopeConsoleSession.get().owns(StandardEntitlement.MUST_CHANGE_PASSWORD)
+ && SyncopeConsoleSession.get().getSelfTO().isMustChangePassword()
? MustChangePassword.class
: Dashboard.class;
}
@@ -270,10 +304,6 @@ public Class extends BasePage> getPageClass(final String key) {
return pageClasses.get(key);
}
- public String getSite() {
- return site;
- }
-
public String getAnonymousUser() {
return anonymousUser;
}
@@ -282,10 +312,6 @@ public String getAnonymousKey() {
return anonymousKey;
}
- public String getFlowableModelerDirectory() {
- return flowableModelerDirectory;
- }
-
public String getReconciliationReportKey() {
return reconciliationReportKey;
}
@@ -298,9 +324,29 @@ public Integer getMaxWaitTimeInSeconds() {
return maxWaitTime;
}
+ public Integer getCorePoolSize() {
+ return corePoolSize;
+ }
+
+ public Integer getMaxPoolSize() {
+ return maxPoolSize;
+ }
+
+ public Integer getQueueCapacity() {
+ return queueCapacity;
+ }
+
+ public String getDefaultAnyLayoutClass() {
+ return defaultAnyLayoutClass;
+ }
+
+ public String getDefaultTopologyClass() {
+ return defaultTopologyClass;
+ }
+
public SyncopeClientFactoryBean newClientFactory() {
return new SyncopeClientFactoryBean().
- setAddress(scheme + "://" + host + ":" + port + "/" + rootPath).
+ setAddress(scheme + "://" + host + ":" + port + StringUtils.prependIfMissing(rootPath, "/")).
setUseCompression(BooleanUtils.toBoolean(useGZIPCompression));
}
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleRequestCycleListener.java b/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleRequestCycleListener.java
index 5d18bda7f55..19302a5fd7b 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleRequestCycleListener.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleRequestCycleListener.java
@@ -22,6 +22,7 @@
import javax.ws.rs.BadRequestException;
import javax.ws.rs.ForbiddenException;
import javax.xml.ws.WebServiceException;
+import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.console.pages.Login;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.wicket.authorization.UnauthorizedInstantiationException;
@@ -41,14 +42,6 @@ public class SyncopeConsoleRequestCycleListener implements IRequestCycleListener
private static final Logger LOG = LoggerFactory.getLogger(SyncopeConsoleRequestCycleListener.class);
- private static final String PAGE_EXPIRED = "Session expired: please login again";
-
- private static final String MISSING_AUTHORIZATION = "Missing authorization";
-
- private static final String MISSING_AUTHORIZATION_CORE = "Missing authorization while contacting Syncope core";
-
- private static final String REST = "Error while contacting Syncope core";
-
private Throwable instanceOf(final Exception e, final Class extends Exception> clazz) {
return clazz.isAssignableFrom(e.getClass())
? e
@@ -68,23 +61,23 @@ public IRequestHandler onException(final RequestCycle cycle, final Exception e)
IRequestablePage errorPage;
if (instanceOf(e, UnauthorizedInstantiationException.class) != null) {
- errorParameters.add("errorMessage", MISSING_AUTHORIZATION);
+ errorParameters.add("errorMessage", SyncopeConsoleSession.Error.AUTHORIZATION.fallback());
errorPage = new Login(errorParameters);
} else if (instanceOf(e, AccessControlException.class) != null) {
- if (instanceOf(e, AccessControlException.class).getMessage().contains("expired")) {
- errorParameters.add("errorMessage", PAGE_EXPIRED);
+ if (StringUtils.containsIgnoreCase(instanceOf(e, AccessControlException.class).getMessage(), "expired")) {
+ errorParameters.add("errorMessage", SyncopeConsoleSession.Error.SESSION_EXPIRED.fallback());
} else {
- errorParameters.add("errorMessage", MISSING_AUTHORIZATION_CORE);
+ errorParameters.add("errorMessage", SyncopeConsoleSession.Error.AUTHORIZATION.fallback());
}
errorPage = new Login(errorParameters);
} else if (instanceOf(e, PageExpiredException.class) != null || !SyncopeConsoleSession.get().isSignedIn()) {
- errorParameters.add("errorMessage", PAGE_EXPIRED);
+ errorParameters.add("errorMessage", SyncopeConsoleSession.Error.SESSION_EXPIRED.fallback());
errorPage = new Login(errorParameters);
} else if (instanceOf(e, BadRequestException.class) != null
|| instanceOf(e, WebServiceException.class) != null
|| instanceOf(e, SyncopeClientException.class) != null) {
- errorParameters.add("errorMessage", REST);
+ errorParameters.add("errorMessage", SyncopeConsoleSession.Error.REST.fallback());
errorPage = new Login(errorParameters);
} else {
Throwable cause = instanceOf(e, ForbiddenException.class);
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java b/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java
index 568148cc977..0b6878ef368 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java
@@ -18,31 +18,43 @@
*/
package org.apache.syncope.client.console;
+import java.security.AccessControlException;
import java.text.DateFormat;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
+import java.util.stream.Collectors;
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.ForbiddenException;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.MediaType;
-import org.apache.commons.collections4.list.SetUniqueList;
+import javax.xml.ws.WebServiceException;
+import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.time.FastDateFormat;
-import org.apache.commons.lang3.tuple.Pair;
+import org.apache.commons.lang3.tuple.Triple;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.RealmsUtils;
import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
+import org.apache.syncope.client.lib.batch.BatchRequest;
+import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.info.PlatformInfo;
import org.apache.syncope.common.lib.info.SystemInfo;
import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
import org.apache.syncope.common.rest.api.service.SyncopeService;
import org.apache.wicket.Session;
import org.apache.wicket.authroles.authentication.AuthenticatedWebSession;
@@ -50,35 +62,64 @@
import org.apache.wicket.request.Request;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.core.task.TaskRejectedException;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.util.CollectionUtils;
public class SyncopeConsoleSession extends AuthenticatedWebSession {
private static final long serialVersionUID = 747562246415852166L;
- private static final Logger LOG = LoggerFactory.getLogger(SyncopeConsoleSession.class);
+ public enum Error {
+ SESSION_EXPIRED("error.session.expired", "Session expired: please login again"),
+ AUTHORIZATION("error.authorization", "Insufficient access rights when performing the requested operation"),
+ REST("error.rest", "There was an error while contacting the Core server");
- private final SyncopeClientFactoryBean clientFactory;
+ private final String key;
- private final SyncopeClient anonymousClient;
+ private final String fallback;
- private final PlatformInfo platformInfo;
+ Error(final String key, final String fallback) {
+ this.key = key;
+ this.fallback = fallback;
+ }
+
+ public String key() {
+ return key;
+ }
+
+ public String fallback() {
+ return fallback;
+ }
+ }
+
+ protected static final Logger LOG = LoggerFactory.getLogger(SyncopeConsoleSession.class);
+
+ protected final SyncopeClientFactoryBean clientFactory;
+
+ protected final SyncopeClient anonymousClient;
+
+ protected final PlatformInfo platformInfo;
+
+ protected final SystemInfo systemInfo;
+
+ protected final Map, Object> services = Collections.synchronizedMap(new HashMap<>());
- private final SystemInfo systemInfo;
+ protected final ThreadPoolTaskExecutor executor;
- private final Map, Object> services = Collections.synchronizedMap(new HashMap<>());
+ protected String domain;
- private final ThreadPoolTaskExecutor executor;
+ protected SyncopeClient client;
- private String domain;
+ protected UserTO selfTO;
- private SyncopeClient client;
+ protected Map> auth;
- private UserTO selfTO;
+ protected List delegations;
- private Map> auth;
+ protected String delegatedBy;
- private Roles roles;
+ protected Roles roles;
public static SyncopeConsoleSession get() {
return (SyncopeConsoleSession) Session.get();
@@ -88,22 +129,58 @@ public SyncopeConsoleSession(final Request request) {
super(request);
clientFactory = SyncopeConsoleApplication.get().newClientFactory();
- anonymousClient = clientFactory.
- create(new AnonymousAuthenticationHandler(
- SyncopeConsoleApplication.get().getAnonymousUser(),
- SyncopeConsoleApplication.get().getAnonymousKey()));
+ anonymousClient = clientFactory.create(new AnonymousAuthenticationHandler(
+ SyncopeConsoleApplication.get().getAnonymousUser(),
+ SyncopeConsoleApplication.get().getAnonymousKey()));
platformInfo = anonymousClient.getService(SyncopeService.class).platform();
systemInfo = anonymousClient.getService(SyncopeService.class).system();
executor = new ThreadPoolTaskExecutor();
executor.setWaitForTasksToCompleteOnShutdown(false);
- executor.setCorePoolSize(5);
- executor.setMaxPoolSize(10);
- executor.setQueueCapacity(50);
+ executor.setCorePoolSize(SyncopeConsoleApplication.get().getCorePoolSize());
+ executor.setMaxPoolSize(SyncopeConsoleApplication.get().getMaxPoolSize());
+ executor.setQueueCapacity(SyncopeConsoleApplication.get().getQueueCapacity());
executor.initialize();
}
+ protected String message(final SyncopeClientException sce) {
+ return sce.getType().name() + ": " + sce.getElements().stream().collect(Collectors.joining(", "));
+ }
+
+ /**
+ * Extract and localize (if translation available) the actual message from the given exception; then, report it
+ * via {@link Session#error(java.io.Serializable)}.
+ *
+ * @see org.apache.syncope.client.lib.RestClientExceptionMapper
+ *
+ * @param e raised exception
+ */
+ public void onException(final Exception e) {
+ Throwable root = ExceptionUtils.getRootCause(e);
+ String message = root.getMessage();
+
+ if (root instanceof SyncopeClientException) {
+ SyncopeClientException sce = (SyncopeClientException) root;
+ message = sce.isComposite()
+ ? sce.asComposite().getExceptions().stream().map(this::message).collect(Collectors.joining("; "))
+ : message(sce);
+ } else if (root instanceof AccessControlException || root instanceof ForbiddenException) {
+ Error error = StringUtils.containsIgnoreCase(message, "expired")
+ ? Error.SESSION_EXPIRED
+ : Error.AUTHORIZATION;
+ message = getApplication().getResourceSettings().getLocalizer().
+ getString(error.key(), null, null, null, null, error.fallback());
+ } else if (root instanceof BadRequestException || root instanceof WebServiceException) {
+ message = getApplication().getResourceSettings().getLocalizer().
+ getString(Error.REST.key(), null, null, null, null, Error.REST.fallback());
+ }
+
+ message = getApplication().getResourceSettings().getLocalizer().
+ getString(message, null, null, null, null, message);
+ error(message);
+ }
+
public MediaType getMediaType() {
return clientFactory.getContentType().getMediaType();
}
@@ -113,11 +190,21 @@ public SyncopeClient getAnonymousClient() {
}
public void execute(final Runnable command) {
- executor.execute(command);
+ try {
+ executor.execute(command);
+ } catch (TaskRejectedException e) {
+ LOG.error("Could not execute {}", command, e);
+ }
}
public Future execute(final Callable command) {
- return executor.submit(command);
+ try {
+ return executor.submit(command);
+ } catch (TaskRejectedException e) {
+ LOG.error("Could not execute {}", command, e);
+
+ return new CompletableFuture<>();
+ }
}
public PlatformInfo getPlatformInfo() {
@@ -147,7 +234,7 @@ public boolean authenticate(final String username, final String password) {
try {
client = clientFactory.setDomain(getDomain()).create(username, password);
- refreshAuth();
+ refreshAuth(username);
authenticated = true;
} catch (Exception e) {
@@ -163,7 +250,7 @@ public boolean authenticate(final String jwt) {
try {
client = clientFactory.setDomain(getDomain()).create(jwt);
- refreshAuth();
+ refreshAuth(null);
authenticated = true;
} catch (Exception e) {
@@ -181,6 +268,8 @@ public boolean authenticate(final String jwt) {
public void cleanup() {
client = null;
auth = null;
+ delegations = null;
+ delegatedBy = null;
selfTO = null;
services.clear();
}
@@ -202,28 +291,57 @@ public UserTO getSelfTO() {
}
public List getAuthRealms() {
- List sortable = new ArrayList<>();
- List available = SetUniqueList.setUniqueList(sortable);
- auth.values().forEach(entitlement -> {
- available.addAll(entitlement);
- });
- Collections.sort(sortable);
- return sortable;
+ return auth.values().stream().flatMap(Set::stream).distinct().sorted().collect(Collectors.toList());
}
- public boolean owns(final String entitlements) {
- return owns(entitlements, SyncopeConstants.ROOT_REALM);
+ public List getSearchableRealms() {
+ Set roots = auth.get(StandardEntitlement.REALM_LIST);
+ return CollectionUtils.isEmpty(roots)
+ ? Collections.emptyList()
+ : roots.stream().sorted().collect(Collectors.toList());
}
- public boolean owns(final String entitlements, final String realm) {
+ public Optional getRootRealm(final String initial) {
+ List searchable = getSearchableRealms();
+ return searchable.isEmpty()
+ ? Optional.empty()
+ : initial != null && searchable.stream().anyMatch(initial::startsWith)
+ ? Optional.of(initial)
+ : searchable.stream().findFirst();
+ }
+
+ public boolean owns(final String entitlements, final String... realms) {
if (StringUtils.isEmpty(entitlements)) {
return true;
}
+ if (auth == null) {
+ return false;
+ }
+
+ Set requested = ArrayUtils.isEmpty(realms)
+ ? Collections.emptySet()
+ : new HashSet<>(Arrays.asList(realms));
+
for (String entitlement : entitlements.split(",")) {
- if (auth != null && auth.containsKey(entitlement) && (realm == null
- || auth.get(entitlement).stream().anyMatch(ownedRealm -> realm.startsWith(ownedRealm)))) {
- return true;
+ if (auth.containsKey(entitlement)) {
+ boolean owns = false;
+
+ Set owned = auth.get(entitlement).stream().
+ map(RealmsUtils::getFullPath).collect(Collectors.toSet());
+ if (requested.isEmpty()) {
+ return !owned.isEmpty();
+ } else {
+ for (String realm : requested) {
+ if (realm.startsWith(SyncopeConstants.ROOT_REALM)) {
+ owns |= owned.stream().anyMatch(ownedRealm -> realm.startsWith(ownedRealm));
+ } else {
+ owns |= owned.contains(realm);
+ }
+ }
+ }
+
+ return owns;
}
}
@@ -240,15 +358,40 @@ public Roles getRoles() {
return roles;
}
- public void refreshAuth() {
- Pair