diff --git a/.github/workflows/fit_WA_OpenFGA.yml b/.github/workflows/fit_WA_OpenFGA.yml index 59ff9dda026..00c116b7fe2 100644 --- a/.github/workflows/fit_WA_OpenFGA.yml +++ b/.github/workflows/fit_WA_OpenFGA.yml @@ -42,6 +42,6 @@ jobs: - name: Build run: mvn -U -T 1C -P 'skipTests,all,use-apache-snapshots' - name: 'Unit Tests: OpenFGA' - run: mvn -f ext/openfga/client-openfga/pom.xml -P openfga -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true + run: mvn -f ext/openfga/ -P openfga -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true - name: 'WA / OpenFGA' run: mvn -f fit/wa-reference/pom.xml verify -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true -Dit.test=OpenFGAUIITCase diff --git a/.gitignore b/.gitignore index 6087560fb24..25e93365fa1 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ maven-eclipse.xml .vscode node_modules **/checkstyle.cache +ENV.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000000..cd9107d1a4b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,572 @@ + +# Apache Syncope - AGENTS.md + +This document provides context and guidelines for AI agents interacting with the Apache Syncope codebase. + +## Project Overview + +Apache Syncope is a unified identity management platform that enables consistent management of identities, attributes, group membership, relationships, and authorization across multiple applications and directories (LDAP, Active Directory, databases, SCIM, cloud IdPs). + +**Version:** 4.2.0-SNAPSHOT +**License:** Apache-2.0 +**Inception Year:** 2010 +**Website:** https://syncope.apache.org/ +**Build JDK:** OpenJDK 25 (CI also runs on 28-ea) +**Maven:** 3.9.x (CI uses 3.9.6) + +## Architecture: Three Domains + Keymaster + +Since 4.x the codebase is organized around three parallel domains sharing the same layering: + +- **idrepo** - Users, Groups, AnyObjects, Realms, types, schemas, policies +- **idm** - Identity management extensions (roles, delegations, etc.) +- **am** - Access management (auth modules, OIDC clients, SAML SP, MFA) + +Each domain is split into `common/` (API contracts), `core/` (server logic + REST), `client/` (Wicket UIs). +**Keymaster** provides key management / encryption and lives in `common/keymaster` + `core/self-keymaster-starter`. + +Typical request flow: `client` → JAX-RS interface (`common/*/rest-api`) → implementation (`core/*/rest-cxf`) → logic (`core/*/logic`) → DAO (`core/persistence-api` → `core/persistence-jpa`) → database. + +## Multi-Module Maven Structure + +``` +root (parent POM) [4.2.0-SNAPSHOT] +├── common/ +│ ├── keymaster/ - Key management +│ │ ├── client-api/ +│ │ ├── client-zookeeper/ +│ │ └── self/ +│ ├── idrepo/ +│ │ ├── lib/ - Shared beans, TOs, utilities +│ │ └── rest-api/ - JAX-RS annotated service interfaces +│ ├── idm/ +│ │ ├── lib/ +│ │ └── rest-api/ +│ └── am/ +│ ├── lib/ +│ └── rest-api/ +├── core/ +│ ├── idrepo/ - logic/ + rest-cxf/ +│ ├── idm/ - logic/ + rest-cxf/ +│ ├── am/ - logic/ + rest-cxf/ +│ ├── persistence-api/ - Entity + DAO interfaces +│ ├── persistence-common/ - Shared persistence code +│ ├── persistence-jpa/ - OpenJPA entities and DAOs +│ ├── persistence-neo4j/ - Neo4j persistence +│ ├── provisioning-api/ - Provisioning interfaces +│ ├── provisioning-java/ - Provisioning managers, push/pull +│ ├── workflow-api/ - Workflow interfaces +│ ├── workflow-java/ - Workflow logic +│ ├── spring/ - Spring Boot auto-configuration, security +│ ├── starter/ - Spring Boot starter +│ ├── self-keymaster-starter/ +│ └── metrics-starter/ - Micrometer / metrics +├── client/ +│ ├── idrepo/ - common-ui/, console/, enduser/, lib/ +│ ├── idm/ - console/, lib/ +│ └── am/ - console/, enduser/, lib/ +├── ext/ +│ ├── flowable/ - Workflow engine integration (Flowable) +│ ├── saml2sp4ui/ - SAML 2.0 Service Provider for UIs +│ ├── oidcc4ui/ - OpenID Connect for UIs +│ ├── elasticsearch/ - Elasticsearch persistence +│ ├── opensearch/ - OpenSearch persistence +│ ├── scimv2/ - SCIM v2 API +│ └── openfga/ - OpenFGA authorization +├── wa/ - Web Access (legacy): bootstrap/, starter/ with CAS +├── fit/ - Functional integration tests +│ ├── build-tools/ +│ ├── persistence-embedded/ +│ ├── core-reference/ +│ ├── console-reference/ +│ ├── enduser-reference/ +│ └── wa-reference/ +├── standalone/ - Standalone distribution +├── docker/ - Docker images (console/, core/, enduser/, wa/) +├── archetype/ - Maven archetype +└── src/main/asciidoc/ - Reference guide / getting started sources +``` + +Note: Kubernetes Helm charts are **not** a top-level module; they live under `docker/src/main/resources/kubernetes/`. + +## Key Technologies and Dependencies + +### Core stack (versions from root `pom.xml`) + +| Component | Version | Notes | +|-----------|---------|-------| +| Java / `targetJdk` | 25 | `maven-compiler-plugin` `25` | +| Spring Boot | 4.1.2 | Provides BOM, auto-configuration, embedded Tomcat | +| Apache CXF | 4.2.4 | JAX-RS (REST) endpoints | +| OpenJPA | 4.2.0-SNAPSHOT | JPA provider (not Hibernate) | +| Spring Security | 7.x | Via `spring-boot-starter-security` | +| Apache Wicket | 10.12.0 | Console / Enduser UIs (not React/Vue) | +| Swagger / OpenAPI | 3.0.0 | + `swagger-ui` 5.34.1 | +| Tomcat | 11.0.27 (from Spring Boot BOM line) | Embedded container | + +### Security & identity protocols + +- **Spring Security 7** - authentication/authorization in core and clients (no Apache Shiro anymore) +- **Pac4j 6.5.9** - SAML 2.0 (`ext/saml2sp4ui`) and OpenID Connect (`ext/oidcc4ui`) +- **CAS 8.0.3** / CAS client 4.1.1 - used by the legacy `wa` module +- **OpenFGA** (`openfga.version = v1`) - relationship-based authorization, `ext/openfga` +- **SCIM v2** - `ext/scimv2` +- **BouncyCastle 1.86**, **Nimbus JOSE+JWT 10.10** - crypto/JWT support + +### Persistence, search, workflow + +- **OpenJPA 4.2.0-SNAPSHOT** with `jakarta.persistence` annotations +- **Spring Data JPA** - `SyncopeJPARepository` extends `SimpleJpaRepository` +- **Neo4j** - `core/persistence-neo4j` +- **Elasticsearch** 9.4.9 / **OpenSearch** 3.9.0 (Java client 3.10.0) - optional persistence +- **Flowable** 8.0.0 - workflow engine (`ext/flowable`); legacy 6.8.1 also referenced +- **Debezium** 3.4 - CDC integration + +### Connectors (ConnId bundles, configured via `connid.*.version`) + +LDAP 1.5.11, Active Directory 1.4.0, Azure 3.0.0, SCIM 1.0.5, Okta 3.0.8, +ServiceNow 1.0.4, Database 2.5.0, CSV directory 0.8.10, Kafka 1.0.0, REST 1.2.0, Google Apps 1.4.6. + +### Apache Commons (versions in parent POM) + +`commons-lang3` 3.21.0, `commons-text` 1.15.0, `commons-codec` 1.22.1, `commons-jexl` 3.7.1; +others (`commons-io`, `commons-collections4`, ...) managed transitively by the Spring Boot BOM. + +### Testing stack + +| Framework | Version | Purpose | +|-----------|---------|---------| +| JUnit Jupiter | 6.x (Spring Boot BOM) | Unit/integration tests | +| Mockito | 5.x | Mocking | +| AssertJ | 3.x | Fluent assertions | +| Spring Boot Test | 4.1.2 | Context/integration tests | +| Testcontainers | via BOM | `core/persistence-jpa` and `ext/openfga` tests (FIT uses embedded PostgreSQL + Cargo instead) | +| JaCoCo | | Coverage | + +## Agent Guidelines + +### Code Quality Standards + +1. **Module boundaries** + - `common//rest-api`: JAX-RS **interfaces** only (annotations live here) + - `core//rest-cxf`: implementations, extend `Abstract*Service` + - `core//logic`: business logic, classes usually named `*Logic` / `*LogicOp` + - `core/persistence-api`: entity and DAO **interfaces** + - `core/persistence-jpa`: JPA entities (`JPA*`) and DAO implementations (`JPA*DAO`) + - `ext/*`: optional integrations; keep core free of optional dependencies + +2. **Persistence** + - JPA provider is **OpenJPA**, annotations are `jakarta.persistence.*` + - Entity interfaces live in `core/persistence-api/entity`, implementations in `core/persistence-jpa/entity` + - DAO interfaces in `core/persistence-api/dao`, implementations in `core/persistence-jpa/dao` + - Never mix raw SQL in business logic; use DAOs / JPQL / Criteria + +3. **Security-first** + - Use the existing Spring Security configuration (`core/spring`) and the domain auth modules + - Validate inputs via Bean Validation (`jakarta.validation`) and existing validators + - Use parameterized JPA/JPQL queries only; never concatenate user input + - Never log secrets, passwords, tokens, or key material + +4. **Testing** + - Unit tests: JUnit Jupiter + Mockito in the same module under `src/test/java` + - Integration/FIT: `fit/core-reference`, `fit/console-reference`, `fit/enduser-reference`, `fit/wa-reference` + (see [Integration Tests (FIT)](#integration-tests-fit) for commands) + - Embedded DB tests: `fit/persistence-embedded` + - Prefer meaningful coverage of logic-bearing classes; exceptions, annotations, enums and + Spring `@Configuration` classes are lower priority + - Test-only changes: run the affected module's tests and Checkstyle + - Production behavior changes: also run `fit/core-reference` when feasible + - Tests must be independent: never rely on class execution order + - **Neo4j persistence tests** (`core/persistence-neo4j`) require the `-Pneo4j` profile and a + running Docker daemon: they start Neo4j containers via the `docker-maven-plugin` and run + through `maven-failsafe-plugin` (integration tests), e.g. + `mvn -pl core/persistence-neo4j -Pneo4j verify`. Use `-Dit.test=...` to select individual + tests (surefire's `-Dtest=...` does not apply). + +5. **No compiler warnings** + - Produce clean compilation output: zero compiler warnings (unchecked, deprecation, raw types, etc.). + - Fix root causes rather than suppressing warnings with `@SuppressWarnings`. + - Verify by running `mvn -pl test-compile` and checking for warnings. + +6. **Modernizer rules** + - Code must pass the `modernizer:modernizer` check (e.g. prefer `java.util.Map.of()` over traditional map construction). + - Verify by running `mvn -pl modernizer:modernizer` and fixing any reported violations. + +### Common Code Patterns + +#### JAX-RS service interface (`common//rest-api`) + +```java +package org.apache.syncope.common.rest.api.service; + +@Path("groups") +@Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML }) +@Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML }) +public interface GroupService extends AnyService { + @GET + @Path("{key}") + GroupTO read(@PathParam("key") String key); +} +``` + +#### REST implementation (`core//rest-cxf/.../service`) + +```java +public class GroupServiceImpl extends AbstractAnyService + implements GroupService { + + protected final GroupDAO groupDAO; + protected final GroupLogicOp logic; + + public GroupServiceImpl(AnySearchCondVisitor v, GroupDAO groupDAO, GroupLogicOp logic) { + super(v); + this.groupDAO = groupDAO; + this.logic = logic; + } +} +``` + +There are **no** `@RestResource` / `@RestMethod` annotations in 4.2; use standard JAX-RS plus the `JAXRSService` interfaces. + +#### JPA entity (`core/persistence-jpa/.../entity`) + +```java +package org.apache.syncope.core.persistence.jpa.entity; + +@Entity +@Table(name = JPADelegation.TABLE, uniqueConstraints = + @UniqueConstraint(columnNames = { "delegating_id", "delegated_id" })) +@Cacheable +public class JPADelegation extends AbstractGeneratedKeyEntity implements Delegation { + public static final String TABLE = "Delegation"; + + @ManyToOne(optional = false) + private JPAUser delegating; + + @NotNull + @Column(nullable = false) + private OffsetDateTime startDate; +} +``` + +#### DAO (`core/persistence-api/dao` + `core/persistence-jpa/dao`) + +```java +public interface GroupDAO extends AnyDAO { ... } + +// implementation in core/persistence-jpa/dao, e.g. JPAGroupDAO +``` + +#### Provisioning (`core/provisioning-java`) + +```java +public class DefaultGroupProvisioningManager implements GroupProvisioningManager { + // push / pull / deprovision logic +} +``` + +#### Client UI (`client/`) + +The Console and Enduser are **Apache Wicket 10** applications on Spring Boot +(`wicket-spring-boot-starter`), not React/Vue/Angular. + +#### Unit tests in `core/spring` + +`core/spring` hosts most of the pure unit tests for security, policies, JWS and task +execution. Follow these patterns: + +- Use JUnit Jupiter + Mockito; plain `Mockito.mock(...)` is fine, `MockitoExtension` is optional. +- Put the test in the same package as the class under test when protected members need to be + exercised (for example filters' `doFilterInternal` or rules' `enforce`). +- Use `mockStatic(...)` and `mockConstruction(...)` for static factories and collaborators + created with `new` (for example `ImplementationManager` and `RestTemplate`). +- Save and restore global static state in `@BeforeEach` / `@AfterEach`: + - `ApplicationContextProvider` (application context **and** bean factory) + - `SecurityContextHolder` (call `clearContext()`) + - JCache caches used by throttlers +- When a test needs `ApplicationContextProvider`, build a `StaticApplicationContext`, register + the required beans (for example `SecurityProperties`, `UserDAO`), and restore the previous + state afterwards. +- Do **not** leave `ApplicationContextProvider` set to `null`: Spring Test caches application + contexts and will not re-invoke `ApplicationContextAware` on a cached context, which can break + unrelated tests in the same module run. +- For time-dependent throttlers, override `now()` or inject a clock supplier instead of sleeping. +- Respect Checkstyle `ImportOrder`: static imports first (alphabetical, separate group), then + non-static imports alphabetically. + +## Build Commands + +### Maven + +```bash +# Fast local build (skip tests) +mvn clean install -DskipTests + +# Full build including unit + integration (FIT) tests - slow +mvn clean install + +# Everything except the FIT integration tests +mvn clean install -DskipITs + +# Single module + required upstream modules +mvn clean install -pl core/idrepo -am + +# Tests only (ignore failures to inspect all) +mvn test -Dmaven.test.failure.ignore=true + +# Unit tests for one module plus Checkstyle +mvn -pl core/spring test checkstyle:check + +# Single test class / selected test classes +mvn -pl core/spring -Dtest=MyTest test +mvn -pl core/spring -Dtest='MyTest,OtherTest' -DfailIfNoTests=false test + +# Disable Maven build cache (avoids stale results when verifying fixes) +mvn -pl ... -Dmaven.build.cache.enabled=false + +# JPA persistence tests against a real DBMS via Docker (default: embedded PostgreSQL) +mvn -pl core/persistence-jpa -Pmysql test +mvn -pl core/persistence-jpa -Pmariadb test +mvn -pl core/persistence-jpa -Poracle test + +# Static checks used by CI +mvn checkstyle:check tools:verify-legal-files modernizer:modernizer apache-rat:check + +# Site / docs (AsciiDoc reference guide) +mvn clean generate-resources site-deploy -Psite + +# Docker profile (adds docker/ module) +mvn clean install -Pdocker +``` + +### Integration Tests (FIT) + +The `fit/` modules are part of the default reactor and are **not** unit tests: they start an +embedded PostgreSQL, deploy WARs into an embedded Tomcat 11 (Cargo) and exercise the REST API, +the Console (WicketTester, in-process) and the Enduser. A plain `mvn clean install` therefore +runs them too - use `-DskipITs` (or `-DskipTests`) to skip them. + +| Module | Content | +|--------|---------| +| `fit/build-tools` | Shared WAR deployed by all FIT setups (ConnId bundles, test resources) | +| `fit/persistence-embedded` | Embedded PostgreSQL (`io.zonky.test`) behind the `embedded` Spring profile | +| `fit/core-reference` | **All IT sources**: Core REST (`org.apache.syncope.fit.core`), Console (`...fit.console`), Enduser (`...fit.enduser`) | +| `fit/console-reference` | Console WAR (deployed by `fit/wa-reference`) | +| `fit/enduser-reference` | Enduser WAR (deployed by `fit/wa-reference`) | +| `fit/wa-reference` | WA WAR plus `OIDCC4UIITCase`, `OpenFGAUIITCase`, `SAML2SP4UIITCase` (`org.apache.syncope.fit.ui`) | + +FIT modules resolve project artifacts from the local repository (not from a reactor), so +install everything first - this is exactly what CI does before running any FIT job: + +```bash +mvn -U -T 1C -P 'skipTests,all,use-apache-snapshots' +``` + +Core FIT runs `maven-failsafe-plugin` (`*ITCase`) during `verify`, deploying the WAR to +`http://localhost:9080/syncope` (property `cargo.servlet.port`): + +```bash +# Default: embedded PostgreSQL + all extensions (Spring profile "embedded,all") +mvn -f fit/core-reference/pom.xml verify + +# Single IT / several ITs - -Dit.test is failsafe; -Dtest only reaches surefire +mvn -f fit/core-reference/pom.xml verify -Dit.test=RESTITCase +mvn -f fit/core-reference/pom.xml verify -Dit.test='RESTITCase,UserITCase' + +# Same flags as CI, to silence unrelated plugins +mvn -f fit/core-reference/pom.xml verify -Dit.test=RESTITCase \ + -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true \ + -Dcheckstyle.skip=true -Djacoco.skip=true +``` + +Backend-specific runs use the `*-it` profiles below. They start containers with +`docker-maven-plugin` (Docker daemon required). `elasticsearch-it`, `opensearch-it`, +`mysql-it`, `mariadb-it`, `oracle-it`, `debezium-it` and `syncrepl-it` restrict failsafe to +`org/apache/syncope/fit/core/*ITCase` (Console/Enduser ITs are not run); `zookeeper-it` and +`neo4j-it` run the full suite: + +| Profile | Spring profile / container | +|---------|----------------------------| +| `zookeeper-it` | `embedded,zookeeper` - ZooKeeper (keymaster) | +| `elasticsearch-it` | `embedded,elasticsearch` - Elasticsearch + Kibana | +| `opensearch-it` | `embedded,opensearch` - OpenSearch | +| `mysql-it` | `embedded,mysql` - MySQL | +| `mariadb-it` | `embedded,mariadb` - MariaDB | +| `oracle-it` | `embedded,oracle` - Oracle | +| `neo4j-it` | `embedded,neo4j` - Neo4j | +| `debezium-it` | MySQL + Kafka Connect, only `DebeziumITCase` | +| `syncrepl-it` | OpenLDAP, only `SyncReplITCase` | + +```bash +mvn -f fit/core-reference/pom.xml -P mysql-it +mvn -f fit/core-reference/pom.xml -P elasticsearch-it +``` + +#### External search backends: index refresh and IT timing + +When the `elasticsearch-it` / `opensearch-it` profile is active, `AnySearchDAO` is backed by the +external index. User / Group / AnyObject documents are written by +`ElasticsearchIndexManager#entity` / `OpenSearchIndexManager#entity`, which are +`@TransactionalEventListener`s invoked after commit; unlike the Realm branch, they do **not** +pass `refresh(Refresh.True)`. Changes therefore become searchable only after the index refresh +interval (default 1s), which can be longer on loaded CI runners. + +This makes ITs that assert exact counts immediately after create / update / delete racy; the +`awaitIfExtSearchEnabled()` helper in `AbstractITCase` is only a fixed ~2s sleep, not a real +readiness check. Prefer Awaitility polling until the expected state is observed, re-reading all +involved values inside the retry loop. Also note that with external search `orderBy` does not +participate in `AnySearchDAO.count(...)`, so comparing two counts only checks whether the index +changed between the two requests. These failures typically reproduce only in CI (containerised +ES/OS with limited heap), not locally. + +WA FIT deploys Core, Console, Enduser and WA WARs over HTTPS (9443) and starts `httpbin` +and `openfga` containers: + +```bash +mvn -f fit/wa-reference/pom.xml verify -Dit.test=OIDCC4UIITCase +mvn -f fit/wa-reference/pom.xml verify -Dit.test=OpenFGAUIITCase + +# SAML2SP4UI additionally needs the test keystore on its test classpath +mkdir -p fit/wa-reference/target/test-classes +cp fit/core-reference/src/test/resources/saml.keystore.jks fit/wa-reference/target/test-classes/ +mvn -f fit/wa-reference/pom.xml verify -Dit.test=SAML2SP4UIITCase +``` + +`-P debug` and `-P hotswap` (all `fit/*-reference` modules) change the default goal to +`clean verify cargo:run`: the ITs are skipped and Tomcat stays in the foreground with JDWP on +port 8000 for interactive debugging. + +Reports: `target/failsafe-reports/`; Cargo/Tomcat logs under `target/cargo/logs/`. + +### Profiles + +| Profile | Purpose | +|---------|---------| +| `skipTests` | Skip tests via profile (used by CI build step) | +| `docker` | Adds the `docker` module / image build | +| `site` | AsciiDoc site generation and deployment | +| `jdk27` | Auto-activates on JDK >= 27 to add ByteBuddy deps | +| `use-apache-snapshots` | Needed to resolve `openjpa 4.2.0-SNAPSHOT` | +| `mysql`, `mariadb`, `oracle` | `core/persistence-jpa` only: run its tests against the matching DBMS via Docker/Testcontainers instead of embedded PostgreSQL | +| `zookeeper-it`, `elasticsearch-it`, `opensearch-it`, `mysql-it`, `mariadb-it`, `oracle-it`, `neo4j-it`, `debezium-it`, `syncrepl-it` | `fit/core-reference` only: run FIT against the matching backend/container - see [Integration Tests (FIT)](#integration-tests-fit) | +| `debug`, `hotswap` | `fit/*-reference` only: start the container for interactive debugging instead of running ITs | + +OpenJPA is a **snapshot** dependency: if resolution fails, ensure the Apache snapshots +repository is enabled (CI uses `-P 'skipTests,all,use-apache-snapshots'`). + +### Docker Compose + +Compose files live in `docker/src/main/resources/docker-compose/`: + +```bash +cd docker/src/main/resources/docker-compose + +docker compose -f docker-compose-all.yml up -d --build # full stack, PostgreSQL +docker compose -f docker-compose-postgresql.yml up -d +docker compose -f docker-compose-mysql.yml up -d +docker compose -f docker-compose-mariadb.yml up -d +docker compose -f docker-compose-oracle.yml up -d +docker compose -f docker-compose-ha.yml up -d # HA setup +``` + +`docker-compose-all.yml` is the full deployment (Core, Console, Enduser, WA, Keymaster/Zookeeper) +on **PostgreSQL**, not MySQL. + +### Kubernetes (Helm) + +```bash +kubectl apply -f docker/src/main/resources/kubernetes/syncope/ +# or Helm +helm install syncope ./docker/src/main/resources/kubernetes/syncope +helm install postgres ./docker/src/main/resources/kubernetes/postgres +``` + +Charts: `docker/src/main/resources/kubernetes/syncope/` (Core, Console, Enduser) +and `docker/src/main/resources/kubernetes/postgres/`. + +## Database Support + +- **PostgreSQL** - default for Docker/K8s +- **MySQL**, **MariaDB**, **Oracle** +- **Neo4j** - via `core/persistence-neo4j` (GitHub Actions workflow `neo4j.yml`) +- **Elasticsearch / OpenSearch** - via `ext/elasticsearch` / `ext/opensearch` +- Embedded DB for tests - `fit/persistence-embedded` + +Spring profiles select the database in Docker (`docker,postgresql,saml2`, `docker,mysql`, ...). + +## CI / Quality Gates + +CI is on **GitHub Actions** (`.github/workflows/`), not Jenkins: + +- `crosschecks.yml` - build + validate + test matrix on JDK 25 and 28-ea, Linux/Windows/macOS +- `codeql-analysis.yml` - CodeQL security analysis +- `dockerhub.yml` - Docker image publication +- `fit_*.yml` - FIT suites: `fit_Tomcat_PostgreSQL.yml` (default `fit/core-reference`), `fit_Elasticsearch.yml`, `fit_OpenSearch.yml`, `fit_Zookeeper.yml`, `fit_Tomcat_LiveSync.yml` (`debezium-it` + `syncrepl-it`), `fit_WA_*.yml` +- `mysql.yml`, `mariadb.yml`, `oracle.yml`, `neo4j.yml` - `core/persistence-jpa` (or `-neo4j`) unit tests + the matching `fit/core-reference` `*-it` profile + +Every FIT job starts with `mvn -U -T 1C -P 'skipTests,all,use-apache-snapshots'` and then runs +the corresponding `mvn -f fit/...` command (see [Integration Tests (FIT)](#integration-tests-fit)). + +Quality tooling: Checkstyle, Modernizer, Apache RAT, JaCoCo, Maven Enforcer. + +## Troubleshooting + +1. **JDK errors** - build requires JDK 25 (`targetJdk=25`); older JDKs fail at compile. +2. **OpenJPA snapshot not found** - enable Apache snapshots (`-Puse-apache-snapshots`) or build OpenJPA locally. +3. **Checkstyle/Modernizer/RAT failures** - run the same goals as CI (see Build Commands) before submitting. +4. **FIT failures** - default FIT needs no Docker (embedded PostgreSQL via `fit/persistence-embedded`); + the `*-it` profiles and `fit/wa-reference` start containers with `docker-maven-plugin`, so Docker + must be running and its images reachable. Install the project first (`-P 'skipTests,...'`), + otherwise FIT resolves stale artifacts from the local repository. +5. **Docker stack issues** - use the compose files under `docker/src/main/resources/docker-compose/` + and check `SPRING_PROFILES_ACTIVE` matches the chosen database. +6. **UI changes** - Console/Enduser are Wicket apps; rebuild `client/` and use the + `*-reference` FIT modules for regression checks. +7. **Test order interference** - if a test fails only in the full module run, look for global + static state (`ApplicationContextProvider`, `SecurityContextHolder`, JCache) that was not + saved and restored around the test. +8. **External-search FIT failures (counts / visibility)** - `elasticsearch-it` and + `opensearch-it` index writes are near-real-time (no forced refresh for Users / Groups / + AnyObjects), so exact-count assertions right after mutations can fail only in CI; see + [External search backends](#external-search-backends-index-refresh-and-it-timing). + +## Repository Information + +- **Main Repository:** https://github.com/apache/syncope/ +- **CI:** GitHub Actions - https://github.com/apache/syncope/actions +- **Issue Tracker:** https://issues.apache.org/jira/browse/SYNCOPE +- **Mailing Lists:** `user@syncope.apache.org`, `dev@syncope.apache.org`, `commits@syncope.apache.org` + - Archives: https://lists.apache.org/list.html?dev@syncope.apache.org +- **Website & Docs:** https://syncope.apache.org/ + +## For AI Agents Working with Syncope + +1. **Respect the domain layering**: `common//rest-api` → `core//rest-cxf` → `core//logic` → DAO. +2. **JPA is OpenJPA + Jakarta Persistence**: do not introduce Hibernate-specific APIs. +3. **No Shiro**: authentication/authorization is Spring Security (core/clients) plus Pac4j and OpenFGA. +4. **UIs are Apache Wicket**, not SPA frameworks. +5. **Optional features belong in `ext/`**; core must not depend on them. +6. **Keep JDK 25 compatibility** and run Checkstyle/Modernizer/RAT-compatible code. +7. **Keep unit tests order-independent** and restore global static state + (`ApplicationContextProvider`, `SecurityContextHolder`, JCache) after each test. + +--- + +*This AGENTS.md was generated for Apache Syncope 4.2.0-SNAPSHOT to assist AI agents and developers working with the codebase.* diff --git a/SECURITY.md b/SECURITY.md index 72c8f7851db..7556e21f748 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -22,11 +22,10 @@ under the License. | Version | Supported | | ------- | ------------------ | -| 3.0.x | :white_check_mark: | -| 2.1.x | :white_check_mark: | -| 2.0.x | :x: | -| 1.2.x | :x: | -| 1.1.x | :x: | +| 4.2.x | :white_check_mark: | +| 4.1.x | :white_check_mark: | +| 4.0.x | :x: | +| 3.0.x | :x: | ## Reporting a Vulnerability diff --git a/client/am/console/pom.xml b/client/am/console/pom.xml index 405dcf880ba..80677efd69c 100644 --- a/client/am/console/pom.xml +++ b/client/am/console/pom.xml @@ -54,6 +54,18 @@ under the License. syncope-client-am-lib ${project.version} + + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + diff --git a/client/am/console/src/test/java/org/apache/syncope/client/console/commons/AMConstantsTest.java b/client/am/console/src/test/java/org/apache/syncope/client/console/commons/AMConstantsTest.java new file mode 100644 index 00000000000..8dd3053a490 --- /dev/null +++ b/client/am/console/src/test/java/org/apache/syncope/client/console/commons/AMConstantsTest.java @@ -0,0 +1,55 @@ +/* + * 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.console.commons; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class AMConstantsTest { + + @Test + void constants() { + assertEquals("gatewayroute.paginator.rows", AMConstants.PREF_GATEWAYROUTE_PAGINATOR_ROWS); + assertEquals("waconfig.paginator.rows", AMConstants.PREF_WACONFIG_PAGINATOR_ROWS); + assertEquals("wasession.paginator.rows", AMConstants.PREF_WASESSION_PAGINATOR_ROWS); + assertEquals("authmodule.paginator.rows", AMConstants.PREF_AUTHMODULE_PAGINATOR_ROWS); + assertEquals("clientapp.paginator.rows", AMConstants.PREF_CLIENTAPP_PAGINATOR_ROWS); + assertEquals("clientapp.properties.paginator.rows", + AMConstants.PREF_CLIENTAPP_PROPERTIES_PAGINATOR_ROWS); + assertEquals("accesspolicy.conf.attrs.paginator.rows", + AMConstants.PREF_ACCESS_POLICY_CONF_ATTRS_PAGINATOR_ROWS); + assertEquals("saml2idpentity.properties.paginator.rows", + AMConstants.PREF_SAML2_IDP_ENTITY_PAGINATOR_ROWS); + assertEquals("authprofile.paginator.rows", AMConstants.PREF_AUTHPROFILE_PAGINATOR_ROWS); + assertEquals("authprofile.impersonated.paginator.rows", + AMConstants.PREF_AUTHPROFILE_IMPERSONATED_PAGINATOR_ROWS); + assertEquals("authprofile.googlemfaauthtokens.paginator.rows", + AMConstants.PREF_AUTHPROFILE_GOOGLEMFAAUTHTOKENS_PAGINATOR_ROWS); + assertEquals("authprofile.googlemfaauthaccounts.paginator.rows", + AMConstants.PREF_AUTHPROFILE_GOOGLEMFAAUTHACCOUNTS_PAGINATOR_ROWS); + assertEquals("authprofile.mfaTrustedDevices.paginator.rows", + AMConstants.PREF_AUTHPROFILE_MFA_TRUSTED_FDEVICES_PAGINATOR_ROWS); + assertEquals("authprofile.webAuthnDeviceCredentials.paginator.rows", + AMConstants.PREF_AUTHPROFILE_WEBAUTHNDEVICECREDENTIALS_PAGINATOR_ROWS); + assertEquals("authprofile.consentDecisions.paginator.rows", + AMConstants.PREF_AUTHPROFILE_CONSENT_DECISION_PAGINATOR_ROWS); + assertEquals("oidc.customScopes.paginator.rows", AMConstants.PREF_OIDC_CUSTOMSCOPES_PAGINATOR_ROWS); + } +} diff --git a/client/idm/console/pom.xml b/client/idm/console/pom.xml index b2c01da292e..13401773a5c 100644 --- a/client/idm/console/pom.xml +++ b/client/idm/console/pom.xml @@ -54,6 +54,18 @@ under the License. syncope-client-idm-lib ${project.version} + + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + diff --git a/client/idm/console/src/test/java/org/apache/syncope/client/console/commons/IdMConstantsTest.java b/client/idm/console/src/test/java/org/apache/syncope/client/console/commons/IdMConstantsTest.java new file mode 100644 index 00000000000..824f243026f --- /dev/null +++ b/client/idm/console/src/test/java/org/apache/syncope/client/console/commons/IdMConstantsTest.java @@ -0,0 +1,37 @@ +/* + * 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.console.commons; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class IdMConstantsTest { + + @Test + void constants() { + assertEquals("connid.locations.paginator.rows", IdMConstants.PREF_CONNID_LOCATIONS_PAGINATOR_ROWS); + assertEquals("reconciliation.paginator.rows", IdMConstants.PREF_RECONCILIATION_PAGINATOR_ROWS); + assertEquals("resource.status.paginator.rows", IdMConstants.PREF_RESOURCE_STATUS_PAGINATOR_ROWS); + assertEquals("resources.paginator.rows", IdMConstants.PREF_RESOURCES_PAGINATOR_ROWS); + assertEquals("remediation.paginator.rows", IdMConstants.PREF_REMEDIATION_PAGINATOR_ROWS); + assertEquals("org.identityconnectors.common.security.GuardedString", IdMConstants.GUARDED_STRING); + assertEquals("org.identityconnectors.common.security.GuardedByteArray", IdMConstants.GUARDED_BYTE_ARRAY); + } +} diff --git a/client/idm/console/src/test/java/org/apache/syncope/client/console/layout/IdMUserFormLayoutInfoTest.java b/client/idm/console/src/test/java/org/apache/syncope/client/console/layout/IdMUserFormLayoutInfoTest.java new file mode 100644 index 00000000000..8568563f963 --- /dev/null +++ b/client/idm/console/src/test/java/org/apache/syncope/client/console/layout/IdMUserFormLayoutInfoTest.java @@ -0,0 +1,45 @@ +/* + * 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.console.layout; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +import org.junit.jupiter.api.Test; + +class IdMUserFormLayoutInfoTest { + + @Test + void defaults() { + IdMUserFormLayoutInfo info = new IdMUserFormLayoutInfo(); + + assertNotNull(info); + assertNotNull(info.getLinkedAccountFormLayoutInfo()); + assertSame(info.getLinkedAccountFormLayoutInfo(), info.getLinkedAccountFormLayoutInfo()); + } + + @Test + void settersAndGetters() { + IdMUserFormLayoutInfo info = new IdMUserFormLayoutInfo(); + + LinkedAccountFormLayoutInfo layout = new LinkedAccountFormLayoutInfo(); + info.setLinkedAccountFormLayoutInfo(layout); + assertSame(layout, info.getLinkedAccountFormLayoutInfo()); + } +} diff --git a/client/idm/console/src/test/java/org/apache/syncope/client/console/layout/LinkedAccountFormLayoutInfoTest.java b/client/idm/console/src/test/java/org/apache/syncope/client/console/layout/LinkedAccountFormLayoutInfoTest.java new file mode 100644 index 00000000000..c33497ba914 --- /dev/null +++ b/client/idm/console/src/test/java/org/apache/syncope/client/console/layout/LinkedAccountFormLayoutInfoTest.java @@ -0,0 +1,64 @@ +/* + * 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.console.layout; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.apache.syncope.client.console.wizards.any.LinkedAccountWizardBuilder; +import org.junit.jupiter.api.Test; + +class LinkedAccountFormLayoutInfoTest { + + @Test + void defaults() { + LinkedAccountFormLayoutInfo info = new LinkedAccountFormLayoutInfo(); + + assertNotNull(info); + assertEquals(LinkedAccountWizardBuilder.class, info.getFormClass()); + assertTrue(info.isPlainAttrs()); + assertTrue(info.isCredentials()); + assertNotNull(info.getWhichPlainAttrs()); + assertTrue(info.getWhichPlainAttrs().isEmpty()); + assertNotNull(info.getWhichCredentials()); + assertTrue(info.getWhichCredentials().isEmpty()); + } + + @Test + void settersAndGetters() { + LinkedAccountFormLayoutInfo info = new LinkedAccountFormLayoutInfo(); + + info.setPlainAttrs(false); + assertFalse(info.isPlainAttrs()); + + info.setCredentials(false); + assertFalse(info.isCredentials()); + + List credentials = List.of("cred1", "cred2"); + info.getWhichCredentials().addAll(credentials); + assertEquals(credentials, info.getWhichCredentials()); + + List plainAttrs = List.of("attr1", "attr2"); + info.getWhichPlainAttrs().addAll(plainAttrs); + assertEquals(plainAttrs, info.getWhichPlainAttrs()); + } +} diff --git a/client/idm/console/src/test/java/org/apache/syncope/client/console/topology/TopologyNodeTest.java b/client/idm/console/src/test/java/org/apache/syncope/client/console/topology/TopologyNodeTest.java new file mode 100644 index 00000000000..efd5593d2a6 --- /dev/null +++ b/client/idm/console/src/test/java/org/apache/syncope/client/console/topology/TopologyNodeTest.java @@ -0,0 +1,82 @@ +/* + * 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.console.topology; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +class TopologyNodeTest { + + @Test + void constructor() { + TopologyNode node = new TopologyNode("key1", "display1", TopologyNode.Kind.RESOURCE); + + assertNotNull(node); + assertEquals("key1", node.getKey()); + assertEquals("display1", node.getDisplayName()); + assertEquals(TopologyNode.Kind.RESOURCE, node.getKind()); + } + + @Test + void kinds() { + assertEquals(5, TopologyNode.Kind.values().length); + assertEquals(TopologyNode.Kind.RESOURCE, TopologyNode.Kind.valueOf("RESOURCE")); + assertEquals(TopologyNode.Kind.CONNECTOR, TopologyNode.Kind.valueOf("CONNECTOR")); + assertEquals(TopologyNode.Kind.CONNECTOR_SERVER, TopologyNode.Kind.valueOf("CONNECTOR_SERVER")); + assertEquals(TopologyNode.Kind.FS_PATH, TopologyNode.Kind.valueOf("FS_PATH")); + assertEquals(TopologyNode.Kind.SYNCOPE, TopologyNode.Kind.valueOf("SYNCOPE")); + } + + @Test + void statuses() { + assertEquals(4, TopologyNode.Status.values().length); + assertEquals(TopologyNode.Status.UNKNOWN, TopologyNode.Status.valueOf("UNKNOWN")); + assertEquals(TopologyNode.Status.REACHABLE, TopologyNode.Status.valueOf("REACHABLE")); + assertEquals(TopologyNode.Status.UNREACHABLE, TopologyNode.Status.valueOf("UNREACHABLE")); + assertEquals(TopologyNode.Status.FAILURE, TopologyNode.Status.valueOf("FAILURE")); + } + + @Test + void settersAndGetters() { + TopologyNode node = new TopologyNode("key", "name", TopologyNode.Kind.CONNECTOR); + + node.setDisplayName("new name"); + assertEquals("new name", node.getDisplayName()); + + node.setConnectionDisplayName("conn name"); + assertEquals("conn name", node.getConnectionDisplayName()); + + node.setKind(TopologyNode.Kind.FS_PATH); + assertEquals(TopologyNode.Kind.FS_PATH, node.getKind()); + + node.setHost("host"); + assertEquals("host", node.getHost()); + + node.setPort(1234); + assertEquals(1234, node.getPort()); + + node.setX(100); + assertEquals(100, node.getX()); + + node.setY(200); + assertEquals(200, node.getY()); + } +} diff --git a/client/idm/console/src/test/java/org/apache/syncope/client/console/topology/TopologyTest.java b/client/idm/console/src/test/java/org/apache/syncope/client/console/topology/TopologyTest.java new file mode 100644 index 00000000000..22647d0e4d8 --- /dev/null +++ b/client/idm/console/src/test/java/org/apache/syncope/client/console/topology/TopologyTest.java @@ -0,0 +1,37 @@ +/* + * 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.console.topology; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class TopologyTest { + + @Test + void sanitizeMarkupId() { + assertEquals("hello_world", Topology.sanitizeMarkupId("hello world")); + assertEquals("hello-world", Topology.sanitizeMarkupId("hello-world")); + assertEquals("hello.world", Topology.sanitizeMarkupId("hello.world")); + assertEquals("clean_id", Topology.sanitizeMarkupId("clean_id")); + assertEquals("", Topology.sanitizeMarkupId("")); + assertEquals("a_b_c", Topology.sanitizeMarkupId("a@b#c")); + assertEquals("connid___example", Topology.sanitizeMarkupId("connid://example")); + } +} diff --git a/client/idrepo/common-ui/pom.xml b/client/idrepo/common-ui/pom.xml index 78a506c924c..ae073bb3bc7 100644 --- a/client/idrepo/common-ui/pom.xml +++ b/client/idrepo/common-ui/pom.xml @@ -130,6 +130,18 @@ under the License. syncope-client-idrepo-lib ${project.version} + + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + diff --git a/client/idrepo/common-ui/src/test/java/org/apache/syncope/client/ui/commons/BaseSessionErrorTest.java b/client/idrepo/common-ui/src/test/java/org/apache/syncope/client/ui/commons/BaseSessionErrorTest.java new file mode 100644 index 00000000000..36ad9dd937a --- /dev/null +++ b/client/idrepo/common-ui/src/test/java/org/apache/syncope/client/ui/commons/BaseSessionErrorTest.java @@ -0,0 +1,36 @@ +/* + * 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.ui.commons; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +class BaseSessionErrorTest { + + @Test + void errorEnum() { + assertEquals(3, BaseSession.Error.values().length); + + assertNotNull(BaseSession.Error.SESSION_EXPIRED); + assertNotNull(BaseSession.Error.AUTHORIZATION); + assertNotNull(BaseSession.Error.REST); + } +} diff --git a/client/idrepo/common-ui/src/test/java/org/apache/syncope/client/ui/commons/ConnIdSpecialNameTest.java b/client/idrepo/common-ui/src/test/java/org/apache/syncope/client/ui/commons/ConnIdSpecialNameTest.java new file mode 100644 index 00000000000..49acc0e0af8 --- /dev/null +++ b/client/idrepo/common-ui/src/test/java/org/apache/syncope/client/ui/commons/ConnIdSpecialNameTest.java @@ -0,0 +1,36 @@ +/* + * 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.ui.commons; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class ConnIdSpecialNameTest { + + @Test + void constants() { + assertEquals("__ENABLE__", ConnIdSpecialName.ENABLE); + assertEquals("__NAME__", ConnIdSpecialName.NAME); + assertEquals("__UID__", ConnIdSpecialName.UID); + assertEquals("__PASSWORD__", ConnIdSpecialName.PASSWORD); + assertEquals("__ACCOUNT__", ConnIdSpecialName.ACCOUNT); + assertEquals("__GROUP__", ConnIdSpecialName.GROUP); + } +} diff --git a/client/idrepo/common-ui/src/test/java/org/apache/syncope/client/ui/commons/DateOpsTest.java b/client/idrepo/common-ui/src/test/java/org/apache/syncope/client/ui/commons/DateOpsTest.java new file mode 100644 index 00000000000..6144a55c9ad --- /dev/null +++ b/client/idrepo/common-ui/src/test/java/org/apache/syncope/client/ui/commons/DateOpsTest.java @@ -0,0 +1,77 @@ +/* + * 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.ui.commons; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Date; +import org.junit.jupiter.api.Test; + +class DateOpsTest { + + @Test + void convertNull() { + assertNull(DateOps.convert((OffsetDateTime) null)); + assertNull(DateOps.convert((ZonedDateTime) null)); + assertNull(DateOps.convert((LocalDateTime) null)); + assertNull(DateOps.toOffsetDateTime((Date) null)); + assertNull(DateOps.toZonedDateTime((Date) null)); + assertNull(DateOps.toLocalDateTime((Date) null)); + } + + @Test + void convertOffsetDateTime() { + OffsetDateTime odt = OffsetDateTime.now(); + Date date = DateOps.convert(odt); + assertNotNull(date); + assertEquals(odt.toInstant().toEpochMilli(), date.getTime()); + + OffsetDateTime back = DateOps.toOffsetDateTime(date); + assertEquals( + odt.toInstant().truncatedTo(ChronoUnit.MILLIS), + back.toInstant().truncatedTo(ChronoUnit.MILLIS)); + } + + @Test + void convertZonedDateTime() { + ZonedDateTime zdt = ZonedDateTime.now(); + Date date = DateOps.convert(zdt); + assertNotNull(date); + assertEquals(zdt.toInstant().toEpochMilli(), date.getTime()); + + ZonedDateTime back = DateOps.toZonedDateTime(date); + assertEquals( + zdt.toInstant().truncatedTo(ChronoUnit.MILLIS), + back.toInstant().truncatedTo(ChronoUnit.MILLIS)); + } + + @Test + void convertLocalDateTime() { + LocalDateTime ldt = LocalDateTime.now(); + Date date = DateOps.convert(ldt); + assertNotNull(date); + assertEquals(ldt.atZone(DateOps.DEFAULT_ZONE).toInstant().toEpochMilli(), date.getTime()); + } +} diff --git a/client/idrepo/common-ui/src/test/java/org/apache/syncope/client/ui/commons/status/ConnObjectWrapperTest.java b/client/idrepo/common-ui/src/test/java/org/apache/syncope/client/ui/commons/status/ConnObjectWrapperTest.java new file mode 100644 index 00000000000..2116bbfe6cf --- /dev/null +++ b/client/idrepo/common-ui/src/test/java/org/apache/syncope/client/ui/commons/status/ConnObjectWrapperTest.java @@ -0,0 +1,61 @@ +/* + * 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.ui.commons.status; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.apache.syncope.common.lib.to.ConnObject; +import org.apache.syncope.common.lib.to.GroupTO; +import org.apache.syncope.common.lib.to.UserTO; +import org.junit.jupiter.api.Test; + +class ConnObjectWrapperTest { + + @Test + void constructorAndGetters() { + UserTO any = new UserTO(); + any.setKey("key1"); + + String resource = "ldap"; + + ConnObjectWrapper wrapper = new ConnObjectWrapper(any, resource, null); + + assertNotNull(wrapper); + assertEquals(any, wrapper.getAny()); + assertEquals(resource, wrapper.getResource()); + assertEquals(null, wrapper.getConnObjectTO()); + } + + @Test + void constructorWithConnObject() { + GroupTO any = new GroupTO(); + any.setKey("key2"); + + ConnObject connObject = new ConnObject() { + private static final long serialVersionUID = 1L; + }; + + ConnObjectWrapper wrapper = new ConnObjectWrapper(any, "adb", connObject); + + assertEquals(any, wrapper.getAny()); + assertEquals("adb", wrapper.getResource()); + assertEquals(connObject, wrapper.getConnObjectTO()); + } +} diff --git a/client/idrepo/common-ui/src/test/java/org/apache/syncope/client/ui/commons/status/StatusBeanTest.java b/client/idrepo/common-ui/src/test/java/org/apache/syncope/client/ui/commons/status/StatusBeanTest.java new file mode 100644 index 00000000000..510c4dfc5fa --- /dev/null +++ b/client/idrepo/common-ui/src/test/java/org/apache/syncope/client/ui/commons/status/StatusBeanTest.java @@ -0,0 +1,106 @@ +/* + * 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.ui.commons.status; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.UUID; +import org.apache.syncope.common.lib.to.GroupTO; +import org.apache.syncope.common.lib.to.UserTO; +import org.junit.jupiter.api.Test; + +class StatusBeanTest { + + private static final String KEY = UUID.randomUUID().toString(); + private static final String NAME = "testUser"; + private static final String RESOURCE = "ldap"; + + @Test + void userConstructor() { + UserTO user = new UserTO(); + user.setKey(KEY); + user.setUsername(NAME); + + StatusBean bean = new StatusBean(user, RESOURCE); + + assertNotNull(bean); + assertEquals(KEY, bean.getKey()); + assertEquals(NAME, bean.getName()); + assertEquals(RESOURCE, bean.getResource()); + assertEquals(Status.OBJECT_NOT_FOUND, bean.getStatus()); + assertTrue(bean.isLinked()); + assertEquals(bean, bean); + } + + @Test + void groupConstructor() { + GroupTO group = new GroupTO(); + group.setKey(KEY); + group.setName(NAME); + + StatusBean bean = new StatusBean(group, RESOURCE); + + assertNotNull(bean); + assertEquals(KEY, bean.getKey()); + assertEquals(NAME, bean.getName()); + assertEquals(RESOURCE, bean.getResource()); + } + + @Test + void settersAndGetters() { + StatusBean bean = new StatusBean(new UserTO(), RESOURCE); + + bean.setConnObjectLink("connLink"); + assertEquals("connLink", bean.getConnObjectLink()); + + bean.setStatus(Status.ACTIVE); + assertEquals(Status.ACTIVE, bean.getStatus()); + + bean.setLinked(false); + assertFalse(bean.isLinked()); + } + + @Test + void equalsAndHashCode() { + UserTO user1 = new UserTO(); + user1.setKey(KEY); + user1.setUsername(NAME); + + UserTO user2 = new UserTO(); + user2.setKey(KEY); + user2.setUsername(NAME); + + StatusBean bean1 = new StatusBean(user1, RESOURCE); + StatusBean bean2 = new StatusBean(user2, RESOURCE); + + assertEquals(bean1, bean2); + assertEquals(bean1.hashCode(), bean2.hashCode()); + assertNotEquals(bean1, new StatusBean(new UserTO(), RESOURCE)); + } + + @Test + void toStringNotNull() { + StatusBean bean = new StatusBean(new UserTO(), RESOURCE); + assertNotNull(bean.toString()); + } +} diff --git a/client/idrepo/common-ui/src/test/java/org/apache/syncope/client/ui/commons/status/StatusTest.java b/client/idrepo/common-ui/src/test/java/org/apache/syncope/client/ui/commons/status/StatusTest.java new file mode 100644 index 00000000000..9fe5ec7ae5b --- /dev/null +++ b/client/idrepo/common-ui/src/test/java/org/apache/syncope/client/ui/commons/status/StatusTest.java @@ -0,0 +1,57 @@ +/* + * 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.ui.commons.status; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class StatusTest { + + @Test + void values() { + assertEquals(6, Status.values().length); + + assertEquals("", Status.NOT_YET_SUBMITTED.toString()); + assertEquals("created", Status.CREATED.toString()); + assertEquals("active", Status.ACTIVE.toString()); + assertEquals("inactive", Status.SUSPENDED.toString()); + assertEquals("undefined", Status.UNDEFINED.toString()); + assertEquals("objectnotfound", Status.OBJECT_NOT_FOUND.toString()); + } + + @Test + void isActive() { + assertTrue(Status.ACTIVE.isActive()); + assertFalse(Status.NOT_YET_SUBMITTED.isActive()); + assertFalse(Status.CREATED.isActive()); + assertFalse(Status.SUSPENDED.isActive()); + assertFalse(Status.UNDEFINED.isActive()); + assertFalse(Status.OBJECT_NOT_FOUND.isActive()); + } + + @Test + void valueOf() { + assertEquals(Status.ACTIVE, Status.valueOf("ACTIVE")); + assertEquals(Status.SUSPENDED, Status.valueOf("SUSPENDED")); + assertEquals(Status.NOT_YET_SUBMITTED, Status.valueOf("NOT_YET_SUBMITTED")); + } +} diff --git a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/BookmarkablePageLinkBuilderTest.java b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/BookmarkablePageLinkBuilderTest.java new file mode 100644 index 00000000000..0d84480eb26 --- /dev/null +++ b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/BookmarkablePageLinkBuilderTest.java @@ -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. + */ +package org.apache.syncope.client.console; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +import org.apache.syncope.client.console.pages.Dashboard; +import org.apache.wicket.markup.html.link.BookmarkablePageLink; +import org.junit.jupiter.api.Test; + +class BookmarkablePageLinkBuilderTest extends AbstractTest { + + @Test + void buildWithKeyAndDefaultPageClass() { + BookmarkablePageLink link = BookmarkablePageLinkBuilder.build("nope", Dashboard.class); + + assertNotNull(link); + assertSame(Dashboard.class, link.getPageClass()); + } + + @Test + void buildWithKeyIdAndDefaultPageClass() { + BookmarkablePageLink link = BookmarkablePageLinkBuilder.build("nope", "myId", Dashboard.class); + + assertNotNull(link); + assertEquals("myId", link.getId()); + assertSame(Dashboard.class, link.getPageClass()); + } +} diff --git a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/ConsolePropertiesTest.java b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/ConsolePropertiesTest.java new file mode 100644 index 00000000000..dd2bb3aeaec --- /dev/null +++ b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/ConsolePropertiesTest.java @@ -0,0 +1,91 @@ +/* + * 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.console; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Map; +import org.apache.syncope.client.console.panels.AnyPanel; +import org.junit.jupiter.api.Test; + +class ConsolePropertiesTest { + + @Test + void defaults() { + ConsoleProperties props = new ConsoleProperties(); + + assertNotNull(props); + assertEquals(AnyPanel.class.getName(), props.getDefaultAnyPanelClass()); + assertEquals(20, props.getRealmsFullTreeThreshold()); + assertEquals(3, props.getJwtExpirationMinutesThreshold()); + assertTrue(props.getPage().isEmpty()); + assertTrue(props.isUseGZIPCompression()); + assertTrue(props.isxForward()); + assertTrue(props.isCsrf()); + assertEquals("admin", props.getAdminUser()); + } + + @Test + void settersAndGetters() { + ConsoleProperties props = new ConsoleProperties(); + + props.setDefaultAnyPanelClass("custom.panel.Class"); + assertEquals("custom.panel.Class", props.getDefaultAnyPanelClass()); + + props.setRealmsFullTreeThreshold(50); + assertEquals(50, props.getRealmsFullTreeThreshold()); + + props.setJwtExpirationMinutesThreshold(10); + assertEquals(10, props.getJwtExpirationMinutesThreshold()); + + props.setAdminUser("adminUser"); + assertEquals("adminUser", props.getAdminUser()); + + props.setAnonymousUser("anonymous"); + assertEquals("anonymous", props.getAnonymousUser()); + + props.setAnonymousKey("anonymousKey"); + assertEquals("anonymousKey", props.getAnonymousKey()); + + props.setUseGZIPCompression(false); + assertFalse(props.isUseGZIPCompression()); + + props.setxForward(false); + assertFalse(props.isxForward()); + + props.setCsrf(false); + assertFalse(props.isCsrf()); + + Map securityHeaders = props.getSecurityHeaders(); + securityHeaders.put("Custom-Header", "value"); + assertEquals(1, securityHeaders.size()); + } + + @Test + void pageMap() { + ConsoleProperties props = new ConsoleProperties(); + + Map> page = props.getPage(); + assertNotNull(page); + assertTrue(page.isEmpty()); + } +} diff --git a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/PreferenceManagerTest.java b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/PreferenceManagerTest.java new file mode 100644 index 00000000000..c1abe76866f --- /dev/null +++ b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/PreferenceManagerTest.java @@ -0,0 +1,37 @@ +/* + * 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.console; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class PreferenceManagerTest { + + @Test + void getPaginatorChoices() { + List choices = PreferenceManager.getPaginatorChoices(); + + assertNotNull(choices); + assertEquals(3, choices.size()); + assertEquals(List.of(10, 25, 50), choices); + } +} diff --git a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/SyncopeConsoleApplicationTest.java b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/SyncopeConsoleApplicationTest.java index b10076942f7..3657e7b08c2 100644 --- a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/SyncopeConsoleApplicationTest.java +++ b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/SyncopeConsoleApplicationTest.java @@ -39,10 +39,10 @@ import org.apache.wicket.util.tester.FormTester; import org.junit.jupiter.api.Test; -public class SyncopeConsoleApplicationTest extends AbstractTest { +class SyncopeConsoleApplicationTest extends AbstractTest { @Test - public void securityHeaders() throws IOException { + void securityHeaders() throws IOException { Map securityHeaders = PROPS.getSecurityHeaders(); assertEquals(4, securityHeaders.size()); @@ -62,7 +62,7 @@ public void securityHeaders() throws IOException { } @Test - public void errors() { + void errors() { SyncopeConsoleSession session = SyncopeConsoleSession.get(); assertNull(session.getFeedbackMessages().first()); diff --git a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/SyncopeConsoleSessionErrorTest.java b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/SyncopeConsoleSessionErrorTest.java new file mode 100644 index 00000000000..cd54117766e --- /dev/null +++ b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/SyncopeConsoleSessionErrorTest.java @@ -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. + */ +package org.apache.syncope.client.console; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +class SyncopeConsoleSessionErrorTest { + + @Test + void errorEnum() { + assertEquals(3, SyncopeConsoleSession.Error.values().length); + + SyncopeConsoleSession.Error sessionExpired = SyncopeConsoleSession.Error.SESSION_EXPIRED; + assertNotNull(sessionExpired); + assertEquals("error.session.expired", sessionExpired.key()); + assertEquals("Session expired: please login again", sessionExpired.fallback()); + + SyncopeConsoleSession.Error authorization = SyncopeConsoleSession.Error.AUTHORIZATION; + assertNotNull(authorization); + assertEquals("error.authorization", authorization.key()); + assertEquals("Insufficient access rights when performing the requested operation", authorization.fallback()); + + SyncopeConsoleSession.Error rest = SyncopeConsoleSession.Error.REST; + assertNotNull(rest); + assertEquals("error.rest", rest.key()); + assertEquals("There was an error while contacting the Core server", rest.fallback()); + } +} diff --git a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/chartjs/ChartTest.java b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/chartjs/ChartTest.java new file mode 100644 index 00000000000..6966e023a3a --- /dev/null +++ b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/chartjs/ChartTest.java @@ -0,0 +1,85 @@ +/* + * 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.console.chartjs; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +import org.apache.syncope.client.console.chartjs.data.ChartData; +import org.apache.syncope.client.console.chartjs.options.ChartOptions; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; +import org.junit.jupiter.api.Test; +import tools.jackson.databind.json.JsonMapper; + +class ChartTest { + + private static final JsonMapper MAPPER = new SyncopeJsonMapper(); + + @Test + void defaults() { + Chart chart = new Chart(); + + assertNotNull(chart); + assertNull(chart.getType()); + assertNotNull(chart.getData()); + assertNotNull(chart.getOptions()); + } + + @Test + void getOptionsCreatesInstance() { + Chart chart = new Chart(); + + ChartOptions options = chart.getOptions(); + assertNotNull(options); + assertSame(options, chart.getOptions()); + } + + @Test + void settersAndGetters() { + Chart chart = new Chart(); + + chart.setType(ChartType.line); + assertEquals(ChartType.line, chart.getType()); + + ChartData data = new ChartData(); + chart.setData(data); + assertSame(data, chart.getData()); + + ChartOptions options = new ChartOptions(); + chart.setOptions(options); + assertSame(options, chart.getOptions()); + } + + @Test + void serialization() throws Exception { + Chart chart = new Chart(); + chart.setType(ChartType.bar); + + String json = MAPPER.writeValueAsString(chart); + assertNotNull(json); + + Chart deserialized = MAPPER.readValue(json, Chart.class); + assertNotNull(deserialized); + assertEquals(ChartType.bar, deserialized.getType()); + assertNotNull(deserialized.getData()); + assertNotNull(deserialized.getOptions()); + } +} diff --git a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/chartjs/ChartTypeTest.java b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/chartjs/ChartTypeTest.java new file mode 100644 index 00000000000..144c3fda23b --- /dev/null +++ b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/chartjs/ChartTypeTest.java @@ -0,0 +1,45 @@ +/* + * 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.console.chartjs; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class ChartTypeTest { + + @Test + void values() { + assertEquals(5, ChartType.values().length); + assertEquals(ChartType.line, ChartType.values()[0]); + assertEquals(ChartType.bar, ChartType.values()[1]); + assertEquals(ChartType.pie, ChartType.values()[2]); + assertEquals(ChartType.doughnut, ChartType.values()[3]); + assertEquals(ChartType.scatter, ChartType.values()[4]); + } + + @Test + void valueOf() { + assertEquals(ChartType.line, ChartType.valueOf("line")); + assertEquals(ChartType.bar, ChartType.valueOf("bar")); + assertEquals(ChartType.pie, ChartType.valueOf("pie")); + assertEquals(ChartType.doughnut, ChartType.valueOf("doughnut")); + assertEquals(ChartType.scatter, ChartType.valueOf("scatter")); + } +} diff --git a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/commons/ModeTest.java b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/commons/ModeTest.java new file mode 100644 index 00000000000..46df94fde98 --- /dev/null +++ b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/commons/ModeTest.java @@ -0,0 +1,39 @@ +/* + * 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.console.commons; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class ModeTest { + + @Test + void values() { + assertEquals(2, Mode.values().length); + assertEquals(Mode.ADMIN, Mode.values()[0]); + assertEquals(Mode.TEMPLATE, Mode.values()[1]); + } + + @Test + void valueOf() { + assertEquals(Mode.ADMIN, Mode.valueOf("ADMIN")); + assertEquals(Mode.TEMPLATE, Mode.valueOf("TEMPLATE")); + } +} diff --git a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/layout/AnyLayoutTest.java b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/layout/AnyLayoutTest.java index a1b65d2efa9..ad0cd862553 100644 --- a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/layout/AnyLayoutTest.java +++ b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/layout/AnyLayoutTest.java @@ -28,12 +28,12 @@ import org.junit.jupiter.api.Test; import tools.jackson.databind.json.JsonMapper; -public class AnyLayoutTest { +class AnyLayoutTest { private static final JsonMapper MAPPER = new SyncopeJsonMapper(); @Test - public void issueSYNCOPE1554() { + void issueSYNCOPE1554() { AnyLayout defaultObj = new AnyLayout(); defaultObj.setUser(new UserFormLayoutInfo()); defaultObj.setGroup(new GroupFormLayoutInfo()); diff --git a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/panels/search/UserSearchPanelTest.java b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/panels/search/UserSearchPanelTest.java index f6e26c379d2..5f526f60b4d 100644 --- a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/panels/search/UserSearchPanelTest.java +++ b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/panels/search/UserSearchPanelTest.java @@ -33,10 +33,10 @@ import org.apache.wicket.util.tester.FormTester; import org.junit.jupiter.api.Test; -public class UserSearchPanelTest extends AbstractAdminTest { +class UserSearchPanelTest extends AbstractAdminTest { @Test - public void test() { + void test() { BasePage page = new BasePage(); PageReference pageRef = mock(PageReference.class); when(pageRef.getPage()).thenReturn(page); diff --git a/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/BookmarkablePageLinkBuilderTest.java b/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/BookmarkablePageLinkBuilderTest.java new file mode 100644 index 00000000000..0a7c002610f --- /dev/null +++ b/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/BookmarkablePageLinkBuilderTest.java @@ -0,0 +1,48 @@ +/* + * 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.enduser; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +import org.apache.syncope.client.enduser.pages.Dashboard; +import org.apache.wicket.markup.html.link.BookmarkablePageLink; +import org.junit.jupiter.api.Test; + +public class BookmarkablePageLinkBuilderTest extends AbstractTest { + + @Test + void buildWithKeyAndDefaultPageClass() { + BookmarkablePageLink link = BookmarkablePageLinkBuilder.build("nonexistent", Dashboard.class); + + assertNotNull(link); + assertSame(Dashboard.class, link.getPageClass()); + } + + @Test + void buildWithKeyIdAndDefaultPageClass() { + BookmarkablePageLink link = BookmarkablePageLinkBuilder. + build("nonexistent", "myId", Dashboard.class); + + assertNotNull(link); + assertEquals("myId", link.getId()); + assertSame(Dashboard.class, link.getPageClass()); + } +} diff --git a/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/commons/EnduserConstantsTest.java b/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/commons/EnduserConstantsTest.java new file mode 100644 index 00000000000..d9674b5a142 --- /dev/null +++ b/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/commons/EnduserConstantsTest.java @@ -0,0 +1,34 @@ +/* + * 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.enduser.commons; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class EnduserConstantsTest { + + @Test + void constants() { + assertEquals("status", EnduserConstants.STATUS); + assertEquals("landingPage", EnduserConstants.LANDING_PAGE); + assertEquals("contentPanel", EnduserConstants.CONTENT_PANEL); + assertEquals("reauth", EnduserConstants.REAUTH); + } +} diff --git a/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/layout/CustomizationOptionTest.java b/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/layout/CustomizationOptionTest.java new file mode 100644 index 00000000000..266cbea7df4 --- /dev/null +++ b/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/layout/CustomizationOptionTest.java @@ -0,0 +1,54 @@ +/* + * 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.enduser.layout; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class CustomizationOptionTest { + + @Test + void defaults() { + CustomizationOption option = new CustomizationOption(); + + assertNotNull(option); + assertFalse(option.isReadonly()); + assertNotNull(option.getDefaultValues()); + assertTrue(option.getDefaultValues().isEmpty()); + } + + @Test + void settersAndGetters() { + CustomizationOption option = new CustomizationOption(); + + option.setReadonly(true); + assertTrue(option.isReadonly()); + + option.setReadonly(false); + assertFalse(option.isReadonly()); + + option.setDefaultValues(List.of("v1", "v2")); + assertEquals(List.of("v1", "v2"), option.getDefaultValues()); + } +} diff --git a/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/layout/SidebarLayoutTest.java b/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/layout/SidebarLayoutTest.java new file mode 100644 index 00000000000..9b7e28f09d2 --- /dev/null +++ b/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/layout/SidebarLayoutTest.java @@ -0,0 +1,73 @@ +/* + * 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.enduser.layout; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Map; +import org.junit.jupiter.api.Test; + +class SidebarLayoutTest { + + @Test + void defaults() { + SidebarLayout layout = new SidebarLayout(); + + assertNotNull(layout); + assertTrue(layout.isEditUserEnabled()); + assertTrue(layout.isPasswordManagementEnabled()); + assertTrue(layout.isSecurityQuestionManagementEnabled()); + assertTrue(layout.isDismissMfaEnabled()); + assertNotNull(layout.getExtensionsEnabled()); + assertTrue(layout.getExtensionsEnabled().isEmpty()); + } + + @Test + void isExtensionEnabledDefaultTrue() { + SidebarLayout layout = new SidebarLayout(); + + assertTrue(layout.isExtensionEnabled("unknown")); + } + + @Test + void settersAndGetters() { + SidebarLayout layout = new SidebarLayout(); + + layout.setEditUserEnabled(false); + assertFalse(layout.isEditUserEnabled()); + + layout.setPasswordManagementEnabled(false); + assertFalse(layout.isPasswordManagementEnabled()); + + layout.setSecurityQuestionManagementEnabled(false); + assertFalse(layout.isSecurityQuestionManagementEnabled()); + + layout.setDismissMfaEnabled(false); + assertFalse(layout.isDismissMfaEnabled()); + + Map extensions = Map.of("ext1", false, "ext2", true); + layout.getExtensionsEnabled().putAll(extensions); + assertEquals(2, layout.getExtensionsEnabled().size()); + assertFalse(layout.isExtensionEnabled("ext1")); + assertTrue(layout.isExtensionEnabled("ext2")); + } +} diff --git a/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/model/CustomAttributeTest.java b/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/model/CustomAttributeTest.java new file mode 100644 index 00000000000..34d8b87f4a4 --- /dev/null +++ b/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/model/CustomAttributeTest.java @@ -0,0 +1,66 @@ +/* + * 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.enduser.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class CustomAttributeTest { + + @Test + void defaults() { + CustomAttribute attribute = new CustomAttribute(); + + assertNotNull(attribute); + assertFalse(attribute.isReadonly()); + assertNotNull(attribute.getDefaultValues()); + assertTrue(attribute.getDefaultValues().isEmpty()); + } + + @Test + void settersAndGetters() { + CustomAttribute attribute = new CustomAttribute(); + + attribute.readonly(true); + assertTrue(attribute.isReadonly()); + + attribute.readonly(false); + assertFalse(attribute.isReadonly()); + + List values = List.of("value1", "value2"); + attribute.defaultValues(values); + assertSame(values, attribute.getDefaultValues()); + } + + @Test + void fluentApi() { + CustomAttribute attribute = new CustomAttribute() + .readonly(true) + .defaultValues(List.of("a", "b")); + + assertTrue(attribute.isReadonly()); + assertEquals(List.of("a", "b"), attribute.getDefaultValues()); + } +} diff --git a/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/model/CustomAttributesInfoTest.java b/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/model/CustomAttributesInfoTest.java new file mode 100644 index 00000000000..3206b41a617 --- /dev/null +++ b/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/model/CustomAttributesInfoTest.java @@ -0,0 +1,63 @@ +/* + * 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.enduser.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class CustomAttributesInfoTest { + + @Test + void defaults() { + CustomAttributesInfo info = new CustomAttributesInfo(); + + assertNotNull(info); + assertNotNull(info.getAttributes()); + assertTrue(info.getAttributes().isEmpty()); + } + + @Test + void settersAndGetters() { + CustomAttributesInfo info = new CustomAttributesInfo(); + + Map attributes = new HashMap<>(); + attributes.put("attr1", new CustomAttribute()); + info.setAttributes(attributes); + + assertSame(attributes, info.getAttributes()); + } + + @Test + void fluentApi() { + CustomAttributesInfo info = new CustomAttributesInfo(); + CustomAttribute attribute = new CustomAttribute(); + + CustomAttributesInfo result = info.attributes(Map.of("attr1", attribute)); + + assertSame(info, result); + assertEquals(1, info.getAttributes().size()); + assertSame(attribute, info.getAttributes().get("attr1")); + } +} diff --git a/client/idrepo/lib/pom.xml b/client/idrepo/lib/pom.xml index 9b677f63256..08e2befbb7e 100644 --- a/client/idrepo/lib/pom.xml +++ b/client/idrepo/lib/pom.xml @@ -65,7 +65,8 @@ under the License. syncope-common-idrepo-rest-api ${project.version} - + + org.slf4j slf4j-simple @@ -76,6 +77,11 @@ under the License. junit-jupiter test + + org.mockito + mockito-core + test + diff --git a/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/BasicAuthenticationHandlerTest.java b/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/BasicAuthenticationHandlerTest.java new file mode 100644 index 00000000000..26d9ba805bb --- /dev/null +++ b/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/BasicAuthenticationHandlerTest.java @@ -0,0 +1,48 @@ +/* + * 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.lib; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +import org.junit.jupiter.api.Test; + +class BasicAuthenticationHandlerTest { + + private static final String USERNAME = "username"; + + private static final String PASSWORD = "password"; + + @Test + void constructor() { + BasicAuthenticationHandler handler = new BasicAuthenticationHandler(USERNAME, PASSWORD); + + assertNotNull(handler); + assertEquals(USERNAME, handler.getUsername()); + assertEquals(PASSWORD, handler.getPassword()); + } + + @Test + void isAuthenticationHandler() { + assertNotNull(new BasicAuthenticationHandler(USERNAME, PASSWORD)); + assertSame(AuthenticationHandler.class, new BasicAuthenticationHandler(USERNAME, PASSWORD). + getClass().getInterfaces()[0]); + } +} diff --git a/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/ConcurrencyTest.java b/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/ConcurrencyTest.java index 2ea7b08231e..b02e72193bc 100644 --- a/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/ConcurrencyTest.java +++ b/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/ConcurrencyTest.java @@ -26,7 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ConcurrencyTest { +class ConcurrencyTest { private static final Logger LOG = LoggerFactory.getLogger(ConcurrencyTest.class); @@ -37,7 +37,7 @@ public class ConcurrencyTest { }); @Test - public void multiThreadTest() throws InterruptedException { + void multiThreadTest() throws InterruptedException { for (int i = 0; i < THREAD_NUMBER; i++) { Thread execution = new Thread("Th-" + StringUtils.leftPad(String.valueOf(i), 5, '0')) { @@ -64,7 +64,7 @@ public void run() { } @Test - public void multiCallTest() { + void multiCallTest() { try { for (int i = 0; i < THREAD_NUMBER; i++) { CLIENT.getService(SyncopeService.class); diff --git a/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/JWTAuthenticationHandlerTest.java b/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/JWTAuthenticationHandlerTest.java new file mode 100644 index 00000000000..14dfa16e755 --- /dev/null +++ b/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/JWTAuthenticationHandlerTest.java @@ -0,0 +1,44 @@ +/* + * 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.lib; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +class JWTAuthenticationHandlerTest { + + private static final String JWT = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyIn0.signature"; + + @Test + void constructor() { + JWTAuthenticationHandler handler = new JWTAuthenticationHandler(JWT); + + assertNotNull(handler); + assertEquals(JWT, handler.getJwt()); + } + + @Test + void isAuthenticationHandler() { + assertNotNull(new JWTAuthenticationHandler(JWT)); + assertEquals(AuthenticationHandler.class, new JWTAuthenticationHandler(JWT). + getClass().getInterfaces()[0]); + } +} diff --git a/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/ObtainingJWTAuthenticationHandlerTest.java b/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/ObtainingJWTAuthenticationHandlerTest.java new file mode 100644 index 00000000000..b31242bd695 --- /dev/null +++ b/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/ObtainingJWTAuthenticationHandlerTest.java @@ -0,0 +1,43 @@ +/* + * 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.lib; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +class ObtainingJWTAuthenticationHandlerTest { + + private static final String USERNAME = "username"; + + private static final String PASSWORD = "password"; + + @Test + void constructor() { + ObtainingJWTAuthenticationHandler handler = new ObtainingJWTAuthenticationHandler(USERNAME, PASSWORD); + + assertNotNull(handler); + assertInstanceOf(BasicAuthenticationHandler.class, handler); + assertInstanceOf(AuthenticationHandler.class, handler); + assertEquals(USERNAME, handler.getUsername()); + assertEquals(PASSWORD, handler.getPassword()); + } +} diff --git a/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/RestClientExceptionMapperTest.java b/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/RestClientExceptionMapperTest.java new file mode 100644 index 00000000000..be52297020c --- /dev/null +++ b/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/RestClientExceptionMapperTest.java @@ -0,0 +1,217 @@ +/* + * 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.lib; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.BadRequestException; +import jakarta.ws.rs.ForbiddenException; +import jakarta.ws.rs.NotAuthorizedException; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.Response; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import org.apache.syncope.common.lib.SyncopeClientCompositeException; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.ErrorTO; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.rest.api.RESTHeaders; +import org.junit.jupiter.api.Test; + +class RestClientExceptionMapperTest { + + @Test + void unauthorized() { + RestClientExceptionMapper mapper = new RestClientExceptionMapper(); + Response response = mockResponse(Response.Status.UNAUTHORIZED.getStatusCode()); + when(response.getHeaderString(RESTHeaders.ERROR_INFO)).thenReturn(null); + + Exception ex = mapper.fromResponse(response); + + assertInstanceOf(NotAuthorizedException.class, ex); + assertEquals("Remote unauthorized exception", ex.getMessage()); + } + + @Test + void forbidden() { + RestClientExceptionMapper mapper = new RestClientExceptionMapper(); + Response response = mockResponse(Response.Status.FORBIDDEN.getStatusCode()); + when(response.getHeaderString(RESTHeaders.ERROR_INFO)).thenReturn(null); + + Exception ex = mapper.fromResponse(response); + + assertInstanceOf(ForbiddenException.class, ex); + assertEquals("Remote forbidden exception", ex.getMessage()); + } + + @Test + void badRequestWithoutMessage() { + RestClientExceptionMapper mapper = new RestClientExceptionMapper(); + Response response = mockResponse(Response.Status.BAD_REQUEST.getStatusCode()); + when(response.getHeaderString(RESTHeaders.ERROR_INFO)).thenReturn(null); + + Exception ex = mapper.fromResponse(response); + + assertInstanceOf(BadRequestException.class, ex); + } + + @Test + void badRequestWithMessage() { + RestClientExceptionMapper mapper = new RestClientExceptionMapper(); + Response response = mockResponse(Response.Status.BAD_REQUEST.getStatusCode()); + when(response.getHeaderString(RESTHeaders.ERROR_INFO)).thenReturn("invalid request"); + + Exception ex = mapper.fromResponse(response); + + assertInstanceOf(BadRequestException.class, ex); + assertEquals("invalid request", ex.getMessage()); + } + + @Test + void notFound() { + RestClientExceptionMapper mapper = new RestClientExceptionMapper(); + Response response = mockResponse(Response.Status.NOT_FOUND.getStatusCode()); + + Exception ex = mapper.fromResponse(response); + + assertInstanceOf(WebApplicationException.class, ex); + assertEquals("Remote exception with status code: NOT_FOUND", ex.getMessage()); + } + + @Test + void internalServerError() { + RestClientExceptionMapper mapper = new RestClientExceptionMapper(); + Response response = mockResponse(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()); + + Exception ex = mapper.fromResponse(response); + + assertInstanceOf(WebApplicationException.class, ex); + assertEquals("Remote exception with status code: INTERNAL_SERVER_ERROR", ex.getMessage()); + } + + @Test + void compositeExceptionFromHeadersSingle() { + RestClientExceptionMapper mapper = new RestClientExceptionMapper(); + Response response = mockResponse(Response.Status.BAD_REQUEST.getStatusCode()); + MultivaluedMap headers = headers( + List.of(ClientExceptionType.NotFound.name()), + List.of(ClientExceptionType.NotFound.name() + ":element")); + when(response.getStringHeaders()).thenReturn(headers); + + Exception ex = mapper.fromResponse(response); + + assertInstanceOf(SyncopeClientException.class, ex); + SyncopeClientException clientException = (SyncopeClientException) ex; + assertEquals(ClientExceptionType.NotFound, clientException.getType()); + } + + @Test + void compositeExceptionFromHeadersMultiple() { + RestClientExceptionMapper mapper = new RestClientExceptionMapper(); + Response response = mockResponse(Response.Status.BAD_REQUEST.getStatusCode()); + MultivaluedMap headers = headers( + List.of( + ClientExceptionType.NotFound.name(), + ClientExceptionType.InvalidRealm.name()), + List.of( + ClientExceptionType.NotFound.name() + ":element1", + ClientExceptionType.InvalidRealm.name() + ":element2")); + when(response.getStringHeaders()).thenReturn(headers); + + Exception ex = mapper.fromResponse(response); + + assertInstanceOf(SyncopeClientCompositeException.class, ex); + SyncopeClientCompositeException composite = (SyncopeClientCompositeException) ex; + assertTrue(composite.hasExceptions()); + assertEquals(2, composite.getExceptions().size()); + } + + @Test + void compositeExceptionFromEntity() { + RestClientExceptionMapper mapper = new RestClientExceptionMapper(); + Response response = mockResponse(Response.Status.NOT_FOUND.getStatusCode()); + + ErrorTO error = new ErrorTO(); + error.setType(ClientExceptionType.NotFound); + error.getElements().add("element"); + when(response.readEntity(ErrorTO.class)).thenReturn(error); + + Exception ex = mapper.fromResponse(response); + + assertInstanceOf(SyncopeClientException.class, ex); + SyncopeClientException clientException = (SyncopeClientException) ex; + assertEquals(ClientExceptionType.NotFound, clientException.getType()); + assertEquals(Set.of("element"), clientException.getElements()); + } + + @Test + void unmanagedExceptionTypeInHeaders() { + RestClientExceptionMapper mapper = new RestClientExceptionMapper(); + Response response = mockResponse(Response.Status.BAD_REQUEST.getStatusCode()); + MultivaluedMap headers = headers( + List.of(ClientExceptionType.NotFound.name(), "UnexpectedType"), + List.of()); + when(response.getStringHeaders()).thenReturn(headers); + + Exception ex = mapper.fromResponse(response); + + assertInstanceOf(SyncopeClientException.class, ex); + SyncopeClientException clientException = (SyncopeClientException) ex; + assertEquals(ClientExceptionType.NotFound, clientException.getType()); + } + + @Test + void fromResponseNeverReturnsNull() { + RestClientExceptionMapper mapper = new RestClientExceptionMapper(); + Response response = mockResponse(Response.Status.OK.getStatusCode()); + + assertNotNull(mapper.fromResponse(response)); + } + + private static Response mockResponse(final int status) { + Response response = mock(Response.class); + when(response.getStatus()).thenReturn(status); + when(response.getStatusInfo()).thenReturn(Response.Status.fromStatusCode(status)); + when(response.getStringHeaders()).thenReturn(emptyHeaders()); + return response; + } + + @SuppressWarnings("unchecked") + private static MultivaluedMap emptyHeaders() { + return mock(MultivaluedMap.class); + } + + @SuppressWarnings("unchecked") + private static MultivaluedMap headers( + final List codes, + final List infos) { + + MultivaluedMap headers = mock(MultivaluedMap.class); + when(headers.get(RESTHeaders.ERROR_CODE)).thenReturn(new ArrayList<>(codes)); + when(headers.get(RESTHeaders.ERROR_INFO)).thenReturn(new ArrayList<>(infos)); + return headers; + } +} diff --git a/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/SyncopeClientFactoryBeanTest.java b/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/SyncopeClientFactoryBeanTest.java new file mode 100644 index 00000000000..39c1737254f --- /dev/null +++ b/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/SyncopeClientFactoryBeanTest.java @@ -0,0 +1,153 @@ +/* + * 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.lib; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.cxf.configuration.jsse.TLSClientParameters; +import org.apache.cxf.ext.logging.LoggingFeature; +import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean; +import org.apache.cxf.transports.http.configuration.ConnectionType; +import org.apache.cxf.transports.http.configuration.HTTPClientPolicy; +import org.junit.jupiter.api.Test; +import tools.jackson.jakarta.rs.json.JacksonJsonProvider; + +class SyncopeClientFactoryBeanTest { + + private static final String ADDRESS = "http://localhost"; + + private static final String DOMAIN = "Master"; + + @Test + void defaults() { + SyncopeClientFactoryBean factory = new SyncopeClientFactoryBean(); + factory.setAddress(ADDRESS); + + assertNotNull(factory.getJsonProvider()); + assertInstanceOf(JacksonJsonProvider.class, factory.getJsonProvider()); + assertNotNull(factory.getExceptionMapper()); + assertInstanceOf(RestClientExceptionMapper.class, factory.getExceptionMapper()); + assertNull(factory.getDomain()); + assertFalse(factory.isUseCompression()); + assertNotNull(factory.getHttpClientPolicy()); + assertEquals(ConnectionType.CLOSE, factory.getHttpClientPolicy().getConnection()); + assertNull(factory.getTlsClientParameters()); + assertNotNull(factory.getRestClientFactoryBean()); + } + + @Test + void settersAndGetters() { + SyncopeClientFactoryBean factory = new SyncopeClientFactoryBean(); + + JacksonJsonProvider jsonProvider = new JacksonJsonProvider(); + factory.setJsonProvider(jsonProvider); + assertSame(jsonProvider, factory.getJsonProvider()); + + RestClientExceptionMapper exceptionMapper = new RestClientExceptionMapper(); + factory.setExceptionMapper(exceptionMapper); + assertSame(exceptionMapper, factory.getExceptionMapper()); + + factory.setAddress(ADDRESS); + assertEquals(ADDRESS, factory.getAddress()); + + factory.setDomain(DOMAIN); + assertEquals(DOMAIN, factory.getDomain()); + + factory.setUseCompression(true); + assertTrue(factory.isUseCompression()); + + HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy(); + factory.setHttpClientPolicy(httpClientPolicy); + assertSame(httpClientPolicy, factory.getHttpClientPolicy()); + + TLSClientParameters tlsClientParameters = new TLSClientParameters(); + factory.setTlsClientParameters(tlsClientParameters); + assertSame(tlsClientParameters, factory.getTlsClientParameters()); + + JAXRSClientFactoryBean restClientFactoryBean = new JAXRSClientFactoryBean(); + factory.setRestClientFactoryBean(restClientFactoryBean); + assertSame(restClientFactoryBean, factory.getRestClientFactoryBean()); + } + + @Test + void defaultRestClientFactoryBeanRequiresAddress() { + SyncopeClientFactoryBean factory = new SyncopeClientFactoryBean(); + + assertThrows(IllegalArgumentException.class, factory::getRestClientFactoryBean); + } + + @Test + void defaultRestClientFactoryBean() { + SyncopeClientFactoryBean factory = new SyncopeClientFactoryBean(); + factory.setAddress(ADDRESS); + factory.setDomain(DOMAIN); + + JAXRSClientFactoryBean bean = factory.getRestClientFactoryBean(); + + assertNotNull(bean); + assertEquals(ADDRESS, bean.getAddress()); + assertNotNull(bean.getHeaders()); + assertEquals(DOMAIN, bean.getHeaders().get(org.apache.syncope.common.rest.api.RESTHeaders.DOMAIN). + getFirst()); + assertNotNull(bean.getFeatures()); + assertEquals(1, bean.getFeatures().size()); + assertInstanceOf(LoggingFeature.class, bean.getFeatures().get(0)); + assertNotNull(bean.getProviders()); + } + + @Test + void createBasicAuthenticationHandler() { + SyncopeClientFactoryBean factory = new SyncopeClientFactoryBean(); + factory.setAddress(ADDRESS); + + SyncopeClient client = factory.create(new BasicAuthenticationHandler("user", "password")); + + assertNotNull(client); + assertEquals(ADDRESS, client.getAddress()); + } + + @Test + void createJWT() { + SyncopeClientFactoryBean factory = new SyncopeClientFactoryBean(); + factory.setAddress(ADDRESS); + + SyncopeClient client = factory.create("token"); + + assertNotNull(client); + assertEquals(ADDRESS, client.getAddress()); + } + + @Test + void createAnonymous() { + SyncopeClientFactoryBean factory = new SyncopeClientFactoryBean(); + factory.setAddress(ADDRESS); + + SyncopeAnonymousClient client = factory.createAnonymous("user", "password"); + + assertNotNull(client); + assertEquals(ADDRESS, client.getAddress()); + } +} diff --git a/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/SyncopeClientTest.java b/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/SyncopeClientTest.java new file mode 100644 index 00000000000..45d97a0a000 --- /dev/null +++ b/client/idrepo/lib/src/test/java/org/apache/syncope/client/lib/SyncopeClientTest.java @@ -0,0 +1,175 @@ +/* + * 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.lib; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import jakarta.ws.rs.core.EntityTag; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; +import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean; +import org.apache.cxf.transports.http.configuration.HTTPClientPolicy; +import org.apache.syncope.common.rest.api.RESTHeaders; +import org.apache.syncope.common.rest.api.service.SyncopeService; +import org.junit.jupiter.api.Test; + +class SyncopeClientTest { + + private static final String ADDRESS = "http://localhost"; + + private static final String DOMAIN = "Master"; + + private static final String JWT = "token"; + + private SyncopeClient newClient() { + return newClient(new JWTAuthenticationHandler(JWT)); + } + + private SyncopeClient newClient(final AuthenticationHandler authHandler) { + JAXRSClientFactoryBean restClientFactory = new JAXRSClientFactoryBean(); + restClientFactory.setAddress(ADDRESS); + restClientFactory.setHeaders(new HashMap<>()); + + return new SyncopeClient( + restClientFactory, + new RestClientExceptionMapper(), + authHandler, + false, + new HTTPClientPolicy(), + null); + } + + @Test + void getRealmFiqlSearchConditionBuilder() { + assertNotNull(SyncopeClient.getRealmFiqlSearchConditionBuilder()); + } + + @Test + void getUserSearchConditionBuilder() { + assertNotNull(SyncopeClient.getUserSearchConditionBuilder()); + } + + @Test + void getGroupSearchConditionBuilder() { + assertNotNull(SyncopeClient.getGroupSearchConditionBuilder()); + } + + @Test + void getAnyObjectSearchConditionBuilder() { + assertNotNull(SyncopeClient.getAnyObjectSearchConditionBuilder("anyType")); + } + + @Test + void getConnObjectTOFiqlSearchConditionBuilder() { + assertNotNull(SyncopeClient.getConnObjectTOFiqlSearchConditionBuilder()); + } + + @Test + void getOrderByClauseBuilder() { + assertNotNull(SyncopeClient.getOrderByClauseBuilder()); + } + + @Test + void getAddress() { + assertEquals(ADDRESS, newClient().getAddress()); + } + + @Test + void getDomainDefaultMasterDomain() { + assertEquals("Master", newClient().getDomain()); + } + + @Test + void getDomainCustomDomain() { + SyncopeClient client = newClient(); + client.restClientFactory.getHeaders().put(RESTHeaders.DOMAIN, List.of(DOMAIN)); + + assertEquals(DOMAIN, client.getDomain()); + } + + @Test + void jwtInfoEmpty() { + JAXRSClientFactoryBean restClientFactory = new JAXRSClientFactoryBean(); + restClientFactory.setAddress(ADDRESS); + restClientFactory.setHeaders(new HashMap<>()); + + SyncopeClient client = new SyncopeClient( + restClientFactory, + new RestClientExceptionMapper(), + new BasicAuthenticationHandler("user", "password"), + false, + new HTTPClientPolicy(), + null); + + assertFalse(client.jwtInfo().isPresent()); + } + + @Test + void jwtInfo() { + Optional jwtInfo = newClient().jwtInfo(); + + assertTrue(jwtInfo.isPresent()); + assertEquals(JWT, jwtInfo.get().value()); + assertNull(jwtInfo.get().expiration()); + } + + @Test + void delegatedBy() { + SyncopeClient client = newClient(); + + assertSame(client, client.delegatedBy("delegating")); + assertEquals(List.of("delegating"), client.restClientFactory.getHeaders().get(RESTHeaders.DELEGATED_BY)); + + assertSame(client, client.delegatedBy(null)); + assertNull(client.restClientFactory.getHeaders().get(RESTHeaders.DELEGATED_BY)); + } + + @Test + void header() { + SyncopeService service = newClient().getService(SyncopeService.class); + + assertSame(service, SyncopeClient.header(service, "X-Custom", "value")); + } + + @Test + void ifMatch() { + SyncopeService service = newClient().getService(SyncopeService.class); + + assertSame(service, SyncopeClient.ifMatch(service, new EntityTag("etag"))); + } + + @Test + void ifNoneMatch() { + SyncopeService service = newClient().getService(SyncopeService.class); + + assertSame(service, SyncopeClient.ifNoneMatch(service, new EntityTag("etag"))); + } + + @Test + void getService() { + assertInstanceOf(SyncopeService.class, newClient().getService(SyncopeService.class)); + } +} diff --git a/common/am/lib/pom.xml b/common/am/lib/pom.xml index b3a0b120be9..f61af416b05 100644 --- a/common/am/lib/pom.xml +++ b/common/am/lib/pom.xml @@ -44,11 +44,17 @@ under the License. ${project.version} + org.junit.jupiter junit-jupiter test + + org.mockito + mockito-core + test + diff --git a/common/am/lib/src/test/java/org/apache/syncope/common/lib/AMSessionTest.java b/common/am/lib/src/test/java/org/apache/syncope/common/lib/AMSessionTest.java new file mode 100644 index 00000000000..5c0968e1d11 --- /dev/null +++ b/common/am/lib/src/test/java/org/apache/syncope/common/lib/AMSessionTest.java @@ -0,0 +1,50 @@ +/* + * 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.common.lib; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import org.junit.jupiter.api.Test; + +class AMSessionTest { + + @Test + void session() { + OffsetDateTime authenticationDate = OffsetDateTime.of(2026, 1, 1, 12, 0, 0, 0, ZoneOffset.UTC); + AMSession session = new AMSession(); + session.setKey("key"); + session.setAuthenticationDate(authenticationDate); + session.setPrincipal("principal"); + session.setJson("json"); + + assertEquals("key", session.getKey()); + assertEquals(authenticationDate, session.getAuthenticationDate()); + assertEquals("principal", session.getPrincipal()); + assertEquals("json", session.getJson()); + } + + @Test + void standardScopes() { + assertSame(OIDCStandardScope.openid, OIDCStandardScope.valueOf("openid")); + assertEquals(8, OIDCStandardScope.values().length); + } +} diff --git a/common/am/lib/src/test/java/org/apache/syncope/common/lib/AbstractConfTest.java b/common/am/lib/src/test/java/org/apache/syncope/common/lib/AbstractConfTest.java new file mode 100644 index 00000000000..63552d0198e --- /dev/null +++ b/common/am/lib/src/test/java/org/apache/syncope/common/lib/AbstractConfTest.java @@ -0,0 +1,199 @@ +/* + * 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.common.lib; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.Duration; +import java.util.List; +import java.util.Map; +import org.apache.syncope.common.lib.auth.AzureActiveDirectoryAuthModuleConf; +import org.apache.syncope.common.lib.auth.JDBCAuthModuleConf; +import org.apache.syncope.common.lib.auth.LDAPAuthModuleConf; +import org.apache.syncope.common.lib.auth.OIDCAuthModuleConf; +import org.junit.jupiter.api.Test; + +class AbstractConfTest { + + @Test + void ldap() { + LDAPAuthModuleConf conf = new LDAPAuthModuleConf(); + + conf.setSearchFilter("cn={user}"); + conf.setPageSize(100); + conf.setSubtreeSearch(false); + conf.setLdapType(AbstractLDAPConf.LdapType.AD); + conf.setLdapUrl("ldap://localhost:389"); + conf.setBindDn("cn=admin"); + conf.setBindCredential("secret"); + conf.setBaseDn("dc=example,dc=com"); + conf.setDisablePooling(true); + conf.setMinPoolSize(1); + conf.setMaxPoolSize(2); + conf.setPoolPassivator(AbstractLDAPConf.LdapConnectionPoolPassivator.NONE); + conf.setHostnameVerifier(AbstractLDAPConf.LdapHostnameVerifier.ANY); + conf.setTrustManager(AbstractLDAPConf.LdapTrustManager.ANY); + conf.setValidateOnCheckout(false); + conf.setValidatePeriodically(false); + conf.setValidateTimeout(Duration.ofSeconds(1)); + conf.setValidatePeriod(Duration.ofMinutes(1)); + conf.setFailFast(false); + conf.setIdleTime(Duration.ofMinutes(2)); + conf.setPrunePeriod(Duration.ofMinutes(3)); + conf.setBlockWaitTime(Duration.ofSeconds(2)); + conf.setConnectionStrategy(AbstractLDAPConf.LdapConnectionStrategy.ROUND_ROBIN); + conf.setUseStartTls(true); + conf.setConnectTimeout(Duration.ofSeconds(3)); + conf.setResponseTimeout(Duration.ofSeconds(4)); + conf.setAllowMultipleDns(true); + conf.setAllowMultipleEntries(true); + conf.setFollowReferrals(false); + conf.setBinaryAttributes(List.of("binary")); + + assertEquals("cn={user}", conf.getSearchFilter()); + assertEquals(100, conf.getPageSize()); + assertFalse(conf.isSubtreeSearch()); + assertEquals(AbstractLDAPConf.LdapType.AD, conf.getLdapType()); + assertEquals("ldap://localhost:389", conf.getLdapUrl()); + assertEquals("cn=admin", conf.getBindDn()); + assertEquals("secret", conf.getBindCredential()); + assertEquals("dc=example,dc=com", conf.getBaseDn()); + assertTrue(conf.isDisablePooling()); + assertEquals(1, conf.getMinPoolSize()); + assertEquals(2, conf.getMaxPoolSize()); + assertEquals(AbstractLDAPConf.LdapConnectionPoolPassivator.NONE, conf.getPoolPassivator()); + assertEquals(AbstractLDAPConf.LdapHostnameVerifier.ANY, conf.getHostnameVerifier()); + assertEquals(AbstractLDAPConf.LdapTrustManager.ANY, conf.getTrustManager()); + assertFalse(conf.isValidateOnCheckout()); + assertFalse(conf.isValidatePeriodically()); + assertEquals(Duration.ofSeconds(1), conf.getValidateTimeout()); + assertEquals(Duration.ofMinutes(1), conf.getValidatePeriod()); + assertFalse(conf.isFailFast()); + assertEquals(Duration.ofMinutes(2), conf.getIdleTime()); + assertEquals(Duration.ofMinutes(3), conf.getPrunePeriod()); + assertEquals(Duration.ofSeconds(2), conf.getBlockWaitTime()); + assertEquals(AbstractLDAPConf.LdapConnectionStrategy.ROUND_ROBIN, conf.getConnectionStrategy()); + assertTrue(conf.isUseStartTls()); + assertEquals(Duration.ofSeconds(3), conf.getConnectTimeout()); + assertEquals(Duration.ofSeconds(4), conf.getResponseTimeout()); + assertTrue(conf.isAllowMultipleDns()); + assertTrue(conf.isAllowMultipleEntries()); + assertFalse(conf.isFollowReferrals()); + assertEquals(List.of("binary"), conf.getBinaryAttributes()); + } + + @Test + void jdbc() { + JDBCAuthModuleConf conf = new JDBCAuthModuleConf(); + + conf.setSql("SELECT 1"); + conf.setDialect("dialect"); + conf.setDriverClass("driver"); + conf.setUrl("url"); + conf.setUser("user"); + conf.setPassword("password"); + conf.setDefaultCatalog("catalog"); + conf.setDefaultSchema("schema"); + conf.setHealthQuery("health"); + conf.setIdleTimeout(Duration.ofSeconds(1)); + conf.setDataSourceName("dataSource"); + conf.setMinPoolSize(1); + conf.setMaxPoolSize(2); + conf.setMaxPoolWait(Duration.ofSeconds(2)); + conf.setPoolSuspension(true); + conf.setPoolTimeoutMillis(3L); + conf.setPoolLeakThreshold(Duration.ofSeconds(3)); + + assertEquals("SELECT 1", conf.getSql()); + assertEquals("dialect", conf.getDialect()); + assertEquals("driver", conf.getDriverClass()); + assertEquals("url", conf.getUrl()); + assertEquals("user", conf.getUser()); + assertEquals("password", conf.getPassword()); + assertEquals("catalog", conf.getDefaultCatalog()); + assertEquals("schema", conf.getDefaultSchema()); + assertEquals("health", conf.getHealthQuery()); + assertEquals(Duration.ofSeconds(1), conf.getIdleTimeout()); + assertEquals("dataSource", conf.getDataSourceName()); + assertEquals(1, conf.getMinPoolSize()); + assertEquals(2, conf.getMaxPoolSize()); + assertEquals(Duration.ofSeconds(2), conf.getMaxPoolWait()); + assertTrue(conf.isPoolSuspension()); + assertEquals(3L, conf.getPoolTimeoutMillis()); + assertEquals(Duration.ofSeconds(3), conf.getPoolLeakThreshold()); + } + + @Test + void azureActiveDirectory() { + AzureActiveDirectoryAuthModuleConf conf = new AzureActiveDirectoryAuthModuleConf(); + + conf.setClientId("clientId"); + conf.setClientSecret("clientSecret"); + conf.setLoginUrl("loginUrl"); + conf.setResource("resource"); + conf.setTenant("tenant"); + conf.setScope("scope"); + + assertEquals("clientId", conf.getClientId()); + assertEquals("clientSecret", conf.getClientSecret()); + assertEquals("loginUrl", conf.getLoginUrl()); + assertEquals("resource", conf.getResource()); + assertEquals("tenant", conf.getTenant()); + assertEquals("scope", conf.getScope()); + } + + @Test + void oauth20AndOidc() { + OIDCAuthModuleConf conf = new OIDCAuthModuleConf(); + + conf.setId("clientId"); + conf.setClientSecret("clientSecret"); + conf.setEnabled(false); + conf.setCustomParams(Map.of("key", "value")); + conf.setTokenUrl("tokenUrl"); + conf.setResponseType("responseType"); + conf.setScope("scope"); + conf.setUserIdAttribute("userIdAttribute"); + conf.setDiscoveryUri("discoveryUri"); + conf.setUseNonce(false); + conf.setPreferredJwsAlgorithm("algorithm"); + conf.setMaxClockSkew("skew"); + conf.setResponseMode("responseMode"); + conf.setExpireSessionWithToken(true); + conf.setTokenExpirationAdvance("advance"); + + assertEquals("clientId", conf.getClientId()); + assertEquals("clientSecret", conf.getClientSecret()); + assertFalse(conf.isEnabled()); + assertEquals("value", conf.getCustomParams().get("key")); + assertEquals("tokenUrl", conf.getTokenUrl()); + assertEquals("responseType", conf.getResponseType()); + assertEquals("scope", conf.getScope()); + assertEquals("userIdAttribute", conf.getUserIdAttribute()); + assertEquals("discoveryUri", conf.getDiscoveryUri()); + assertFalse(conf.isUseNonce()); + assertEquals("algorithm", conf.getPreferredJwsAlgorithm()); + assertEquals("skew", conf.getMaxClockSkew()); + assertEquals("responseMode", conf.getResponseMode()); + assertTrue(conf.isExpireSessionWithToken()); + assertEquals("advance", conf.getTokenExpirationAdvance()); + } +} diff --git a/common/am/lib/src/test/java/org/apache/syncope/common/lib/SerializationTest.java b/common/am/lib/src/test/java/org/apache/syncope/common/lib/SerializationTest.java index a2832ab18c4..b1e5f4dc0f1 100644 --- a/common/am/lib/src/test/java/org/apache/syncope/common/lib/SerializationTest.java +++ b/common/am/lib/src/test/java/org/apache/syncope/common/lib/SerializationTest.java @@ -31,12 +31,12 @@ import org.junit.jupiter.api.Test; import tools.jackson.databind.json.JsonMapper; -public class SerializationTest { +class SerializationTest { private static final JsonMapper MAPPER = new SyncopeJsonMapper(); @Test - public void accessPolicyConf() { + void accessPolicyConf() { AccessPolicyTO policy = new AccessPolicyTO(); policy.setName("Test Access policy"); @@ -56,7 +56,7 @@ public void accessPolicyConf() { } @Test - public void attrReleasePolicyConf() { + void attrReleasePolicyConf() { AttrReleasePolicyTO policy = new AttrReleasePolicyTO(); policy.setName("Test Attribute Release Policy"); diff --git a/common/am/lib/src/test/java/org/apache/syncope/common/lib/attr/AttrRepoConfTest.java b/common/am/lib/src/test/java/org/apache/syncope/common/lib/attr/AttrRepoConfTest.java new file mode 100644 index 00000000000..47f182947d7 --- /dev/null +++ b/common/am/lib/src/test/java/org/apache/syncope/common/lib/attr/AttrRepoConfTest.java @@ -0,0 +1,144 @@ +/* + * 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.common.lib.attr; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; +import org.apache.syncope.common.lib.to.AttrRepoTO; +import org.junit.jupiter.api.Test; + +class AttrRepoConfTest { + + private final AttrRepoTO attrRepo = new AttrRepoTO(); + + private final AttrRepoConf.Mapper mapper = mock(AttrRepoConf.Mapper.class); + + @Test + void stub() { + StubAttrRepoConf conf = new StubAttrRepoConf(); + conf.getAttributes().put("cn", "value"); + + Map expected = Map.of("stub", conf); + when(mapper.map(attrRepo, conf)).thenReturn(expected); + + assertSame(expected, conf.map(attrRepo, mapper)); + verify(mapper).map(attrRepo, conf); + assertEquals("value", conf.getAttributes().get("cn")); + } + + @Test + void ldap() { + LDAPAttrRepoConf conf = new LDAPAttrRepoConf(); + conf.setUseAllQueryAttributes(false); + conf.getQueryAttributes().put("uid", "cn"); + + Map expected = Map.of("ldap", conf); + when(mapper.map(attrRepo, conf)).thenReturn(expected); + + assertSame(expected, conf.map(attrRepo, mapper)); + verify(mapper).map(attrRepo, conf); + assertFalse(conf.isUseAllQueryAttributes()); + assertEquals("cn", conf.getQueryAttributes().get("uid")); + } + + @Test + void jdbc() { + JDBCAttrRepoConf conf = new JDBCAttrRepoConf(); + conf.setSingleRow(false); + conf.setRequireAllAttributes(false); + conf.setQueryType(JDBCAttrRepoConf.QueryType.OR); + conf.getColumnMappings().put("cn", "name"); + conf.getUsername().add("uid"); + + Map expected = Map.of("jdbc", conf); + when(mapper.map(attrRepo, conf)).thenReturn(expected); + + assertSame(expected, conf.map(attrRepo, mapper)); + verify(mapper).map(attrRepo, conf); + assertFalse(conf.isSingleRow()); + assertFalse(conf.isRequireAllAttributes()); + assertEquals(JDBCAttrRepoConf.QueryType.OR, conf.getQueryType()); + assertEquals("name", conf.getColumnMappings().get("cn")); + assertEquals(1, conf.getUsername().size()); + } + + @Test + void syncope() { + SyncopeAttrRepoConf conf = new SyncopeAttrRepoConf(); + conf.setDomain("domain"); + conf.setSearchFilter("username==rossini"); + conf.setBasicAuthUsername("user"); + conf.setBasicAuthPassword("pass"); + conf.getHeaders().put("X-Test", "value"); + + Map expected = Map.of("syncope", conf); + when(mapper.map(attrRepo, conf)).thenReturn(expected); + + assertSame(expected, conf.map(attrRepo, mapper)); + verify(mapper).map(attrRepo, conf); + assertEquals("domain", conf.getDomain()); + assertEquals("username==rossini", conf.getSearchFilter()); + assertEquals("user", conf.getBasicAuthUsername()); + assertEquals("pass", conf.getBasicAuthPassword()); + assertEquals("value", conf.getHeaders().get("X-Test")); + } + + @Test + void azureActiveDirectory() { + AzureActiveDirectoryAttrRepoConf conf = new AzureActiveDirectoryAttrRepoConf(); + conf.setCaseInsensitive(true); + conf.setClientId("clientId"); + conf.setTenant("tenant"); + + Map expected = Map.of("azure", conf); + when(mapper.map(attrRepo, conf)).thenReturn(expected); + + assertSame(expected, conf.map(attrRepo, mapper)); + verify(mapper).map(attrRepo, conf); + assertTrue(conf.isCaseInsensitive()); + assertEquals("clientId", conf.getClientId()); + assertEquals("tenant", conf.getTenant()); + assertTrue(conf.getScope().contains("openid")); + } + + @Test + void okta() { + OktaAttrRepoConf conf = new OktaAttrRepoConf(); + conf.setUsernameAttribute("email"); + conf.setApiToken("token"); + + Map expected = Map.of("okta", conf); + when(mapper.map(attrRepo, conf)).thenReturn(expected); + + assertSame(expected, conf.map(attrRepo, mapper)); + verify(mapper).map(attrRepo, conf); + assertEquals("email", conf.getUsernameAttribute()); + assertEquals("token", conf.getApiToken()); + assertTrue(conf.getScopes().contains("okta.users.read")); + conf.setOrganizationUrl("https://example.com"); + assertEquals("https://example.com", conf.getOrganizationUrl()); + } +} diff --git a/common/am/lib/src/test/java/org/apache/syncope/common/lib/auth/AuthModuleConfDetailsTest.java b/common/am/lib/src/test/java/org/apache/syncope/common/lib/auth/AuthModuleConfDetailsTest.java new file mode 100644 index 00000000000..5774a7f16cb --- /dev/null +++ b/common/am/lib/src/test/java/org/apache/syncope/common/lib/auth/AuthModuleConfDetailsTest.java @@ -0,0 +1,380 @@ +/* + * 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.common.lib.auth; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.Map; +import org.apache.syncope.common.lib.types.OIDCTokenEncryptionEncoding; +import org.apache.syncope.common.lib.types.SAML2BindingType; +import org.apache.syncope.common.lib.types.TriStateBoolean; +import org.apache.syncope.common.lib.types.X509PolicySetting; +import org.apache.syncope.common.lib.types.X509PrincipalType; +import org.apache.syncope.common.lib.types.X509RevocationCheckerType; +import org.apache.syncope.common.lib.types.X509RevocationFetcherType; +import org.apache.syncope.common.lib.types.X509SubjectDnFormat; +import org.junit.jupiter.api.Test; + +class AuthModuleConfDetailsTest { + + @Test + void x509() { + X509AuthModuleConf conf = new X509AuthModuleConf(); + X509AuthModuleConf.LDAP ldap = new X509AuthModuleConf.LDAP(); + ldap.setCertificateAttribute("certificate"); + + conf.setName("name"); + conf.setOrder(1); + conf.setRevocationPolicyThreshold(2); + conf.setCheckAll(true); + conf.setRefreshIntervalSeconds(3); + conf.setThrowOnFetchFailure(true); + conf.setPrincipalType(X509PrincipalType.SUBJECT_ALT_NAME); + conf.setPrincipalAlternateAttribute("alternate"); + conf.setPrincipalTypeSubjectDnFormat(X509SubjectDnFormat.RFC2253); + conf.setPrincipalTypeSerialNoDnSerialNumberPrefix("prefix"); + conf.setPrincipalTypeSerialNoDnValueDelimiter("delimiter"); + conf.setPrincipalTypeSerialNoSNRadix(16); + conf.setPrincipalTypeSerialNoHexSNZeroPadding(true); + conf.setRevocationChecker(X509RevocationCheckerType.CRL); + conf.setCrlFetcher(X509RevocationFetcherType.LDAP); + conf.getCrlResources().add("resource"); + conf.setCacheMaxElementsInMemory(4); + conf.setMixedMode(false); + conf.setCacheTimeToLiveSeconds("PT1H"); + conf.setCrlResourceUnavailablePolicy(X509PolicySetting.ALLOW); + conf.setCrlResourceExpiredPolicy(X509PolicySetting.THRESHOLD); + conf.setCrlUnavailablePolicy(X509PolicySetting.ALLOW); + conf.setCrlExpiredPolicy(X509PolicySetting.THRESHOLD); + conf.setRegExTrustedIssuerDnPattern("trusted"); + conf.setMaxPathLength(5); + conf.setMaxPathLengthAllowUnspecified(true); + conf.setCheckKeyUsage(true); + conf.setRequireKeyUsage(true); + conf.setRegExSubjectDnPattern("subject"); + conf.setExtractCert(true); + conf.setSslHeaderName("header"); + conf.setLdap(ldap); + + assertEquals("name", conf.getName()); + assertEquals(1, conf.getOrder()); + assertEquals(2, conf.getRevocationPolicyThreshold()); + assertTrue(conf.isCheckAll()); + assertEquals(3, conf.getRefreshIntervalSeconds()); + assertTrue(conf.isThrowOnFetchFailure()); + assertEquals(X509PrincipalType.SUBJECT_ALT_NAME, conf.getPrincipalType()); + assertEquals("alternate", conf.getPrincipalAlternateAttribute()); + assertEquals(X509SubjectDnFormat.RFC2253, conf.getPrincipalTypeSubjectDnFormat()); + assertEquals("prefix", conf.getPrincipalTypeSerialNoDnSerialNumberPrefix()); + assertEquals("delimiter", conf.getPrincipalTypeSerialNoDnValueDelimiter()); + assertEquals(16, conf.getPrincipalTypeSerialNoSNRadix()); + assertTrue(conf.isPrincipalTypeSerialNoHexSNZeroPadding()); + assertEquals(X509RevocationCheckerType.CRL, conf.getRevocationChecker()); + assertEquals(X509RevocationFetcherType.LDAP, conf.getCrlFetcher()); + assertEquals(List.of("resource"), conf.getCrlResources()); + assertEquals(4, conf.getCacheMaxElementsInMemory()); + assertFalse(conf.isMixedMode()); + assertEquals("PT1H", conf.getCacheTimeToLiveSeconds()); + assertEquals(X509PolicySetting.ALLOW, conf.getCrlResourceUnavailablePolicy()); + assertEquals(X509PolicySetting.THRESHOLD, conf.getCrlResourceExpiredPolicy()); + assertEquals(X509PolicySetting.ALLOW, conf.getCrlUnavailablePolicy()); + assertEquals(X509PolicySetting.THRESHOLD, conf.getCrlExpiredPolicy()); + assertEquals("trusted", conf.getRegExTrustedIssuerDnPattern()); + assertEquals(5, conf.getMaxPathLength()); + assertTrue(conf.isMaxPathLengthAllowUnspecified()); + assertTrue(conf.isCheckKeyUsage()); + assertTrue(conf.isRequireKeyUsage()); + assertEquals("subject", conf.getRegExSubjectDnPattern()); + assertTrue(conf.isExtractCert()); + assertEquals("header", conf.getSslHeaderName()); + assertSame(ldap, conf.getLdap()); + assertEquals("certificate", ldap.getCertificateAttribute()); + } + + @Test + void saml2IdP() { + SAML2IdPAuthModuleConf conf = new SAML2IdPAuthModuleConf(); + + conf.setUserIdAttribute("userIdAttribute"); + conf.setDestinationBinding(SAML2BindingType.POST); + conf.setKeystorePassword("keystorePassword"); + conf.setPrivateKeyPassword("privateKeyPassword"); + conf.setKeystore("keystore"); + conf.setServiceProviderMetadata("serviceProviderMetadata"); + conf.setIdentityProviderMetadataPath("identityProviderMetadataPath"); + conf.setMaximumAuthenticationLifetime("PT1H"); + conf.setAcceptedSkew("PT1M"); + conf.setServiceProviderEntityId("serviceProviderEntityId"); + conf.setForceAuth(true); + conf.setPassive(true); + conf.setNameIdPolicyAllowCreate(TriStateBoolean.FALSE); + conf.getAuthnContextClassRefs().add("authnContextClassRef"); + conf.setAuthnContextComparisonType("exact"); + conf.setNameIdPolicyFormat("nameIdPolicyFormat"); + conf.setResponsesSigned(true); + conf.setWantsAssertionsSigned(true); + conf.setAttributeConsumingServiceIndex(1); + conf.setAssertionConsumerServiceIndex(2); + conf.setUseNameQualifier(false); + conf.setSignServiceProviderMetadata(true); + conf.setSignAuthnRequest(true); + conf.setSignServiceProviderLogoutRequest(true); + conf.getBlockedSignatureSigningAlgorithms().add("blocked"); + conf.getSignatureAlgorithms().add("signature"); + conf.getSignatureReferenceDigestMethods().add("digest"); + conf.setSignatureCanonicalizationAlgorithm("canonicalization"); + conf.setProviderName("providerName"); + + assertEquals("userIdAttribute", conf.getUserIdAttribute()); + assertEquals(SAML2BindingType.POST, conf.getDestinationBinding()); + assertEquals("keystorePassword", conf.getKeystorePassword()); + assertEquals("privateKeyPassword", conf.getPrivateKeyPassword()); + assertEquals("keystore", conf.getKeystore()); + assertEquals("serviceProviderMetadata", conf.getServiceProviderMetadata()); + assertEquals("identityProviderMetadataPath", conf.getIdentityProviderMetadataPath()); + assertEquals("PT1H", conf.getMaximumAuthenticationLifetime()); + assertEquals("PT1M", conf.getAcceptedSkew()); + assertEquals("serviceProviderEntityId", conf.getServiceProviderEntityId()); + assertTrue(conf.isForceAuth()); + assertTrue(conf.isPassive()); + assertEquals(TriStateBoolean.FALSE, conf.getNameIdPolicyAllowCreate()); + assertEquals(List.of("authnContextClassRef"), conf.getAuthnContextClassRefs()); + assertEquals("exact", conf.getAuthnContextComparisonType()); + assertEquals("nameIdPolicyFormat", conf.getNameIdPolicyFormat()); + assertTrue(conf.isResponsesSigned()); + assertTrue(conf.isWantsAssertionsSigned()); + assertEquals(1, conf.getAttributeConsumingServiceIndex()); + assertEquals(2, conf.getAssertionConsumerServiceIndex()); + assertFalse(conf.isUseNameQualifier()); + assertTrue(conf.isSignServiceProviderMetadata()); + assertTrue(conf.isSignAuthnRequest()); + assertTrue(conf.isSignServiceProviderLogoutRequest()); + assertEquals(List.of("blocked"), conf.getBlockedSignatureSigningAlgorithms()); + assertEquals(List.of("signature"), conf.getSignatureAlgorithms()); + assertEquals(List.of("digest"), conf.getSignatureReferenceDigestMethods()); + assertEquals("canonicalization", conf.getSignatureCanonicalizationAlgorithm()); + assertEquals("providerName", conf.getProviderName()); + } + + @Test + void spnego() { + SpnegoAuthModuleConf conf = new SpnegoAuthModuleConf(); + SpnegoAuthModuleConf.LDAP ldap = new SpnegoAuthModuleConf.LDAP(); + + conf.setLoginConf("loginConf"); + conf.setKerberosConf("kerberosConf"); + conf.setKerberosKdc("kerberosKdc"); + conf.setJcifsServicePrincipal("jcifsServicePrincipal"); + conf.setKerberosRealm("kerberosRealm"); + conf.setKerberosDebug(true); + conf.setUseSubjectCredsOnly(true); + conf.setPrincipalWithDomainName(true); + conf.setNtlmAllowed(false); + conf.setSend401OnAuthenticationFailure(false); + conf.setHostNameClientActionStrategy("hostNameClientActionStrategy"); + conf.setDnsTimeout("PT1S"); + conf.setHostNamePatternString("hostNamePattern"); + conf.setIpsToCheckPattern("ips"); + conf.setAlternativeRemoteHostAttribute("alternative"); + conf.setSpnegoAttributeName("spnego"); + conf.setMixedModeAuthentication(true); + conf.setSupportedBrowsers("browsers"); + conf.setPoolSize(2); + conf.setPoolTimeout("PT2S"); + conf.setAttributeRepoId("attributeRepoId"); + conf.setLdap(ldap); + + assertEquals("loginConf", conf.getLoginConf()); + assertEquals("kerberosConf", conf.getKerberosConf()); + assertEquals("kerberosKdc", conf.getKerberosKdc()); + assertEquals("jcifsServicePrincipal", conf.getJcifsServicePrincipal()); + assertEquals("kerberosRealm", conf.getKerberosRealm()); + assertTrue(conf.isKerberosDebug()); + assertTrue(conf.isUseSubjectCredsOnly()); + assertTrue(conf.isPrincipalWithDomainName()); + assertFalse(conf.isNtlmAllowed()); + assertFalse(conf.isSend401OnAuthenticationFailure()); + assertEquals("hostNameClientActionStrategy", conf.getHostNameClientActionStrategy()); + assertEquals("PT1S", conf.getDnsTimeout()); + assertEquals("hostNamePattern", conf.getHostNamePatternString()); + assertEquals("ips", conf.getIpsToCheckPattern()); + assertEquals("alternative", conf.getAlternativeRemoteHostAttribute()); + assertEquals("spnego", conf.getSpnegoAttributeName()); + assertTrue(conf.isMixedModeAuthentication()); + assertEquals("browsers", conf.getSupportedBrowsers()); + assertEquals(2, conf.getPoolSize()); + assertEquals("PT2S", conf.getPoolTimeout()); + assertEquals("attributeRepoId", conf.getAttributeRepoId()); + assertSame(ldap, conf.getLdap()); + } + + @Test + void ldap() { + LDAPAuthModuleConf conf = new LDAPAuthModuleConf(); + + conf.setAuthenticationType(LDAPAuthModuleConf.AuthenticationType.DIRECT); + conf.setDnFormat("dnFormat"); + conf.setEnhanceWithEntryResolver(false); + conf.setDerefAliases(LDAPAuthModuleConf.DerefAliasesType.ALWAYS); + conf.setResolveFromAttribute("resolveFromAttribute"); + conf.setPrincipalAttributeId("principalAttributeId"); + conf.setPrincipalDnAttributeName("principalDnAttributeName"); + conf.setAllowMultiplePrincipalAttributeValues(true); + conf.getAdditionalAttributes().add("additional"); + conf.setAllowMissingPrincipalAttributeValue(false); + conf.setCollectDnAttribute(true); + conf.setCredentialCriteria("credentialCriteria"); + + assertEquals(LDAPAuthModuleConf.AuthenticationType.DIRECT, conf.getAuthenticationType()); + assertEquals("dnFormat", conf.getDnFormat()); + assertFalse(conf.isEnhanceWithEntryResolver()); + assertEquals(LDAPAuthModuleConf.DerefAliasesType.ALWAYS, conf.getDerefAliases()); + assertEquals("resolveFromAttribute", conf.getResolveFromAttribute()); + assertEquals("principalAttributeId", conf.getPrincipalAttributeId()); + assertEquals("principalDnAttributeName", conf.getPrincipalDnAttributeName()); + assertTrue(conf.isAllowMultiplePrincipalAttributeValues()); + assertEquals(List.of("additional"), conf.getAdditionalAttributes()); + assertFalse(conf.isAllowMissingPrincipalAttributeValue()); + assertTrue(conf.isCollectDnAttribute()); + assertEquals("credentialCriteria", conf.getCredentialCriteria()); + } + + @Test + void googleMfa() { + GoogleMfaAuthModuleConf conf = new GoogleMfaAuthModuleConf(); + GoogleMfaAuthModuleConf.LDAP ldap = new GoogleMfaAuthModuleConf.LDAP(); + + conf.setIssuer("issuer"); + conf.setLabel("label"); + conf.setCodeDigits(8); + conf.setTimeStepSize(60L); + conf.setWindowSize(4); + conf.setEnableCrypto(false); + conf.setCryptoAlgorithm(OIDCTokenEncryptionEncoding.A128GCM); + conf.setCryptoStrategy(GoogleMfaAuthModuleConf.CryptoStrategy.SIGN_AND_ENCRYPT); + conf.setSigningKeySize(1024); + conf.setSigningKey("signingKey"); + conf.setEncryptionKeySize(2048); + conf.setEncryptionKey("encryptionKey"); + conf.setLdap(ldap); + + assertEquals("issuer", conf.getIssuer()); + assertEquals("label", conf.getLabel()); + assertEquals(8, conf.getCodeDigits()); + assertEquals(60L, conf.getTimeStepSize()); + assertEquals(4, conf.getWindowSize()); + assertFalse(conf.isEnableCrypto()); + assertEquals(OIDCTokenEncryptionEncoding.A128GCM, conf.getCryptoAlgorithm()); + assertEquals(GoogleMfaAuthModuleConf.CryptoStrategy.SIGN_AND_ENCRYPT, conf.getCryptoStrategy()); + assertEquals(1024, conf.getSigningKeySize()); + assertEquals("signingKey", conf.getSigningKey()); + assertEquals(2048, conf.getEncryptionKeySize()); + assertEquals("encryptionKey", conf.getEncryptionKey()); + assertSame(ldap, conf.getLdap()); + } + + @Test + void smallerConfs() { + SimpleMfaAuthModuleConf simple = new SimpleMfaAuthModuleConf(); + simple.setTimeToKillInSeconds(10L); + simple.setTokenLength(8); + simple.setBypassGroovyScript("script"); + simple.setEmailAttribute("emailAttribute"); + simple.setEmailFrom("emailFrom"); + simple.setEmailSubject("emailSubject"); + simple.setEmailText("emailText"); + assertEquals(10L, simple.getTimeToKillInSeconds()); + assertEquals(8, simple.getTokenLength()); + assertEquals("script", simple.getBypassGroovyScript()); + assertEquals("emailAttribute", simple.getEmailAttribute()); + assertEquals("emailFrom", simple.getEmailFrom()); + assertEquals("emailSubject", simple.getEmailSubject()); + assertEquals("emailText", simple.getEmailText()); + + JaasAuthModuleConf jaas = new JaasAuthModuleConf(); + jaas.setRealm("realm"); + jaas.setKerberosRealmSystemProperty("realmProperty"); + jaas.setKerberosKdcSystemProperty("kdcProperty"); + jaas.setLoginConfigType("configType"); + jaas.setLoginConfigurationFile("configurationFile"); + jaas.setCredentialCriteria("credentialCriteria"); + assertEquals("realm", jaas.getRealm()); + assertEquals("realmProperty", jaas.getKerberosRealmSystemProperty()); + assertEquals("kdcProperty", jaas.getKerberosKdcSystemProperty()); + assertEquals("configType", jaas.getLoginConfigType()); + assertEquals("configurationFile", jaas.getLoginConfigurationFile()); + assertEquals("credentialCriteria", jaas.getCredentialCriteria()); + + OAuth20AuthModuleConf oauth20 = new OAuth20AuthModuleConf(); + oauth20.setAuthUrl("authUrl"); + oauth20.setProfileAttrs(Map.of("key", "value")); + oauth20.setWithState(true); + oauth20.setProfileUrl("profileUrl"); + oauth20.setProfileVerb("GET"); + assertEquals("authUrl", oauth20.getAuthUrl()); + assertEquals("value", oauth20.getProfileAttrs().get("key")); + assertTrue(oauth20.isWithState()); + assertEquals("profileUrl", oauth20.getProfileUrl()); + assertEquals("GET", oauth20.getProfileVerb()); + + AppleOIDCAuthModuleConf apple = new AppleOIDCAuthModuleConf(); + apple.setTimeout("PT1S"); + apple.setPrivateKey("privateKey"); + apple.setPrivateKeyId("privateKeyId"); + apple.setTeamId("teamId"); + assertEquals("PT1S", apple.getTimeout()); + assertEquals("privateKey", apple.getPrivateKey()); + assertEquals("privateKeyId", apple.getPrivateKeyId()); + assertEquals("teamId", apple.getTeamId()); + + JDBCAuthModuleConf jdbc = new JDBCAuthModuleConf(); + jdbc.setFieldPassword("password"); + jdbc.setFieldExpired("expired"); + jdbc.setFieldDisabled("disabled"); + jdbc.setCredentialCriteria("credentialCriteria"); + assertEquals("password", jdbc.getFieldPassword()); + assertEquals("expired", jdbc.getFieldExpired()); + assertEquals("disabled", jdbc.getFieldDisabled()); + assertEquals("credentialCriteria", jdbc.getCredentialCriteria()); + + SyncopeAuthModuleConf syncope = new SyncopeAuthModuleConf(); + syncope.setDomain("domain"); + syncope.setCredentialCriteria("credentialCriteria"); + syncope.setMaxRetryAttempts(2); + assertEquals("domain", syncope.getDomain()); + assertEquals("credentialCriteria", syncope.getCredentialCriteria()); + assertEquals(2, syncope.getMaxRetryAttempts()); + + DuoMfaAuthModuleConf duo = new DuoMfaAuthModuleConf(); + duo.setIntegrationKey("integrationKey"); + duo.setSecretKey("secretKey"); + duo.setApiHost("apiHost"); + assertEquals("integrationKey", duo.getIntegrationKey()); + assertEquals("secretKey", duo.getSecretKey()); + assertEquals("apiHost", duo.getApiHost()); + + OktaAuthModuleConf okta = new OktaAuthModuleConf(); + okta.setCredentialCriteria("credentialCriteria"); + assertEquals("credentialCriteria", okta.getCredentialCriteria()); + } +} diff --git a/common/am/lib/src/test/java/org/apache/syncope/common/lib/auth/AuthModuleConfTest.java b/common/am/lib/src/test/java/org/apache/syncope/common/lib/auth/AuthModuleConfTest.java new file mode 100644 index 00000000000..d5dbaa2781c --- /dev/null +++ b/common/am/lib/src/test/java/org/apache/syncope/common/lib/auth/AuthModuleConfTest.java @@ -0,0 +1,177 @@ +/* + * 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.common.lib.auth; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; +import org.apache.syncope.common.lib.to.AuthModuleTO; +import org.junit.jupiter.api.Test; + +class AuthModuleConfTest { + + private final AuthModuleTO authModule = new AuthModuleTO(); + + private final AuthModuleConf.Mapper mapper = mock(AuthModuleConf.Mapper.class); + + @Test + void simple() { + StaticAuthModuleConf staticConf = new StaticAuthModuleConf(); + Map staticMap = Map.of("static", staticConf); + when(mapper.map(authModule, staticConf)).thenReturn(staticMap); + assertSame(staticMap, staticConf.map(authModule, mapper)); + + JDBCAuthModuleConf jdbcConf = new JDBCAuthModuleConf(); + Map jdbcMap = Map.of("jdbc", jdbcConf); + when(mapper.map(authModule, jdbcConf)).thenReturn(jdbcMap); + assertSame(jdbcMap, jdbcConf.map(authModule, mapper)); + + JaasAuthModuleConf jaasConf = new JaasAuthModuleConf(); + Map jaasMap = Map.of("jaas", jaasConf); + when(mapper.map(authModule, jaasConf)).thenReturn(jaasMap); + assertSame(jaasMap, jaasConf.map(authModule, mapper)); + + SyncopeAuthModuleConf syncopeConf = new SyncopeAuthModuleConf(); + Map syncopeMap = Map.of("syncope", syncopeConf); + when(mapper.map(authModule, syncopeConf)).thenReturn(syncopeMap); + assertSame(syncopeMap, syncopeConf.map(authModule, mapper)); + + OktaAuthModuleConf oktaConf = new OktaAuthModuleConf(); + Map oktaMap = Map.of("okta", oktaConf); + when(mapper.map(authModule, oktaConf)).thenReturn(oktaMap); + assertSame(oktaMap, oktaConf.map(authModule, mapper)); + + DuoMfaAuthModuleConf duoConf = new DuoMfaAuthModuleConf(); + Map duoMap = Map.of("duo", duoConf); + when(mapper.map(authModule, duoConf)).thenReturn(duoMap); + assertSame(duoMap, duoConf.map(authModule, mapper)); + + SimpleMfaAuthModuleConf simpleConf = new SimpleMfaAuthModuleConf(); + Map simpleMap = Map.of("simple", simpleConf); + when(mapper.map(authModule, simpleConf)).thenReturn(simpleMap); + assertSame(simpleMap, simpleConf.map(authModule, mapper)); + + AzureActiveDirectoryAuthModuleConf azureConf = new AzureActiveDirectoryAuthModuleConf(); + Map azureMap = Map.of("azure", azureConf); + when(mapper.map(authModule, azureConf)).thenReturn(azureMap); + assertSame(azureMap, azureConf.map(authModule, mapper)); + + verify(mapper).map(authModule, staticConf); + verify(mapper).map(authModule, azureConf); + } + + @Test + void oidc() { + OAuth20AuthModuleConf oauth20Conf = new OAuth20AuthModuleConf(); + Map oauth20Map = Map.of("oauth20", oauth20Conf); + when(mapper.map(authModule, oauth20Conf)).thenReturn(oauth20Map); + assertSame(oauth20Map, oauth20Conf.map(authModule, mapper)); + + OIDCAuthModuleConf oidcConf = new OIDCAuthModuleConf(); + Map oidcMap = Map.of("oidc", oidcConf); + when(mapper.map(authModule, oidcConf)).thenReturn(oidcMap); + assertSame(oidcMap, oidcConf.map(authModule, mapper)); + + AzureOIDCAuthModuleConf azureConf = new AzureOIDCAuthModuleConf(); + Map azureMap = Map.of("azure-oidc", azureConf); + when(mapper.map(authModule, azureConf)).thenReturn(azureMap); + assertSame(azureMap, azureConf.map(authModule, mapper)); + + GoogleOIDCAuthModuleConf googleConf = new GoogleOIDCAuthModuleConf(); + Map googleMap = Map.of("google-oidc", googleConf); + when(mapper.map(authModule, googleConf)).thenReturn(googleMap); + assertSame(googleMap, googleConf.map(authModule, mapper)); + + KeycloakOIDCAuthModuleConf keycloakConf = new KeycloakOIDCAuthModuleConf(); + Map keycloakMap = Map.of("keycloak-oidc", keycloakConf); + when(mapper.map(authModule, keycloakConf)).thenReturn(keycloakMap); + assertSame(keycloakMap, keycloakConf.map(authModule, mapper)); + + AppleOIDCAuthModuleConf appleConf = new AppleOIDCAuthModuleConf(); + Map appleMap = Map.of("apple-oidc", appleConf); + when(mapper.map(authModule, appleConf)).thenReturn(appleMap); + assertSame(appleMap, appleConf.map(authModule, mapper)); + } + + @Test + void others() { + LDAPAuthModuleConf ldapConf = new LDAPAuthModuleConf(); + Map ldapMap = Map.of("ldap", ldapConf); + when(mapper.map(authModule, ldapConf)).thenReturn(ldapMap); + assertSame(ldapMap, ldapConf.map(authModule, mapper)); + + SAML2IdPAuthModuleConf saml2Conf = new SAML2IdPAuthModuleConf(); + Map saml2Map = Map.of("saml2", saml2Conf); + when(mapper.map(authModule, saml2Conf)).thenReturn(saml2Map); + assertSame(saml2Map, saml2Conf.map(authModule, mapper)); + + X509AuthModuleConf x509Conf = new X509AuthModuleConf(); + Map x509Map = Map.of("x509", x509Conf); + when(mapper.map(authModule, x509Conf)).thenReturn(x509Map); + assertSame(x509Map, x509Conf.map(authModule, mapper)); + + GoogleMfaAuthModuleConf googleConf = new GoogleMfaAuthModuleConf(); + Map googleMap = Map.of("google-mfa", googleConf); + when(mapper.map(authModule, googleConf)).thenReturn(googleMap); + assertSame(googleMap, googleConf.map(authModule, mapper)); + + SpnegoAuthModuleConf spnegoConf = new SpnegoAuthModuleConf(); + Map spnegoMap = Map.of("spnego", spnegoConf); + when(mapper.map(authModule, spnegoConf)).thenReturn(spnegoMap); + assertSame(spnegoMap, spnegoConf.map(authModule, mapper)); + } + + @Test + void ldapDependant() { + assertInstanceOf(GoogleMfaAuthModuleConf.LDAP.class, new GoogleMfaAuthModuleConf().ldapInstance()); + assertInstanceOf(X509AuthModuleConf.LDAP.class, new X509AuthModuleConf().ldapInstance()); + assertInstanceOf(SpnegoAuthModuleConf.LDAP.class, new SpnegoAuthModuleConf().ldapInstance()); + } + + @Test + void friendlyNames() { + assertEquals("Google Authenticator", new GoogleMfaAuthModuleConf().getFriendlyName()); + assertEquals("Duo Security", new DuoMfaAuthModuleConf().getFriendlyName()); + assertEquals("CAS Simple Multifactor Authentication", new SimpleMfaAuthModuleConf().getFriendlyName()); + } + + @Test + void ldapConfiguration() { + GoogleMfaAuthModuleConf.LDAP googleLdap = new GoogleMfaAuthModuleConf.LDAP(); + googleLdap.setAccountAttributeName("account"); + assertEquals("account", googleLdap.getAccountAttributeName()); + + X509AuthModuleConf.LDAP x509Ldap = new X509AuthModuleConf.LDAP(); + x509Ldap.setCertificateAttribute("certificate"); + assertEquals("certificate", x509Ldap.getCertificateAttribute()); + + X509AuthModuleConf x509 = new X509AuthModuleConf(); + x509.setName("x509"); + x509.setOrder(1); + x509.setLdap(x509Ldap); + assertEquals("x509", x509.getName()); + assertEquals(1, x509.getOrder()); + assertSame(x509Ldap, x509.getLdap()); + } +} diff --git a/common/am/lib/src/test/java/org/apache/syncope/common/lib/clientapps/UsernameAttributeProviderConfTest.java b/common/am/lib/src/test/java/org/apache/syncope/common/lib/clientapps/UsernameAttributeProviderConfTest.java new file mode 100644 index 00000000000..83dbc7232cb --- /dev/null +++ b/common/am/lib/src/test/java/org/apache/syncope/common/lib/clientapps/UsernameAttributeProviderConfTest.java @@ -0,0 +1,100 @@ +/* + * 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.common.lib.clientapps; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import org.apache.syncope.common.lib.types.CaseCanonicalizationMode; +import org.apache.syncope.common.lib.types.PersistentIdGenerator; +import org.junit.jupiter.api.Test; + +class UsernameAttributeProviderConfTest { + + private final UsernameAttributeProviderConf.Mapper mapper = mock(UsernameAttributeProviderConf.Mapper.class); + + @Test + void anonymous() { + AnonymousUsernameAttributeProviderConf conf = new AnonymousUsernameAttributeProviderConf(); + conf.map(mapper); + verify(mapper).map(conf); + } + + @Test + void defaultConf() { + DefaultUsernameAttributeProviderConf conf = new DefaultUsernameAttributeProviderConf(); + conf.map(mapper); + verify(mapper).map(conf); + } + + @Test + void groovy() { + GroovyUsernameAttributeProviderConf conf = new GroovyUsernameAttributeProviderConf(); + conf.setGroovyScript("groovy"); + conf.setCaseCanonicalizationMode(CaseCanonicalizationMode.LOWER); + + conf.map(mapper); + verify(mapper).map(conf); + assertEquals("groovy", conf.getGroovyScript()); + assertEquals(CaseCanonicalizationMode.LOWER, conf.getCaseCanonicalizationMode()); + } + + @Test + void pairwiseOidc() { + PairwiseOidcUsernameAttributeProviderConf conf = new PairwiseOidcUsernameAttributeProviderConf(); + conf.setPersistentIdGenerator(PersistentIdGenerator.SHIBBOLETH); + conf.setCaseCanonicalizationMode(CaseCanonicalizationMode.UPPER); + + conf.map(mapper); + verify(mapper).map(conf); + assertEquals(PersistentIdGenerator.SHIBBOLETH, conf.getPersistentIdGenerator()); + assertEquals(CaseCanonicalizationMode.UPPER, conf.getCaseCanonicalizationMode()); + } + + @Test + void principalAttribute() { + PrincipalAttributeUsernameAttributeProviderConf conf = + new PrincipalAttributeUsernameAttributeProviderConf(); + conf.setUsernameAttribute("email"); + conf.setCaseCanonicalizationMode(CaseCanonicalizationMode.NONE); + + conf.map(mapper); + verify(mapper).map(conf); + assertEquals("email", conf.getUsernameAttribute()); + } + + @Test + void equalsAndHashCode() { + PrincipalAttributeUsernameAttributeProviderConf first = new PrincipalAttributeUsernameAttributeProviderConf(); + first.setUsernameAttribute("email"); + PrincipalAttributeUsernameAttributeProviderConf second = new PrincipalAttributeUsernameAttributeProviderConf(); + second.setUsernameAttribute("email"); + PrincipalAttributeUsernameAttributeProviderConf other = new PrincipalAttributeUsernameAttributeProviderConf(); + other.setUsernameAttribute("uid"); + + assertEquals(first, first); + assertNotEquals(first, null); + assertNotEquals(first, "string"); + assertEquals(first, second); + assertEquals(first.hashCode(), second.hashCode()); + assertNotEquals(first, other); + } +} diff --git a/common/am/lib/src/test/java/org/apache/syncope/common/lib/password/PasswordManagementConfTest.java b/common/am/lib/src/test/java/org/apache/syncope/common/lib/password/PasswordManagementConfTest.java new file mode 100644 index 00000000000..f81bb6ac347 --- /dev/null +++ b/common/am/lib/src/test/java/org/apache/syncope/common/lib/password/PasswordManagementConfTest.java @@ -0,0 +1,88 @@ +/* + * 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.common.lib.password; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; +import org.apache.syncope.common.lib.to.PasswordManagementTO; +import org.junit.jupiter.api.Test; + +class PasswordManagementConfTest { + + private final PasswordManagementTO passwordManagementTO = new PasswordManagementTO(); + + private final PasswordManagementConf.Mapper mapper = mock(PasswordManagementConf.Mapper.class); + + @Test + void syncope() { + SyncopePasswordManagementConf conf = new SyncopePasswordManagementConf(); + conf.setSearchFilter("username==rossini"); + + Map expected = Map.of("syncope", conf); + when(mapper.map(passwordManagementTO, conf)).thenReturn(expected); + + assertSame(expected, conf.map(passwordManagementTO, mapper)); + verify(mapper).map(passwordManagementTO, conf); + assertEquals("username==rossini", conf.getSearchFilter()); + } + + @Test + void ldap() { + LDAPPasswordManagementConf conf = new LDAPPasswordManagementConf(); + conf.setSearchFilter("cn={user}"); + + Map expected = Map.of("ldap", conf); + when(mapper.map(passwordManagementTO, conf)).thenReturn(expected); + + assertSame(expected, conf.map(passwordManagementTO, mapper)); + verify(mapper).map(passwordManagementTO, conf); + assertEquals("cn={user}", conf.getSearchFilter()); + } + + @Test + void jdbc() { + JDBCPasswordManagementConf conf = new JDBCPasswordManagementConf(); + conf.setSql("SELECT 1"); + + Map expected = Map.of("jdbc", conf); + when(mapper.map(passwordManagementTO, conf)).thenReturn(expected); + + assertSame(expected, conf.map(passwordManagementTO, mapper)); + verify(mapper).map(passwordManagementTO, conf); + assertEquals("SELECT 1", conf.getSql()); + } + + @Test + void rest() { + RESTPasswordManagementConf conf = new RESTPasswordManagementConf(); + conf.setEndpointUrlUser("https://example.com"); + + Map expected = Map.of("rest", conf); + when(mapper.map(passwordManagementTO, conf)).thenReturn(expected); + + assertSame(expected, conf.map(passwordManagementTO, mapper)); + verify(mapper).map(passwordManagementTO, conf); + assertEquals("https://example.com", conf.getEndpointUrlUser()); + } +} diff --git a/common/am/lib/src/test/java/org/apache/syncope/common/lib/policy/AMPolicyConfsTest.java b/common/am/lib/src/test/java/org/apache/syncope/common/lib/policy/AMPolicyConfsTest.java new file mode 100644 index 00000000000..9fa407f7e9f --- /dev/null +++ b/common/am/lib/src/test/java/org/apache/syncope/common/lib/policy/AMPolicyConfsTest.java @@ -0,0 +1,217 @@ +/* + * 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.common.lib.policy; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.URI; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.concurrent.TimeUnit; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.junit.jupiter.api.Test; + +class AMPolicyConfsTest { + + @Test + void defaultAccessPolicyConf() { + DefaultAccessPolicyConf conf = new DefaultAccessPolicyConf(); + conf.setOrder(1); + conf.setEnabled(false); + conf.setSsoEnabled(false); + conf.setRequireAllAttributes(false); + conf.setCaseInsensitive(true); + conf.setUnauthorizedRedirectUrl(URI.create("https://syncope.apache.org")); + conf.getRequiredAttrs().put("cn", "admin"); + conf.getRejectedAttrs().put("uid", "plain"); + + assertEquals(1, conf.getOrder()); + assertFalse(conf.isEnabled()); + assertFalse(conf.isSsoEnabled()); + assertFalse(conf.isRequireAllAttributes()); + assertTrue(conf.isCaseInsensitive()); + assertEquals(URI.create("https://syncope.apache.org"), conf.getUnauthorizedRedirectUrl()); + assertEquals("admin", conf.getRequiredAttrs().get("cn")); + assertEquals("plain", conf.getRejectedAttrs().get("uid")); + } + + @Test + void defaultAttrReleasePolicyConf() { + DefaultAttrReleasePolicyConf conf = new DefaultAttrReleasePolicyConf(); + conf.setPrincipalIdAttr("principalIdAttr"); + conf.getReleaseAttrs().put("cn", "value"); + conf.getAllowedAttrs().add("allowed"); + conf.getExcludedAttrs().add("excluded"); + conf.getIncludeOnlyAttrs().add("includeOnly"); + + DefaultAttrReleasePolicyConf.PrincipalAttrRepoConf principalAttrRepoConf = + new DefaultAttrReleasePolicyConf.PrincipalAttrRepoConf(); + principalAttrRepoConf.setMergingStrategy( + DefaultAttrReleasePolicyConf.PrincipalAttrRepoMergingStrategy.ADD); + principalAttrRepoConf.setIgnoreResolvedAttributes(true); + principalAttrRepoConf.setExpiration(1L); + principalAttrRepoConf.setTimeUnit(TimeUnit.MINUTES); + principalAttrRepoConf.getAttrRepos().add("attrRepo"); + conf.setPrincipalAttrRepoConf(principalAttrRepoConf); + + assertEquals("principalIdAttr", conf.getPrincipalIdAttr()); + assertEquals("value", conf.getReleaseAttrs().get("cn")); + assertEquals(1, conf.getAllowedAttrs().size()); + assertEquals(1, conf.getExcludedAttrs().size()); + assertEquals(1, conf.getIncludeOnlyAttrs().size()); + assertSame(principalAttrRepoConf, conf.getPrincipalAttrRepoConf()); + + assertEquals(DefaultAttrReleasePolicyConf.PrincipalAttrRepoMergingStrategy.ADD, + principalAttrRepoConf.getMergingStrategy()); + assertTrue(principalAttrRepoConf.isIgnoreResolvedAttributes()); + assertEquals(1L, principalAttrRepoConf.getExpiration()); + assertEquals(TimeUnit.MINUTES, principalAttrRepoConf.getTimeUnit()); + assertEquals(1, principalAttrRepoConf.getAttrRepos().size()); + } + + @Test + void defaultAuthPolicyConf() { + DefaultAuthPolicyConf conf = new DefaultAuthPolicyConf(); + conf.setTryAll(true); + conf.setBypassEnabled(true); + conf.setForceMfaExecution(false); + conf.setBypassPrincipalAttributeName("name"); + conf.setBypassPrincipalAttributeValue("value"); + conf.setBypassTrustedDeviceEnabled(false); + conf.getAuthModules().add("module"); + + assertTrue(conf.isTryAll()); + assertTrue(conf.isBypassEnabled()); + assertFalse(conf.isForceMfaExecution()); + assertEquals("name", conf.getBypassPrincipalAttributeName()); + assertEquals("value", conf.getBypassPrincipalAttributeValue()); + assertFalse(conf.isBypassTrustedDeviceEnabled()); + assertEquals(1, conf.getAuthModules().size()); + } + + @Test + void defaultTicketExpirationPolicyConf() { + DefaultTicketExpirationPolicyConf conf = new DefaultTicketExpirationPolicyConf(); + + DefaultTicketExpirationPolicyConf.STConf stConf = new DefaultTicketExpirationPolicyConf.STConf(); + stConf.setMaxTimeToLiveInSeconds(10L); + stConf.setNumberOfUses(3L); + DefaultTicketExpirationPolicyConf.TGTConf tgtConf = new DefaultTicketExpirationPolicyConf.TGTConf(); + tgtConf.setMaxTimeToLiveInSeconds(20L); + DefaultTicketExpirationPolicyConf.STConf proxyStConf = new DefaultTicketExpirationPolicyConf.STConf(); + proxyStConf.setMaxTimeToLiveInSeconds(30L); + proxyStConf.setNumberOfUses(4L); + DefaultTicketExpirationPolicyConf.TGTConf proxyTgtConf = new DefaultTicketExpirationPolicyConf.TGTConf(); + proxyTgtConf.setMaxTimeToLiveInSeconds(40L); + + conf.setStConf(stConf); + conf.setTgtConf(tgtConf); + conf.setProxyStConf(proxyStConf); + conf.setProxyTgtConf(proxyTgtConf); + + assertSame(stConf, conf.getStConf()); + assertSame(tgtConf, conf.getTgtConf()); + assertSame(proxyStConf, conf.getProxyStConf()); + assertSame(proxyTgtConf, conf.getProxyTgtConf()); + assertEquals(10L, stConf.getMaxTimeToLiveInSeconds()); + assertEquals(3L, stConf.getNumberOfUses()); + assertEquals(20L, tgtConf.getMaxTimeToLiveInSeconds()); + assertEquals(4L, proxyStConf.getNumberOfUses()); + } + + @Test + void otherAccessPolicyConfs() { + HttpRequestAccessPolicyConf httpConf = new HttpRequestAccessPolicyConf(); + httpConf.setIpAddress("127.0.0.1"); + httpConf.setUserAgent("agent"); + assertEquals("127.0.0.1", httpConf.getIpAddress()); + assertEquals("agent", httpConf.getUserAgent()); + + OpenFGAAccessPolicyConf openFGAConf = new OpenFGAAccessPolicyConf(); + openFGAConf.setApiUrl("https://openfga"); + openFGAConf.setToken("token"); + openFGAConf.setStoreId("store"); + openFGAConf.setUserType(AnyTypeKind.GROUP.name()); + openFGAConf.setRelation("relation"); + openFGAConf.setObject("object"); + assertEquals("https://openfga", openFGAConf.getApiUrl()); + assertEquals("token", openFGAConf.getToken()); + assertEquals("store", openFGAConf.getStoreId()); + assertEquals(AnyTypeKind.GROUP.name(), openFGAConf.getUserType()); + assertEquals("relation", openFGAConf.getRelation()); + assertEquals("object", openFGAConf.getObject()); + + RemoteEndpointAccessPolicyConf remoteConf = new RemoteEndpointAccessPolicyConf(); + remoteConf.setEndpointUrl("https://endpoint"); + remoteConf.getAcceptableResponseCodes().add("200"); + assertEquals("https://endpoint", remoteConf.getEndpointUrl()); + assertEquals(1, remoteConf.getAcceptableResponseCodes().size()); + + TimeBasedAccessPolicyConf timeConf = new TimeBasedAccessPolicyConf(); + OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC); + timeConf.setStart(now); + timeConf.setEnd(now.plusHours(1)); + timeConf.setZoneId("UTC"); + assertEquals(now, timeConf.getStart()); + assertEquals(now.plusHours(1), timeConf.getEnd()); + assertEquals("UTC", timeConf.getZoneId()); + } + + @Test + void policyTOs() { + DefaultAccessPolicyConf accessConf = new DefaultAccessPolicyConf(); + AccessPolicyTO accessTO = new AccessPolicyTO(); + accessTO.setName("access"); + accessTO.setConf(accessConf); + assertEquals("access", accessTO.getName()); + assertEquals(AccessPolicyTO.class.getName(), accessTO.getDiscriminator()); + assertSame(accessConf, accessTO.getConf()); + + DefaultAttrReleasePolicyConf attrReleaseConf = new DefaultAttrReleasePolicyConf(); + AttrReleasePolicyTO attrReleaseTO = new AttrReleasePolicyTO(); + attrReleaseTO.setName("attrRelease"); + attrReleaseTO.setOrder(2); + attrReleaseTO.setStatus(false); + attrReleaseTO.setConf(attrReleaseConf); + assertEquals("attrRelease", attrReleaseTO.getName()); + assertEquals(AttrReleasePolicyTO.class.getName(), attrReleaseTO.getDiscriminator()); + assertEquals(2, attrReleaseTO.getOrder()); + assertFalse(attrReleaseTO.getStatus()); + assertSame(attrReleaseConf, attrReleaseTO.getConf()); + + DefaultAuthPolicyConf authConf = new DefaultAuthPolicyConf(); + AuthPolicyTO authTO = new AuthPolicyTO(); + authTO.setName("auth"); + authTO.setConf(authConf); + assertEquals("auth", authTO.getName()); + assertEquals(AuthPolicyTO.class.getName(), authTO.getDiscriminator()); + assertSame(authConf, authTO.getConf()); + + DefaultTicketExpirationPolicyConf ticketConf = new DefaultTicketExpirationPolicyConf(); + TicketExpirationPolicyTO ticketTO = new TicketExpirationPolicyTO(); + ticketTO.setName("ticket"); + ticketTO.setConf(ticketConf); + assertEquals("ticket", ticketTO.getName()); + assertEquals(TicketExpirationPolicyTO.class.getName(), ticketTO.getDiscriminator()); + assertSame(ticketConf, ticketTO.getConf()); + } +} diff --git a/common/am/lib/src/test/java/org/apache/syncope/common/lib/to/AMTOTest.java b/common/am/lib/src/test/java/org/apache/syncope/common/lib/to/AMTOTest.java new file mode 100644 index 00000000000..b18cde08ddd --- /dev/null +++ b/common/am/lib/src/test/java/org/apache/syncope/common/lib/to/AMTOTest.java @@ -0,0 +1,276 @@ +/* + * 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.common.lib.to; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Set; +import org.apache.syncope.common.lib.attr.StubAttrRepoConf; +import org.apache.syncope.common.lib.auth.StaticAuthModuleConf; +import org.apache.syncope.common.lib.password.SyncopePasswordManagementConf; +import org.apache.syncope.common.lib.types.AttrRepoState; +import org.apache.syncope.common.lib.types.AuthModuleState; +import org.apache.syncope.common.lib.wa.GoogleMfaAuthAccount; +import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken; +import org.apache.syncope.common.lib.wa.ImpersonationAccount; +import org.apache.syncope.common.lib.wa.MfaTrustedDevice; +import org.apache.syncope.common.lib.wa.WAConsentDecision; +import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential; +import org.junit.jupiter.api.Test; + +class AMTOTest { + + @Test + void authModuleTO() { + StaticAuthModuleConf conf = new StaticAuthModuleConf(); + AuthModuleTO module = new AuthModuleTO(); + module.setKey("key"); + module.setDescription("description"); + module.setState(AuthModuleState.STANDBY); + module.setOrder(3); + module.setConf(conf); + Item item = new Item(); + item.setIntAttrName("username"); + module.getItems().add(item); + + assertEquals("key", module.getKey()); + assertEquals("description", module.getDescription()); + assertEquals(AuthModuleState.STANDBY, module.getState()); + assertEquals(3, module.getOrder()); + assertSame(conf, module.getConf()); + assertEquals(1, module.getItems().size()); + + AuthModuleTO same = new AuthModuleTO(); + same.setKey("key"); + same.setDescription("description"); + same.setState(AuthModuleState.STANDBY); + same.setOrder(3); + same.setConf(conf); + same.getItems().add(item); + + AuthModuleTO other = new AuthModuleTO(); + other.setKey("other"); + + assertEquals(module, module); + assertNotEquals(module, null); + assertNotEquals(module, "string"); + assertEquals(module, same); + assertEquals(module.hashCode(), same.hashCode()); + assertNotEquals(module, other); + } + + @Test + void attrRepoTO() { + StubAttrRepoConf conf = new StubAttrRepoConf(); + AttrRepoTO attrRepo = new AttrRepoTO(); + attrRepo.setKey("key"); + attrRepo.setDescription("description"); + attrRepo.setState(AttrRepoState.DISABLED); + attrRepo.setOrder(3); + attrRepo.setConf(conf); + + assertEquals("key", attrRepo.getKey()); + assertEquals("description", attrRepo.getDescription()); + assertEquals(AttrRepoState.DISABLED, attrRepo.getState()); + assertEquals(3, attrRepo.getOrder()); + assertSame(conf, attrRepo.getConf()); + + AttrRepoTO same = new AttrRepoTO(); + same.setKey("key"); + same.setDescription("description"); + same.setState(AttrRepoState.DISABLED); + same.setOrder(3); + same.setConf(conf); + + AttrRepoTO other = new AttrRepoTO(); + other.setKey("other"); + + assertEquals(attrRepo, attrRepo); + assertNotEquals(attrRepo, null); + assertNotEquals(attrRepo, "string"); + assertEquals(attrRepo, same); + assertEquals(attrRepo.hashCode(), same.hashCode()); + assertNotEquals(attrRepo, other); + } + + @Test + void passwordManagementTO() { + SyncopePasswordManagementConf conf = new SyncopePasswordManagementConf(); + PasswordManagementTO passwordManagement = new PasswordManagementTO(); + passwordManagement.setKey("key"); + passwordManagement.setDescription("description"); + passwordManagement.setEnabled(true); + passwordManagement.setConf(conf); + + assertEquals("key", passwordManagement.getKey()); + assertEquals("description", passwordManagement.getDescription()); + assertTrue(passwordManagement.isEnabled()); + assertSame(conf, passwordManagement.getConf()); + + PasswordManagementTO same = new PasswordManagementTO(); + same.setKey("key"); + same.setDescription("description"); + same.setEnabled(true); + same.setConf(conf); + + PasswordManagementTO other = new PasswordManagementTO(); + other.setKey("other"); + + assertEquals(passwordManagement, passwordManagement); + assertNotEquals(passwordManagement, null); + assertNotEquals(passwordManagement, "string"); + assertEquals(passwordManagement, same); + assertEquals(passwordManagement.hashCode(), same.hashCode()); + assertNotEquals(passwordManagement, other); + } + + @Test + void authProfileTO() { + LocalDateTime createdDate = LocalDateTime.of(2026, 1, 1, 12, 0); + AuthProfileTO profile = new AuthProfileTO.Builder(). + key("key"). + owner("owner"). + impersonationAccount(new ImpersonationAccount.Builder().impersonated("one").build()). + impersonationAccounts(new ImpersonationAccount.Builder().impersonated("two").build()). + impersonationAccounts(List.of(new ImpersonationAccount.Builder().impersonated("three").build())). + googleMfaAuthToken(new GoogleMfaAuthToken.Builder().token(1).build()). + googleMfaAuthTokens(new GoogleMfaAuthToken.Builder().token(2).build()). + googleMfaAuthTokens(List.of(new GoogleMfaAuthToken.Builder().token(3).build())). + googleMfaAuthAccount(new GoogleMfaAuthAccount.Builder().name("one").build()). + googleMfaAuthAccounts(new GoogleMfaAuthAccount.Builder().name("two").build()). + googleMfaAuthAccounts(List.of(new GoogleMfaAuthAccount.Builder().name("three").build())). + mfaTrustedDevice(new MfaTrustedDevice()). + mfaTrustedDevices(new MfaTrustedDevice()). + mfaTrustedDevices(List.of(new MfaTrustedDevice())). + webAuthnDeviceCredential(new WebAuthnDeviceCredential.Builder().identifier("one").build()). + webAuthnDeviceCredentials(new WebAuthnDeviceCredential.Builder().identifier("two").build()). + webAuthnDeviceCredentials( + List.of(new WebAuthnDeviceCredential.Builder().identifier("three").build())). + consentDecision(new WAConsentDecision.Builder(1L, "principal", "service", createdDate).build()). + consentDecisions(new WAConsentDecision.Builder(2L, "principal", "service", createdDate).build()). + consentDecisions( + List.of(new WAConsentDecision.Builder(3L, "principal", "service", createdDate).build())). + build(); + + assertEquals("key", profile.getKey()); + assertEquals("owner", profile.getOwner()); + assertEquals("owner", profile.getName()); + assertThrows(UnsupportedOperationException.class, () -> profile.setName("name")); + assertEquals(3, profile.getImpersonationAccounts().size()); + assertEquals(3, profile.getGoogleMfaAuthTokens().size()); + assertEquals(3, profile.getGoogleMfaAuthAccounts().size()); + assertEquals(3, profile.getMfaTrustedDevices().size()); + assertEquals(3, profile.getWebAuthnDeviceCredentials().size()); + assertEquals(3, profile.getConsentDecisions().size()); + + AuthProfileTO same = new AuthProfileTO(); + same.setKey("key"); + same.setOwner("owner"); + same.getImpersonationAccounts().addAll(profile.getImpersonationAccounts()); + same.getGoogleMfaAuthTokens().addAll(profile.getGoogleMfaAuthTokens()); + same.getGoogleMfaAuthAccounts().addAll(profile.getGoogleMfaAuthAccounts()); + same.getMfaTrustedDevices().addAll(profile.getMfaTrustedDevices()); + same.getWebAuthnDeviceCredentials().addAll(profile.getWebAuthnDeviceCredentials()); + same.getConsentDecisions().addAll(profile.getConsentDecisions()); + + AuthProfileTO other = new AuthProfileTO(); + other.setKey("other"); + other.setOwner("owner"); + + assertEquals(profile, profile); + assertNotEquals(profile, null); + assertNotEquals(profile, "string"); + assertEquals(profile, same); + assertEquals(profile.hashCode(), same.hashCode()); + assertNotEquals(profile, other); + } + + @Test + void oidcOpEntityTO() { + OIDCOpEntityTO entity = new OIDCOpEntityTO(); + entity.setKey("key"); + entity.setJWKS("jwks"); + entity.getCustomScopes().put("scope", Set.of("value")); + + assertEquals("key", entity.getKey()); + assertEquals("jwks", entity.getJWKS()); + assertEquals(Set.of("value"), entity.getCustomScopes().get("scope")); + assertTrue(entity.toString().contains("jwks")); + + OIDCOpEntityTO same = new OIDCOpEntityTO(); + same.setKey("key"); + same.setJWKS("jwks"); + same.getCustomScopes().put("scope", Set.of("value")); + + OIDCOpEntityTO other = new OIDCOpEntityTO(); + other.setKey("other"); + + assertEquals(entity, entity); + assertNotEquals(entity, null); + assertNotEquals(entity, "string"); + assertNotEquals(entity, same); + assertNotEquals(entity, other); + } + + @Test + void saml2IdPEntityTO() { + SAML2IdPEntityTO entity = new SAML2IdPEntityTO.Builder(). + key("key"). + metadata("metadata"). + signingCertificate("signingCertificate"). + signingKey("signingKey"). + encryptionCertificate("encryptionCertificate"). + encryptionKey("encryptionKey"). + build(); + + assertEquals("key", entity.getKey()); + assertEquals("metadata", entity.getMetadata()); + assertEquals("signingCertificate", entity.getSigningCertificate()); + assertEquals("signingKey", entity.getSigningKey()); + assertEquals("encryptionCertificate", entity.getEncryptionCertificate()); + assertEquals("encryptionKey", entity.getEncryptionKey()); + assertTrue(entity.toString().contains("key")); + + SAML2IdPEntityTO same = new SAML2IdPEntityTO.Builder(). + key("key"). + metadata("metadata"). + signingCertificate("signingCertificate"). + signingKey("signingKey"). + encryptionCertificate("encryptionCertificate"). + encryptionKey("encryptionKey"). + build(); + + SAML2IdPEntityTO other = new SAML2IdPEntityTO.Builder(). + key("other"). + build(); + + assertEquals(entity, entity); + assertNotEquals(entity, null); + assertNotEquals(entity, "string"); + assertEquals(entity, same); + assertEquals(entity.hashCode(), same.hashCode()); + assertNotEquals(entity, other); + } +} diff --git a/common/am/lib/src/test/java/org/apache/syncope/common/lib/to/ClientAppTOTest.java b/common/am/lib/src/test/java/org/apache/syncope/common/lib/to/ClientAppTOTest.java new file mode 100644 index 00000000000..5ad7bae838d --- /dev/null +++ b/common/am/lib/src/test/java/org/apache/syncope/common/lib/to/ClientAppTOTest.java @@ -0,0 +1,313 @@ +/* + * 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.common.lib.to; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.apache.syncope.common.lib.Attr; +import org.apache.syncope.common.lib.clientapps.AnonymousUsernameAttributeProviderConf; +import org.apache.syncope.common.lib.clientapps.UsernameAttributeProviderConf; +import org.apache.syncope.common.lib.types.LogoutType; +import org.apache.syncope.common.lib.types.MetadataCriteriaDirection; +import org.apache.syncope.common.lib.types.OIDCApplicationType; +import org.apache.syncope.common.lib.types.OIDCClientAuthenticationMethod; +import org.apache.syncope.common.lib.types.OIDCGrantType; +import org.apache.syncope.common.lib.types.OIDCResponseType; +import org.apache.syncope.common.lib.types.OIDCSubjectType; +import org.apache.syncope.common.lib.types.OIDCTokenEncryptionAlg; +import org.apache.syncope.common.lib.types.OIDCTokenEncryptionEncoding; +import org.apache.syncope.common.lib.types.OIDCTokenSigningAlg; +import org.apache.syncope.common.lib.types.SAML2BindingType; +import org.apache.syncope.common.lib.types.SAML2SPNameId; +import org.apache.syncope.common.lib.types.SigningCredentialType; +import org.apache.syncope.common.lib.types.XmlSecAlgorithm; +import org.junit.jupiter.api.Test; + +class ClientAppTOTest { + + private void fill(final ClientAppTO clientApp) { + UsernameAttributeProviderConf provider = new AnonymousUsernameAttributeProviderConf(); + clientApp.setKey("key"); + clientApp.setRealm("realm"); + clientApp.setName("name"); + clientApp.setClientAppId(1L); + clientApp.setEvaluationOrder(2); + clientApp.setDescription("description"); + clientApp.setLogo("logo"); + clientApp.setTheme("theme"); + clientApp.setInformationUrl("informationUrl"); + clientApp.setPrivacyUrl("privacyUrl"); + clientApp.setUsernameAttributeProviderConf(provider); + clientApp.setAuthPolicy("authPolicy"); + clientApp.setAccessPolicy("accessPolicy"); + clientApp.setAttrReleasePolicy("attrReleasePolicy"); + clientApp.setTicketExpirationPolicy("ticketExpirationPolicy"); + clientApp.setLogoutType(LogoutType.FRONT_CHANNEL); + Attr property = new Attr(); + property.setSchema("schema"); + clientApp.getProperties().add(property); + } + + private void assertFilled(final ClientAppTO clientApp) { + assertEquals("key", clientApp.getKey()); + assertEquals("realm", clientApp.getRealm()); + assertEquals("name", clientApp.getName()); + assertEquals(1L, clientApp.getClientAppId()); + assertEquals(2, clientApp.getEvaluationOrder()); + assertEquals("description", clientApp.getDescription()); + assertEquals("logo", clientApp.getLogo()); + assertEquals("theme", clientApp.getTheme()); + assertEquals("informationUrl", clientApp.getInformationUrl()); + assertEquals("privacyUrl", clientApp.getPrivacyUrl()); + assertTrue(clientApp.getUsernameAttributeProviderConf() instanceof AnonymousUsernameAttributeProviderConf); + assertEquals("authPolicy", clientApp.getAuthPolicy()); + assertEquals("accessPolicy", clientApp.getAccessPolicy()); + assertEquals("attrReleasePolicy", clientApp.getAttrReleasePolicy()); + assertEquals("ticketExpirationPolicy", clientApp.getTicketExpirationPolicy()); + assertEquals(1, clientApp.getProperties().size()); + assertEquals(LogoutType.FRONT_CHANNEL, clientApp.getLogoutType()); + assertEquals(clientApp.getClass().getName(), clientApp.getDiscriminator()); + } + + @Test + void casSP() { + CASSPClientAppTO clientApp = new CASSPClientAppTO(); + fill(clientApp); + clientApp.setServiceId("serviceId"); + clientApp.setDiscriminator("ignored"); + + assertFilled(clientApp); + assertEquals("serviceId", clientApp.getServiceId()); + assertEquals(clientApp, clientApp); + assertNotEquals(clientApp, null); + assertNotEquals(clientApp, "string"); + assertNotEquals(clientApp, new SAML2SPClientAppTO()); + assertTrue(clientApp.hashCode() != 0); + } + + @Test + void oidcRP() { + OIDCRPClientAppTO clientApp = new OIDCRPClientAppTO(); + fill(clientApp); + + clientApp.setClientId("clientId"); + clientApp.setClientSecret("clientSecret"); + clientApp.setClientSecretExpiration(1L); + clientApp.setIdTokenIssuer("issuer"); + clientApp.setSignIdToken(false); + clientApp.setIdTokenSigningAlg(OIDCTokenSigningAlg.RS256); + clientApp.setEncryptIdToken(true); + clientApp.setIdTokenEncryptionAlg(OIDCTokenEncryptionAlg.RSA_OAEP); + clientApp.setIdTokenEncryptionEncoding(OIDCTokenEncryptionEncoding.A256CBC_HS512); + clientApp.setUserInfoSigningAlg(OIDCTokenSigningAlg.ES256); + clientApp.setUserInfoEncryptedResponseAlg(OIDCTokenEncryptionAlg.A128KW); + clientApp.setUserInfoEncryptedResponseEncoding(OIDCTokenEncryptionEncoding.A128GCM); + clientApp.setJwtAccessToken(true); + clientApp.setBypassApprovalPrompt(false); + clientApp.setGenerateRefreshToken(false); + clientApp.setSubjectType(OIDCSubjectType.PAIRWISE); + clientApp.setApplicationType(OIDCApplicationType.NATIVE); + clientApp.setJwks("jwks"); + clientApp.setJwksUri("jwksUri"); + clientApp.setTokenEndpointAuthenticationMethod(OIDCClientAuthenticationMethod.private_key_jwt); + clientApp.setLogoutUri("logoutUri"); + clientApp.setAccessTokenMaxTimeToLive("accessTokenMaxTimeToLive"); + clientApp.setAccessTokenTimeToKill("accessTokenTimeToKill"); + clientApp.setAccessTokenMaxActiveTokens(3L); + clientApp.setRefreshTokenTimeToKill("refreshTokenTimeToKill"); + clientApp.setRefreshTokenMaxActiveTokens(4L); + clientApp.setDeviceTokenTimeToKill("deviceTokenTimeToKill"); + clientApp.setJwtAccessTokenSigningAlg(OIDCTokenSigningAlg.HS256); + clientApp.setRenewRefreshToken(true); + clientApp.setIdTokenTimeToKill("idTokenTimeToKill"); + clientApp.setAuthorizationCodeTimeToLive("authorizationCodeTimeToLive"); + clientApp.getRedirectUris().add("redirectUri"); + clientApp.getSupportedGrantTypes().add(OIDCGrantType.authorization_code); + clientApp.getSupportedResponseTypes().add(OIDCResponseType.CODE); + clientApp.getScopes().add("openid"); + clientApp.getAudience().add("audience"); + + assertFilled(clientApp); + assertEquals("clientId", clientApp.getClientId()); + assertEquals("clientSecret", clientApp.getClientSecret()); + assertEquals(1L, clientApp.getClientSecretExpiration()); + assertEquals("issuer", clientApp.getIdTokenIssuer()); + assertFalse(clientApp.isSignIdToken()); + assertEquals(OIDCTokenSigningAlg.RS256, clientApp.getIdTokenSigningAlg()); + assertTrue(clientApp.isEncryptIdToken()); + assertEquals(OIDCTokenEncryptionAlg.RSA_OAEP, clientApp.getIdTokenEncryptionAlg()); + assertEquals(OIDCTokenEncryptionEncoding.A256CBC_HS512, clientApp.getIdTokenEncryptionEncoding()); + assertEquals(OIDCTokenSigningAlg.ES256, clientApp.getUserInfoSigningAlg()); + assertEquals(OIDCTokenEncryptionAlg.A128KW, clientApp.getUserInfoEncryptedResponseAlg()); + assertEquals(OIDCTokenEncryptionEncoding.A128GCM, clientApp.getUserInfoEncryptedResponseEncoding()); + assertTrue(clientApp.isJwtAccessToken()); + assertFalse(clientApp.isBypassApprovalPrompt()); + assertFalse(clientApp.isGenerateRefreshToken()); + assertEquals(OIDCSubjectType.PAIRWISE, clientApp.getSubjectType()); + assertEquals(OIDCApplicationType.NATIVE, clientApp.getApplicationType()); + assertEquals("jwks", clientApp.getJwks()); + assertEquals("jwksUri", clientApp.getJwksUri()); + assertEquals(OIDCClientAuthenticationMethod.private_key_jwt, + clientApp.getTokenEndpointAuthenticationMethod()); + assertEquals("logoutUri", clientApp.getLogoutUri()); + assertEquals("accessTokenMaxTimeToLive", clientApp.getAccessTokenMaxTimeToLive()); + assertEquals("accessTokenTimeToKill", clientApp.getAccessTokenTimeToKill()); + assertEquals(3L, clientApp.getAccessTokenMaxActiveTokens()); + assertEquals("refreshTokenTimeToKill", clientApp.getRefreshTokenTimeToKill()); + assertEquals(4L, clientApp.getRefreshTokenMaxActiveTokens()); + assertEquals("deviceTokenTimeToKill", clientApp.getDeviceTokenTimeToKill()); + assertEquals(OIDCTokenSigningAlg.HS256, clientApp.getJwtAccessTokenSigningAlg()); + assertTrue(clientApp.getRenewRefreshToken()); + assertEquals("idTokenTimeToKill", clientApp.getIdTokenTimeToKill()); + assertEquals("authorizationCodeTimeToLive", clientApp.getAuthorizationCodeTimeToLive()); + assertEquals(List.of("redirectUri"), clientApp.getRedirectUris()); + assertEquals(List.of(OIDCGrantType.authorization_code), clientApp.getSupportedGrantTypes()); + assertEquals(List.of(OIDCResponseType.CODE), clientApp.getSupportedResponseTypes()); + assertEquals(List.of("openid"), clientApp.getScopes()); + assertEquals(List.of("audience"), clientApp.getAudience()); + + assertEquals(clientApp, clientApp); + assertNotEquals(clientApp, null); + assertNotEquals(clientApp, "string"); + assertNotEquals(clientApp, new SAML2SPClientAppTO()); + assertTrue(clientApp.hashCode() != 0); + } + + @Test + void saml2SP() { + SAML2SPClientAppTO clientApp = new SAML2SPClientAppTO(); + fill(clientApp); + + clientApp.setEntityId("entityId"); + clientApp.setIdp("idp"); + clientApp.setMetadataLocation("metadataLocation"); + clientApp.setMetadataSignatureLocation("metadataSignatureLocation"); + clientApp.setMetadataCriteriaPattern("metadataCriteriaPattern"); + clientApp.setMetadataCriteriaDirection(MetadataCriteriaDirection.EXCLUDE); + clientApp.setSubjectLocality("subjectLocality"); + clientApp.setSigningCredentialType(SigningCredentialType.BASIC); + clientApp.setLogoutResponseBinding(SAML2BindingType.POST); + clientApp.setSignAssertions(true); + clientApp.setSignResponses(true); + clientApp.setEncryptionOptional(true); + clientApp.setEncryptAssertions(true); + clientApp.setRequireSignedRoot(false); + clientApp.setLogoutResponseEnabled(false); + clientApp.setEncryptAttributes(true); + clientApp.setSkipGeneratingAssertionNameId(true); + clientApp.setSkipGeneratingSubjectConfirmationInResponseTo(true); + clientApp.setSkipGeneratingResponseInResponseTo(true); + clientApp.setSkipGeneratingSubjectConfirmationNotOnOrAfter(true); + clientApp.setSkipGeneratingSubjectConfirmationRecipient(true); + clientApp.setSkipGeneratingSubjectConfirmationAddress(true); + clientApp.setSkipGeneratingSubjectConfirmationNotBefore(false); + clientApp.setSkipGeneratingSubjectConfirmationNameId(false); + clientApp.setSkipGeneratingNameIdQualifiers(true); + clientApp.setSkipGeneratingTransientNameId(true); + clientApp.setSkipValidatingAuthnRequest(true); + clientApp.setSkipGeneratingServiceProviderNameIdQualifier(true); + clientApp.setSkipGeneratingAuthenticatingAuthority(true); + clientApp.setSkipGeneratingNameIdQualifier(true); + clientApp.setSkipGeneratingSessionNotOnOrAfter(true); + clientApp.setValidateMetadataCertificates(true); + clientApp.setRequiredAuthenticationContextClass("requiredAuthenticationContextClass"); + clientApp.setRequiredNameIdFormat(SAML2SPNameId.EMAIL_ADDRESS); + clientApp.setSkewAllowance("skewAllowance"); + clientApp.setValidityUntil("validityUntil"); + clientApp.setNameIdQualifier("nameIdQualifier"); + clientApp.setServiceProviderNameIdQualifier("serviceProviderNameIdQualifier"); + clientApp.getAssertionAudiences().add("audience"); + clientApp.getSigningSignatureAlgorithms().add(XmlSecAlgorithm.SHA256); + clientApp.getSigningSignatureReferenceDigestMethods().add(XmlSecAlgorithm.SHA1); + clientApp.getEncryptionDataAlgorithms().add(XmlSecAlgorithm.AES_128_GCM); + clientApp.getEncryptionKeyAlgorithms().add(XmlSecAlgorithm.AES_128_KeyWrap); + clientApp.getSigningSignatureBlackListedAlgorithms().add(XmlSecAlgorithm.SHA512); + clientApp.getEncryptionBlackListedAlgorithms().add(XmlSecAlgorithm.AES_256_GCM); + + assertFilled(clientApp); + assertEquals("entityId", clientApp.getEntityId()); + assertEquals("idp", clientApp.getIdp()); + assertEquals("metadataLocation", clientApp.getMetadataLocation()); + assertEquals("metadataSignatureLocation", clientApp.getMetadataSignatureLocation()); + assertEquals("metadataCriteriaPattern", clientApp.getMetadataCriteriaPattern()); + assertEquals(MetadataCriteriaDirection.EXCLUDE, clientApp.getMetadataCriteriaDirection()); + assertEquals("subjectLocality", clientApp.getSubjectLocality()); + assertEquals(SigningCredentialType.BASIC, clientApp.getSigningCredentialType()); + assertEquals(SAML2BindingType.POST, clientApp.getLogoutResponseBinding()); + assertTrue(clientApp.isSignAssertions()); + assertTrue(clientApp.isSignResponses()); + assertTrue(clientApp.isEncryptionOptional()); + assertTrue(clientApp.isEncryptAssertions()); + assertFalse(clientApp.isRequireSignedRoot()); + assertFalse(clientApp.isLogoutResponseEnabled()); + assertTrue(clientApp.isEncryptAttributes()); + assertTrue(clientApp.isSkipGeneratingAssertionNameId()); + assertTrue(clientApp.isSkipGeneratingSubjectConfirmationInResponseTo()); + assertTrue(clientApp.isSkipGeneratingResponseInResponseTo()); + assertTrue(clientApp.isSkipGeneratingSubjectConfirmationNotOnOrAfter()); + assertTrue(clientApp.isSkipGeneratingSubjectConfirmationRecipient()); + assertTrue(clientApp.isSkipGeneratingSubjectConfirmationAddress()); + assertFalse(clientApp.isSkipGeneratingSubjectConfirmationNotBefore()); + assertFalse(clientApp.isSkipGeneratingSubjectConfirmationNameId()); + assertTrue(clientApp.isSkipGeneratingNameIdQualifiers()); + assertTrue(clientApp.isSkipGeneratingTransientNameId()); + assertTrue(clientApp.isSkipValidatingAuthnRequest()); + assertTrue(clientApp.isSkipGeneratingServiceProviderNameIdQualifier()); + assertTrue(clientApp.isSkipGeneratingAuthenticatingAuthority()); + assertTrue(clientApp.isSkipGeneratingNameIdQualifier()); + assertTrue(clientApp.isSkipGeneratingSessionNotOnOrAfter()); + assertTrue(clientApp.isValidateMetadataCertificates()); + assertEquals("requiredAuthenticationContextClass", clientApp.getRequiredAuthenticationContextClass()); + assertEquals(SAML2SPNameId.EMAIL_ADDRESS, clientApp.getRequiredNameIdFormat()); + assertEquals("skewAllowance", clientApp.getSkewAllowance()); + assertEquals("validityUntil", clientApp.getValidityUntil()); + assertEquals("nameIdQualifier", clientApp.getNameIdQualifier()); + assertEquals("serviceProviderNameIdQualifier", clientApp.getServiceProviderNameIdQualifier()); + assertEquals(List.of("audience"), clientApp.getAssertionAudiences()); + assertEquals(List.of(XmlSecAlgorithm.SHA256), clientApp.getSigningSignatureAlgorithms()); + assertEquals(List.of(XmlSecAlgorithm.SHA1), clientApp.getSigningSignatureReferenceDigestMethods()); + assertEquals(List.of(XmlSecAlgorithm.AES_128_GCM), clientApp.getEncryptionDataAlgorithms()); + assertEquals(List.of(XmlSecAlgorithm.AES_128_KeyWrap), clientApp.getEncryptionKeyAlgorithms()); + assertEquals(List.of(XmlSecAlgorithm.SHA512), clientApp.getSigningSignatureBlackListedAlgorithms()); + assertEquals(List.of(XmlSecAlgorithm.AES_256_GCM), clientApp.getEncryptionBlackListedAlgorithms()); + + assertEquals(clientApp, clientApp); + assertNotEquals(clientApp, null); + assertNotEquals(clientApp, "string"); + assertNotEquals(clientApp, new OIDCRPClientAppTO()); + assertTrue(clientApp.hashCode() != 0); + } + + @Test + void propertyHolder() { + ClientAppTO clientApp = new CASSPClientAppTO(); + Attr attr = new Attr(); + attr.setSchema("schema"); + attr.getValues().add("value"); + clientApp.getProperties().add(attr); + + assertSame(attr, clientApp.getProperties().get(0)); + assertEquals("value", clientApp.getProperties().get(0).getValues().get(0)); + } +} diff --git a/common/am/lib/src/test/java/org/apache/syncope/common/lib/types/AMEntitlementTest.java b/common/am/lib/src/test/java/org/apache/syncope/common/lib/types/AMEntitlementTest.java new file mode 100644 index 00000000000..08c208a8298 --- /dev/null +++ b/common/am/lib/src/test/java/org/apache/syncope/common/lib/types/AMEntitlementTest.java @@ -0,0 +1,54 @@ +/* + * 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.common.lib.types; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Set; +import org.junit.jupiter.api.Test; + +class AMEntitlementTest { + + @Test + void values() { + Set values = AMEntitlement.values(); + + assertEquals(40, values.size()); + assertTrue(values.contains(AMEntitlement.CLIENTAPP_READ)); + assertTrue(values.contains(AMEntitlement.AUTH_MODULE_CREATE)); + assertTrue(values.contains(AMEntitlement.ATTR_REPO_UPDATE)); + assertTrue(values.contains(AMEntitlement.PASSWORD_MANAGEMENT_DELETE)); + assertTrue(values.contains(AMEntitlement.SAML2_IDP_ENTITY_GET)); + assertTrue(values.contains(AMEntitlement.AUTH_PROFILE_LIST)); + assertTrue(values.contains(AMEntitlement.OIDC_JWKS_GENERATE)); + assertTrue(values.contains(AMEntitlement.WA_CONFIG_PUSH)); + assertTrue(values.contains(AMEntitlement.WA_SESSION_DELETE)); + assertFalse(values.contains("CLIENTAPP_READ_UNKNOWN")); + } + + @Test + void valuesAreUnmodifiableAndStable() { + assertSame(AMEntitlement.values(), AMEntitlement.values()); + assertThrows(UnsupportedOperationException.class, () -> AMEntitlement.values().add("__test__")); + } +} diff --git a/common/am/lib/src/test/java/org/apache/syncope/common/lib/types/XmlSecAlgorithmTest.java b/common/am/lib/src/test/java/org/apache/syncope/common/lib/types/XmlSecAlgorithmTest.java new file mode 100644 index 00000000000..0c1ac438bbb --- /dev/null +++ b/common/am/lib/src/test/java/org/apache/syncope/common/lib/types/XmlSecAlgorithmTest.java @@ -0,0 +1,58 @@ +/* + * 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.common.lib.types; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashSet; +import java.util.Set; +import org.junit.jupiter.api.Test; + +class XmlSecAlgorithmTest { + + @Test + void algorithms() { + assertEquals("http://www.w3.org/2001/04/xmlenc#tripledes-cbc", XmlSecAlgorithm.TRIPLEDES.getAlgorithm()); + assertEquals("http://www.w3.org/2001/04/xmlenc#aes128-cbc", XmlSecAlgorithm.AES_128.getAlgorithm()); + assertEquals("http://www.w3.org/2001/04/xmlenc#aes192-cbc", XmlSecAlgorithm.AES_192.getAlgorithm()); + assertEquals("http://www.w3.org/2001/04/xmlenc#aes256-cbc", XmlSecAlgorithm.AES_256.getAlgorithm()); + assertEquals("http://www.w3.org/2009/xmlenc11#aes128-gcm", XmlSecAlgorithm.AES_128_GCM.getAlgorithm()); + assertEquals("http://www.w3.org/2001/04/xmlenc#rsa-1_5", XmlSecAlgorithm.RSA_v1dot5.getAlgorithm()); + assertEquals("http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p", XmlSecAlgorithm.RSA_OAEP.getAlgorithm()); + assertEquals("http://www.w3.org/2009/xmlenc11#rsa-oaep", XmlSecAlgorithm.RSA_OAEP_11.getAlgorithm()); + assertEquals("http://www.w3.org/2001/04/xmlenc#kw-aes128", XmlSecAlgorithm.AES_128_KeyWrap.getAlgorithm()); + assertEquals("http://www.w3.org/2000/09/xmldsig#sha1", XmlSecAlgorithm.SHA1.getAlgorithm()); + assertEquals("http://www.w3.org/2001/04/xmlenc#sha256", XmlSecAlgorithm.SHA256.getAlgorithm()); + assertEquals("http://www.w3.org/2001/04/xmlenc#sha512", XmlSecAlgorithm.SHA512.getAlgorithm()); + assertEquals("http://www.w3.org/2001/04/xmlenc#ripemd160", XmlSecAlgorithm.RIPEMD_160.getAlgorithm()); + } + + @Test + void allAlgorithmsDefined() { + Set algorithms = new HashSet<>(); + + for (XmlSecAlgorithm algorithm : XmlSecAlgorithm.values()) { + assertTrue(algorithm.getAlgorithm().startsWith("http://")); + algorithms.add(algorithm.getAlgorithm()); + } + + assertEquals(XmlSecAlgorithm.values().length, algorithms.size()); + } +} diff --git a/common/am/lib/src/test/java/org/apache/syncope/common/lib/wa/WABeansTest.java b/common/am/lib/src/test/java/org/apache/syncope/common/lib/wa/WABeansTest.java new file mode 100644 index 00000000000..d4b4f70b471 --- /dev/null +++ b/common/am/lib/src/test/java/org/apache/syncope/common/lib/wa/WABeansTest.java @@ -0,0 +1,262 @@ +/* + * 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.common.lib.wa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; +import org.junit.jupiter.api.Test; + +class WABeansTest { + + @Test + void consentDecision() { + LocalDateTime createdDate = LocalDateTime.of(2026, 1, 1, 12, 0); + WAConsentDecision decision = new WAConsentDecision.Builder(1L, "principal", "service", createdDate). + options(WAConsentDecision.ReminderOptions.ATTRIBUTE_VALUE). + reminder(2L). + reminderTimeUnit(ChronoUnit.HOURS). + attributes("attributes"). + build(); + + assertEquals(1L, decision.getId()); + assertEquals("principal", decision.getPrincipal()); + assertEquals("service", decision.getService()); + assertEquals(createdDate, decision.getCreatedDate()); + assertEquals(WAConsentDecision.ReminderOptions.ATTRIBUTE_VALUE, decision.getOptions()); + assertEquals(2L, decision.getReminder()); + assertEquals(ChronoUnit.HOURS, decision.getReminderTimeUnit()); + assertEquals("attributes", decision.getAttributes()); + assertTrue(decision.toString().contains("principal")); + + WAConsentDecision same = new WAConsentDecision.Builder(1L, "principal", "service", createdDate). + options(WAConsentDecision.ReminderOptions.ATTRIBUTE_VALUE). + reminder(2L). + reminderTimeUnit(ChronoUnit.HOURS). + attributes("attributes"). + build(); + WAConsentDecision other = new WAConsentDecision.Builder(2L, "principal", "service", createdDate).build(); + + assertEquals(decision, decision); + assertNotEquals(decision, null); + assertNotEquals(decision, "string"); + assertEquals(decision, same); + assertEquals(decision.hashCode(), same.hashCode()); + assertNotEquals(decision, other); + } + + @Test + void reminderOptions() { + assertEquals(0, WAConsentDecision.ReminderOptions.ALWAYS.getValue()); + assertEquals(1, WAConsentDecision.ReminderOptions.ATTRIBUTE_NAME.getValue()); + assertEquals(2, WAConsentDecision.ReminderOptions.ATTRIBUTE_VALUE.getValue()); + } + + @Test + void webAuthnAccount() { + WebAuthnDeviceCredential first = new WebAuthnDeviceCredential.Builder(). + identifier("first").json("{}").build(); + WebAuthnDeviceCredential second = new WebAuthnDeviceCredential.Builder(). + identifier("second").json("{}").build(); + + WebAuthnAccount account = new WebAuthnAccount.Builder(). + credential(first). + credentials(second). + credentials(List.of(first)). + build(); + + assertEquals(3, account.getCredentials().size()); + assertTrue(account.toString().contains("credentials")); + + WebAuthnAccount same = new WebAuthnAccount.Builder(). + credential(first). + credentials(second). + credentials(List.of(first)). + build(); + + assertEquals(account, account); + assertNotEquals(account, null); + assertNotEquals(account, "string"); + assertNotEquals(account, same); + } + + @Test + void webAuthnDeviceCredential() { + WebAuthnDeviceCredential credential = new WebAuthnDeviceCredential.Builder(). + identifier("identifier").json("json").build(); + + assertEquals("identifier", credential.getIdentifier()); + assertEquals("json", credential.getJson()); + assertTrue(credential.toString().contains("identifier")); + + WebAuthnDeviceCredential same = new WebAuthnDeviceCredential.Builder(). + identifier("identifier").json("json").build(); + WebAuthnDeviceCredential other = new WebAuthnDeviceCredential.Builder(). + identifier("identifier").json("other").build(); + + assertEquals(credential, credential); + assertNotEquals(credential, null); + assertNotEquals(credential, "string"); + assertEquals(credential, same); + assertEquals(credential.hashCode(), same.hashCode()); + assertNotEquals(credential, other); + } + + @Test + void googleMfaAuthToken() { + LocalDateTime issueDate = LocalDateTime.of(2026, 1, 1, 12, 0); + GoogleMfaAuthToken token = new GoogleMfaAuthToken.Builder().issueDate(issueDate).token(123456).build(); + + assertEquals(123456, token.getOtp()); + assertEquals(issueDate, token.getIssueDate()); + assertTrue(token.toString().contains("issueDate")); + + GoogleMfaAuthToken same = new GoogleMfaAuthToken.Builder().issueDate(issueDate).token(123456).build(); + GoogleMfaAuthToken other = new GoogleMfaAuthToken.Builder().issueDate(issueDate).token(654321).build(); + + assertEquals(token, token); + assertNotEquals(token, null); + assertNotEquals(token, "string"); + assertEquals(token, same); + assertEquals(token.hashCode(), same.hashCode()); + assertNotEquals(token, other); + } + + @Test + void googleMfaAuthAccount() { + ZonedDateTime registrationDate = ZonedDateTime.of(2026, 1, 1, 12, 0, 0, 0, ZoneOffset.UTC); + GoogleMfaAuthAccount account = new GoogleMfaAuthAccount.Builder(). + id(1L). + name("name"). + username("username"). + secretKey("secret"). + validationCode(123). + scratchCodes(List.of(1, 2, 3)). + registrationDate(registrationDate). + source("source"). + build(); + + assertEquals(1L, account.getId()); + assertEquals("name", account.getName()); + assertEquals("username", account.getUsername()); + assertEquals("secret", account.getSecretKey()); + assertEquals(123, account.getValidationCode()); + assertEquals(3, account.getScratchCodes().size()); + assertEquals(registrationDate, account.getRegistrationDate()); + assertEquals("source", account.getSource()); + assertTrue(account.toString().contains("username")); + + GoogleMfaAuthAccount same = new GoogleMfaAuthAccount.Builder(). + id(1L). + name("name"). + username("username"). + secretKey("secret"). + validationCode(123). + scratchCodes(List.of(1, 2, 3)). + registrationDate(registrationDate). + source("source"). + build(); + GoogleMfaAuthAccount other = new GoogleMfaAuthAccount.Builder(). + id(2L). + name("name"). + username("username"). + secretKey("secret"). + validationCode(123). + scratchCodes(List.of(1, 2, 3)). + registrationDate(registrationDate). + source("source"). + build(); + + assertEquals(account, account); + assertNotEquals(account, null); + assertNotEquals(account, "string"); + assertEquals(account, same); + assertEquals(account.hashCode(), same.hashCode()); + assertNotEquals(account, other); + } + + @Test + void impersonationAccount() { + ImpersonationAccount account = new ImpersonationAccount.Builder().impersonated("impersonated").build(); + + assertEquals("impersonated", account.getImpersonated()); + assertTrue(account.toString().contains("impersonated")); + + ImpersonationAccount same = new ImpersonationAccount.Builder().impersonated("impersonated").build(); + ImpersonationAccount other = new ImpersonationAccount.Builder().impersonated("other").build(); + + assertEquals(account, account); + assertNotEquals(account, null); + assertNotEquals(account, "string"); + assertEquals(account, same); + assertEquals(account.hashCode(), same.hashCode()); + assertNotEquals(account, other); + } + + @Test + void mfaTrustedDevice() { + ZonedDateTime recordDate = ZonedDateTime.of(2026, 1, 1, 12, 0, 0, 0, ZoneOffset.UTC); + ZonedDateTime expirationDate = recordDate.plusDays(1); + MfaTrustedDevice device = new MfaTrustedDevice(); + device.setId(1L); + device.setName("name"); + device.setDeviceFingerprint("fingerprint"); + device.setRecordKey("key"); + device.setRecordDate(recordDate); + device.setExpirationDate(expirationDate); + + assertEquals(1L, device.getId()); + assertEquals("name", device.getName()); + assertEquals("fingerprint", device.getDeviceFingerprint()); + assertEquals("key", device.getRecordKey()); + assertEquals(recordDate, device.getRecordDate()); + assertEquals(expirationDate, device.getExpirationDate()); + assertTrue(device.toString().contains("fingerprint")); + + MfaTrustedDevice same = new MfaTrustedDevice(); + same.setId(1L); + same.setName("name"); + same.setDeviceFingerprint("fingerprint"); + same.setRecordKey("key"); + same.setRecordDate(recordDate); + same.setExpirationDate(expirationDate); + + MfaTrustedDevice other = new MfaTrustedDevice(); + other.setId(2L); + other.setName("name"); + other.setDeviceFingerprint("fingerprint"); + other.setRecordKey("key"); + other.setRecordDate(recordDate); + other.setExpirationDate(expirationDate); + + assertEquals(device, device); + assertNotEquals(device, null); + assertNotEquals(device, "string"); + assertEquals(device, same); + assertEquals(device.hashCode(), same.hashCode()); + assertNotEquals(device, other); + assertTrue(device.hashCode() != other.hashCode()); + } +} diff --git a/common/am/rest-api/pom.xml b/common/am/rest-api/pom.xml index ea3431b1b95..b4486089adc 100644 --- a/common/am/rest-api/pom.xml +++ b/common/am/rest-api/pom.xml @@ -58,6 +58,9 @@ under the License. org.apache.maven.plugins maven-javadoc-plugin true + + none + attach-javadocs diff --git a/common/idm/lib/pom.xml b/common/idm/lib/pom.xml index 3722ca95521..3e679ad21db 100644 --- a/common/idm/lib/pom.xml +++ b/common/idm/lib/pom.xml @@ -43,6 +43,18 @@ under the License. syncope-common-idrepo-lib ${project.version} + + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + diff --git a/common/idm/lib/src/test/java/org/apache/syncope/common/lib/types/ConnConfPropSchemaTest.java b/common/idm/lib/src/test/java/org/apache/syncope/common/lib/types/ConnConfPropSchemaTest.java new file mode 100644 index 00000000000..2caa40c0709 --- /dev/null +++ b/common/idm/lib/src/test/java/org/apache/syncope/common/lib/types/ConnConfPropSchemaTest.java @@ -0,0 +1,81 @@ +/* + * 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.common.lib.types; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; + +class ConnConfPropSchemaTest { + + @Test + void schema() { + ConnConfPropSchema schema = new ConnConfPropSchema(); + schema.setName("name"); + schema.setType("type"); + schema.setRequired(true); + schema.setConfidential(false); + List defaults = new ArrayList<>(); + defaults.add("default"); + schema.getDefaultValues().clear(); + schema.getDefaultValues().addAll(defaults); + schema.setDisplayName("display"); + schema.setHelpMessage("help"); + schema.setOrder(1); + + assertEquals("name", schema.getName()); + assertEquals("type", schema.getType()); + assertTrue(schema.isRequired()); + assertTrue(!schema.isConfidential()); + assertEquals(1, schema.getDefaultValues().size()); + assertEquals("default", schema.getDefaultValues().get(0)); + assertEquals("display", schema.getDisplayName()); + assertEquals("help", schema.getHelpMessage()); + assertEquals(1, schema.getOrder()); + + ConnConfPropSchema same = new ConnConfPropSchema(); + same.setName("name"); + same.setType("type"); + same.setRequired(true); + same.setConfidential(false); + List sameDefaults = new ArrayList<>(); + sameDefaults.add("default"); + same.getDefaultValues().clear(); + same.getDefaultValues().addAll(sameDefaults); + same.setDisplayName("display"); + same.setHelpMessage("help"); + same.setOrder(1); + + ConnConfPropSchema other = new ConnConfPropSchema(); + other.setName("other"); + other.setOrder(2); + + assertEquals(schema, schema); + assertTrue(!schema.equals(null)); + assertEquals(schema, same); + assertEquals(schema.hashCode(), same.hashCode()); + assertTrue(!schema.equals(other)); + + assertEquals(0, schema.compareTo(same)); + assertTrue(schema.compareTo(other) < 0); + } +} diff --git a/common/idm/lib/src/test/java/org/apache/syncope/common/lib/types/ConnConfPropertyTest.java b/common/idm/lib/src/test/java/org/apache/syncope/common/lib/types/ConnConfPropertyTest.java new file mode 100644 index 00000000000..5e344332d84 --- /dev/null +++ b/common/idm/lib/src/test/java/org/apache/syncope/common/lib/types/ConnConfPropertyTest.java @@ -0,0 +1,64 @@ +/* + * 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.common.lib.types; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; + +class ConnConfPropertyTest { + + @Test + void property() { + ConnConfProperty property = new ConnConfProperty(); + ConnConfPropSchema schema = new ConnConfPropSchema(); + property.setSchema(schema); + List values = new ArrayList<>(); + values.add("value"); + property.getValues().clear(); + property.getValues().addAll(values); + property.setOverridable(true); + + assertEquals("value", property.getValues().get(0)); + assertTrue(property.isOverridable()); + + ConnConfProperty same = new ConnConfProperty(); + same.setSchema(schema); + List sameValues = new ArrayList<>(); + sameValues.add("value"); + same.getValues().clear(); + same.getValues().addAll(sameValues); + same.setOverridable(true); + + ConnConfProperty other = new ConnConfProperty(); + List otherValues = new ArrayList<>(); + otherValues.add("other"); + other.getValues().clear(); + other.getValues().addAll(otherValues); + + assertEquals(property, property); + assertTrue(!property.equals(null)); + assertEquals(property, same); + assertEquals(property.hashCode(), same.hashCode()); + assertTrue(!property.equals(other)); + } +} diff --git a/common/idm/lib/src/test/java/org/apache/syncope/common/lib/types/ConnPoolConfTest.java b/common/idm/lib/src/test/java/org/apache/syncope/common/lib/types/ConnPoolConfTest.java new file mode 100644 index 00000000000..cb4a424ec31 --- /dev/null +++ b/common/idm/lib/src/test/java/org/apache/syncope/common/lib/types/ConnPoolConfTest.java @@ -0,0 +1,70 @@ +/* + * 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.common.lib.types; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class ConnPoolConfTest { + + @Test + void pool() { + ConnPoolConf pool = new ConnPoolConf(); + + pool.setMaxObjects(10); + pool.setMinIdle(2); + pool.setMaxIdle(5); + pool.setMaxWait(3L); + pool.setMinEvictableIdleTimeMillis(60L); + + assertEquals(10, pool.getMaxObjects().intValue()); + assertEquals(2, pool.getMinIdle().intValue()); + assertEquals(5, pool.getMaxIdle().intValue()); + assertEquals(3L, pool.getMaxWait()); + assertEquals(60L, pool.getMinEvictableIdleTimeMillis()); + + ConnPoolConf same = new ConnPoolConf(); + same.setMaxObjects(10); + same.setMinIdle(2); + same.setMaxIdle(5); + same.setMaxWait(3L); + same.setMinEvictableIdleTimeMillis(60L); + + ConnPoolConf other = new ConnPoolConf(); + other.setMaxObjects(20); + + assertEquals(pool, pool); + assertTrue(!pool.equals(null)); + assertEquals(pool, same); + assertEquals(pool.hashCode(), same.hashCode()); + assertTrue(!pool.equals(other)); + } + + @Test + void poolWithNulls() { + ConnPoolConf pool = new ConnPoolConf(); + assertTrue(pool.getMaxObjects() == null); + assertTrue(pool.getMinIdle() == null); + assertTrue(pool.getMaxIdle() == null); + assertTrue(pool.getMaxWait() == null); + assertTrue(pool.getMinEvictableIdleTimeMillis() == null); + } +} diff --git a/common/idm/lib/src/test/java/org/apache/syncope/common/lib/types/ConnectorCapabilityTest.java b/common/idm/lib/src/test/java/org/apache/syncope/common/lib/types/ConnectorCapabilityTest.java new file mode 100644 index 00000000000..e00b647192a --- /dev/null +++ b/common/idm/lib/src/test/java/org/apache/syncope/common/lib/types/ConnectorCapabilityTest.java @@ -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. + */ +package org.apache.syncope.common.lib.types; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class ConnectorCapabilityTest { + + @Test + void capabilities() { + assertEquals(8, ConnectorCapability.values().length); + assertTrue(List.of(ConnectorCapability.AUTHENTICATE, + ConnectorCapability.CREATE, + ConnectorCapability.UPDATE, + ConnectorCapability.UPDATE_DELTA, + ConnectorCapability.DELETE, + ConnectorCapability.SEARCH, + ConnectorCapability.SYNC, + ConnectorCapability.LIVE_SYNC).contains(ConnectorCapability.AUTHENTICATE)); + assertTrue(List.of(ConnectorCapability.AUTHENTICATE, + ConnectorCapability.CREATE, + ConnectorCapability.UPDATE, + ConnectorCapability.UPDATE_DELTA, + ConnectorCapability.DELETE, + ConnectorCapability.SEARCH, + ConnectorCapability.SYNC, + ConnectorCapability.LIVE_SYNC).contains(ConnectorCapability.DELETE)); + } +} diff --git a/common/idm/lib/src/test/java/org/apache/syncope/common/lib/types/IdMEntitlementTest.java b/common/idm/lib/src/test/java/org/apache/syncope/common/lib/types/IdMEntitlementTest.java new file mode 100644 index 00000000000..ec96b1064ee --- /dev/null +++ b/common/idm/lib/src/test/java/org/apache/syncope/common/lib/types/IdMEntitlementTest.java @@ -0,0 +1,36 @@ +/* + * 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.common.lib.types; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Set; +import org.junit.jupiter.api.Test; + +class IdMEntitlementTest { + + @Test + void values() { + Set values = IdMEntitlement.values(); + + assertEquals(17, values.size()); + assertTrue(values.contains(IdMEntitlement.RESOURCE_LIST)); + } +} diff --git a/common/idm/lib/src/test/java/org/apache/syncope/common/lib/types/IdMImplementationTypeTest.java b/common/idm/lib/src/test/java/org/apache/syncope/common/lib/types/IdMImplementationTypeTest.java new file mode 100644 index 00000000000..c8908e8b5e1 --- /dev/null +++ b/common/idm/lib/src/test/java/org/apache/syncope/common/lib/types/IdMImplementationTypeTest.java @@ -0,0 +1,35 @@ +/* + * 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.common.lib.types; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class IdMImplementationTypeTest { + + @Test + void constants() { + assertEquals("RECON_FILTER_BUILDER", IdMImplementationType.RECON_FILTER_BUILDER); + assertEquals("PROPAGATION_ACTIONS", IdMImplementationType.PROPAGATION_ACTIONS); + + assertTrue(true); // just verify they compile and are accessible + } +} diff --git a/common/idm/rest-api/pom.xml b/common/idm/rest-api/pom.xml index 7726c9f56ac..1f7b837158d 100644 --- a/common/idm/rest-api/pom.xml +++ b/common/idm/rest-api/pom.xml @@ -58,6 +58,9 @@ under the License. org.apache.maven.plugins maven-javadoc-plugin true + + none + attach-javadocs diff --git a/common/idrepo/lib/pom.xml b/common/idrepo/lib/pom.xml index fee39c6e83c..1051aacd6c5 100644 --- a/common/idrepo/lib/pom.xml +++ b/common/idrepo/lib/pom.xml @@ -66,12 +66,18 @@ under the License. org.slf4j slf4j-api - + + org.junit.jupiter junit-jupiter test + + org.mockito + mockito-core + test + diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/AnyOperationsTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/AnyOperationsTest.java index 7e73dd4d742..dd941098c61 100644 --- a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/AnyOperationsTest.java +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/AnyOperationsTest.java @@ -19,32 +19,512 @@ package org.apache.syncope.common.lib; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.List; +import java.util.Set; import org.apache.syncope.common.lib.request.AnyObjectUR; +import org.apache.syncope.common.lib.request.AnyUR; import org.apache.syncope.common.lib.request.AttrPatch; +import org.apache.syncope.common.lib.request.GroupUR; +import org.apache.syncope.common.lib.request.LinkedAccountUR; +import org.apache.syncope.common.lib.request.MembershipUR; +import org.apache.syncope.common.lib.request.PasswordPatch; +import org.apache.syncope.common.lib.request.RelationshipUR; +import org.apache.syncope.common.lib.request.StringPatchItem; +import org.apache.syncope.common.lib.request.StringReplacePatchItem; +import org.apache.syncope.common.lib.request.UserUR; import org.apache.syncope.common.lib.to.AnyObjectTO; +import org.apache.syncope.common.lib.to.AnyTO; +import org.apache.syncope.common.lib.to.GroupTO; +import org.apache.syncope.common.lib.to.LinkedAccountTO; +import org.apache.syncope.common.lib.to.MembershipTO; +import org.apache.syncope.common.lib.to.RelationshipTO; +import org.apache.syncope.common.lib.to.TypeExtensionTO; +import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.types.PatchOperation; import org.junit.jupiter.api.Test; -public class AnyOperationsTest { +class AnyOperationsTest { + + private static AttrPatch plainAttrPatch( + final AnyUR anyUR, final String schema, final PatchOperation operation) { + + return anyUR.getPlainAttrs().stream(). + filter(patch -> patch.getAttr() != null + && schema.equals(patch.getAttr().getSchema()) + && patch.getOperation() == operation). + findFirst().orElseThrow(); + } + + private static MembershipUR membership( + final Set memberships, final String group, final PatchOperation operation) { + + return memberships.stream(). + filter(membership -> group.equals(membership.getGroup()) + && membership.getOperation() == operation). + findFirst().orElseThrow(); + } + + private static RelationshipUR relationship( + final Set relationships, final String type, final PatchOperation operation) { + + return relationships.stream(). + filter(relationship -> type.equals(relationship.getType()) + && relationship.getOperation() == operation). + findFirst().orElseThrow(); + } + + private static LinkedAccountUR linkedAccount( + final List accounts, final String resource) { + + return accounts.stream(). + filter(account -> account.getLinkedAccountTO() != null + && resource.equals(account.getLinkedAccountTO().getResource())). + findFirst().orElseThrow(); + } + + @Test + void diffUser() { + UserTO original = new UserTO(); + original.setKey("key"); + original.setUsername("oldUsername"); + original.setPassword("oldPassword"); + original.setSecurityQuestion("oldQuestion"); + original.setMustChangePassword(true); + original.getRoles().add("roleToRemove"); + original.getRoles().add("roleToKeep"); + original.getAuxClasses().add("auxToRemove"); + original.getResources().add("resourceToRemove"); + original.getPlainAttrs().add(new Attr.Builder("plain").value("oldValue").build()); + original.getPlainAttrs().add(new Attr.Builder("toRemove").value("value").build()); + original.getPlainAttrs().add(new Attr.Builder("onlyOriginal").value("value").build()); + original.getMemberships().add(new MembershipTO.Builder("groupToRemove").build()); + original.getLinkedAccounts().add(new LinkedAccountTO.Builder("resourceToRemove", "key").build()); + original.getRelationships().add(new RelationshipTO.Builder("typeToRemove").otherEnd("other").build()); + + UserTO updated = new UserTO(); + updated.setKey("key"); + updated.setUsername("newUsername"); + updated.setPassword("newPassword"); + updated.setSecurityQuestion("newQuestion"); + updated.setMustChangePassword(false); + updated.getRoles().add("roleToKeep"); + updated.getRoles().add("roleToAdd"); + updated.getAuxClasses().add("auxToAdd"); + updated.getResources().add("resourceToAdd"); + updated.getPlainAttrs().add(new Attr.Builder("plain").value("newValue").build()); + updated.getPlainAttrs().add(new Attr.Builder("toRemove").build()); + updated.getPlainAttrs().add(new Attr.Builder("onlyUpdated").value("value").build()); + updated.getMemberships().add(new MembershipTO.Builder("groupToAdd").build()); + updated.getLinkedAccounts().add(new LinkedAccountTO.Builder("resourceToAdd", "key").build()); + updated.getRelationships().add(new RelationshipTO.Builder("typeToAdd").otherEnd("other").build()); + + UserUR diff = AnyOperations.diff(updated, original, false); + + assertEquals("key", diff.getKey()); + assertEquals("newUsername", diff.getUsername().getValue()); + assertEquals("newPassword", diff.getPassword().getValue()); + assertTrue(diff.getPassword().getResources().contains("resourceToAdd")); + assertEquals("newQuestion", diff.getSecurityQuestion().getValue()); + assertNull(diff.getSecurityAnswer()); + assertFalse(diff.getMustChangePassword().getValue()); + + assertTrue(diff.getRoles().contains( + new StringPatchItem.Builder().operation(PatchOperation.DELETE).value("roleToRemove").build())); + assertTrue(diff.getRoles().contains( + new StringPatchItem.Builder().operation(PatchOperation.ADD_REPLACE).value("roleToAdd").build())); + + assertTrue(diff.getAuxClasses().contains( + new StringPatchItem.Builder().operation(PatchOperation.DELETE).value("auxToRemove").build())); + assertTrue(diff.getAuxClasses().contains( + new StringPatchItem.Builder().operation(PatchOperation.ADD_REPLACE).value("auxToAdd").build())); + + assertTrue(diff.getResources().contains( + new StringPatchItem.Builder().operation(PatchOperation.DELETE).value("resourceToRemove").build())); + assertTrue(diff.getResources().contains( + new StringPatchItem.Builder().operation(PatchOperation.ADD_REPLACE).value("resourceToAdd").build())); + + assertEquals(PatchOperation.DELETE, plainAttrPatch(diff, "onlyOriginal", PatchOperation.DELETE).getOperation()); + assertEquals(PatchOperation.DELETE, plainAttrPatch(diff, "toRemove", PatchOperation.DELETE).getOperation()); + assertEquals("newValue", + plainAttrPatch(diff, "plain", PatchOperation.ADD_REPLACE).getAttr().getValues().get(0)); + assertTrue(diff.getPlainAttrs().contains(plainAttrPatch(diff, "onlyUpdated", PatchOperation.ADD_REPLACE))); + + assertEquals(PatchOperation.DELETE, membership(diff.getMemberships(), "groupToRemove", + PatchOperation.DELETE).getOperation()); + assertEquals(PatchOperation.ADD_REPLACE, membership(diff.getMemberships(), "groupToAdd", + PatchOperation.ADD_REPLACE).getOperation()); + + assertEquals(PatchOperation.DELETE, relationship(diff.getRelationships(), "typeToRemove", + PatchOperation.DELETE).getOperation()); + assertEquals(PatchOperation.ADD_REPLACE, relationship(diff.getRelationships(), "typeToAdd", + PatchOperation.ADD_REPLACE).getOperation()); + + assertEquals(2, diff.getLinkedAccounts().size()); + assertEquals(PatchOperation.DELETE, linkedAccount(diff.getLinkedAccounts(), "resourceToRemove").getOperation()); + assertEquals(PatchOperation.ADD_REPLACE, + linkedAccount(diff.getLinkedAccounts(), "resourceToAdd").getOperation()); + } + + @Test + void diffUserIncremental() { + UserTO original = new UserTO(); + original.setKey("key"); + original.setuManager("uManager"); + original.getRoles().add("roleToRemove"); + original.getAuxClasses().add("auxToRemove"); + original.getResources().add("resourceToRemove"); + original.getPlainAttrs().add(new Attr.Builder("toRemove").value("value").build()); + original.getMemberships().add(new MembershipTO.Builder("groupToRemove").build()); + original.getLinkedAccounts().add(new LinkedAccountTO.Builder("resourceToRemove", "key").build()); + original.getRelationships().add(new RelationshipTO.Builder("typeToRemove").otherEnd("other").build()); + + UserTO updated = new UserTO(); + updated.setKey("key"); + updated.getPlainAttrs().add(new Attr.Builder("toRemove").build()); + + UserUR diff = AnyOperations.diff(updated, original, true); + + assertNull(diff.getuManager()); + assertTrue(diff.getRoles().isEmpty()); + assertTrue(diff.getAuxClasses().isEmpty()); + assertTrue(diff.getResources().isEmpty()); + assertTrue(diff.getPlainAttrs().isEmpty()); + assertTrue(diff.getMemberships().isEmpty()); + assertTrue(diff.getLinkedAccounts().isEmpty()); + assertTrue(diff.getRelationships().isEmpty()); + } + + @Test + void diffGroup() { + GroupTO original = new GroupTO(); + original.setKey("key"); + original.setName("oldName"); + + GroupTO updated = new GroupTO(); + updated.setKey("key"); + updated.setName("newName"); + updated.getTypeExtensions().add(new TypeExtensionTO()); + + GroupUR diff = AnyOperations.diff(updated, original, false); + + assertEquals("newName", diff.getName().getValue()); + assertEquals(1, diff.getTypeExtensions().size()); + } + + @Test + void diffAnyObject() { + AnyObjectTO original = new AnyObjectTO(); + original.setKey("key"); + original.setName("oldName"); + original.getMemberships().add(new MembershipTO.Builder("groupToRemove").build()); + + AnyObjectTO updated = new AnyObjectTO(); + updated.setKey("key"); + updated.setName("newName"); + updated.getMemberships().add(new MembershipTO.Builder("groupToAdd").build()); + + AnyObjectUR diff = AnyOperations.diff(updated, original, false); + + assertEquals("newName", diff.getName().getValue()); + assertEquals(2, diff.getMemberships().size()); + } + + @Test + void diffWithDifferentKeys() { + UserTO updated = new UserTO(); + updated.setKey("first"); + + UserTO original = new UserTO(); + original.setKey("second"); + + assertThrows(IllegalArgumentException.class, () -> AnyOperations.diff(updated, original, false)); + } + + @Test + void diffWithMissingKey() { + UserTO updated = new UserTO(); + + UserTO original = new UserTO(); + original.setKey("second"); + + assertThrows(IllegalArgumentException.class, () -> AnyOperations.diff(updated, original, false)); + } + + @Test + void genericDiff() { + AnyUR user = AnyOperations.diff(new UserTO(), new UserTO(), false); + AnyUR group = AnyOperations.diff(new GroupTO(), new GroupTO(), false); + AnyUR anyObject = AnyOperations.diff(new AnyObjectTO(), new AnyObjectTO(), false); + + assertTrue(user instanceof UserUR); + assertTrue(group instanceof GroupUR); + assertTrue(anyObject instanceof AnyObjectUR); + } @Test - public void mindiff() { - AnyObjectTO oldOne = new AnyObjectTO(); - oldOne.setName("name"); - oldOne.getPlainAttrs().add(new Attr.Builder("plain").value("oldValue").build()); - oldOne.getPlainAttrs().add(new Attr.Builder("encrypted").value("oldValue").build()); + void genericDiffUnsupported() { + AnyTO custom = new AnyTO() { - AnyObjectTO newOne = new AnyObjectTO(); - newOne.setName("name"); - newOne.getPlainAttrs().add(new Attr.Builder("plain").value("newValue").build()); - newOne.getPlainAttrs().add(new Attr.Builder("encrypted").value("oldValue").build()); + private static final long serialVersionUID = 2414620536628749337L; + + @Override + public String getDiscriminator() { + return "custom"; + } + }; + + assertThrows(IllegalArgumentException.class, () -> AnyOperations.diff(custom, custom, false)); + } + + @Test + void patchUser() { + Attr newMembershipAttr = new Attr.Builder("memb").value("new").build(); + LinkedAccountTO oldAccount = new LinkedAccountTO.Builder("resourceToRemove", "key").build(); + + UserTO user = new UserTO(); + user.setKey("key"); + user.setRealm("/"); + user.setuManager("uManager"); + user.getAuxClasses().add("auxToRemove"); + user.getPlainAttrs().add(new Attr.Builder("plain").value("old").build()); + user.getPlainAttrs().add(new Attr.Builder("toRemove").value("value").build()); + user.getResources().add("resourceToRemove"); + user.getRoles().add("roleToRemove"); + user.getMemberships().add(new MembershipTO.Builder("groupToRemove"). + plainAttr(new Attr.Builder("memb").value("old").build()).build()); + user.getRelationships().add(new RelationshipTO.Builder("typeToRemove").otherEnd("other").build()); + user.getLinkedAccounts().add(oldAccount); + + UserUR req = new UserUR(); + req.setKey("key"); + req.setRealm(new StringReplacePatchItem.Builder().value("/new").build()); + req.setuManager(new StringReplacePatchItem.Builder().value("newUManager").build()); + req.setgManager(new StringReplacePatchItem.Builder().value("gManager").build()); + req.getAuxClasses().add( + new StringPatchItem.Builder().operation(PatchOperation.DELETE).value("auxToRemove").build()); + req.getAuxClasses().add( + new StringPatchItem.Builder().operation(PatchOperation.ADD_REPLACE).value("auxToAdd").build()); + req.getPlainAttrs().add(new AttrPatch.Builder( + new Attr.Builder("plain").value("new").build()).build()); + req.getPlainAttrs().add(new AttrPatch.Builder(new Attr.Builder("toRemove").build()). + operation(PatchOperation.DELETE).build()); + req.getResources().add( + new StringPatchItem.Builder().operation(PatchOperation.DELETE).value("resourceToRemove").build()); + req.getResources().add( + new StringPatchItem.Builder().operation(PatchOperation.ADD_REPLACE).value("resourceToAdd").build()); + req.setPassword(new PasswordPatch.Builder().value("password").build()); + req.setUsername(new StringReplacePatchItem.Builder().value("username").build()); + req.getRoles().add( + new StringPatchItem.Builder().operation(PatchOperation.DELETE).value("roleToRemove").build()); + req.getRoles().add( + new StringPatchItem.Builder().operation(PatchOperation.ADD_REPLACE).value("roleToAdd").build()); + req.getMemberships().add( + new MembershipUR.Builder("groupToRemove").operation(PatchOperation.DELETE).build()); + req.getMemberships().add(new MembershipUR.Builder("groupToAdd"). + operation(PatchOperation.ADD_REPLACE).plainAttr(newMembershipAttr).build()); + req.getRelationships().add(new RelationshipUR.Builder("typeToRemove").otherEnd("other"). + operation(PatchOperation.DELETE).build()); + req.getRelationships().add(new RelationshipUR.Builder("typeToAdd").otherEnd("other"). + operation(PatchOperation.ADD_REPLACE). + plainAttr(new Attr.Builder("rel").value("value").build()).build()); + req.getLinkedAccounts().add(new LinkedAccountUR.Builder(). + operation(PatchOperation.DELETE).linkedAccountTO(oldAccount).build()); + req.getLinkedAccounts().add(new LinkedAccountUR.Builder(). + operation(PatchOperation.ADD_REPLACE). + linkedAccountTO(new LinkedAccountTO.Builder("resourceToAdd", "key").build()).build()); + + UserTO patched = AnyOperations.patch(user, req); + + assertNotSame(user, patched); + assertEquals("key", patched.getKey()); + assertEquals("/new", patched.getRealm()); + assertEquals("newUManager", patched.getuManager()); + assertEquals("gManager", patched.getgManager()); + assertEquals(Set.of("auxToAdd"), patched.getAuxClasses()); + assertEquals(Set.of("resourceToAdd"), patched.getResources()); + assertEquals("password", patched.getPassword()); + assertEquals("username", patched.getUsername()); + assertEquals(Set.of("roleToAdd"), Set.copyOf(patched.getRoles())); + assertEquals(1, patched.getPlainAttrs().size()); + assertTrue(patched.getPlainAttrs().contains(new Attr.Builder("plain").value("new").build())); + assertEquals(1, patched.getMemberships().size()); + assertEquals("groupToAdd", patched.getMemberships().get(0).getGroupKey()); + assertTrue(patched.getMemberships().get(0).getPlainAttrs().contains(newMembershipAttr)); + assertEquals(1, patched.getRelationships().size()); + assertEquals("typeToAdd", patched.getRelationships().get(0).getType()); + assertTrue(patched.getRelationships().get(0).getPlainAttrs().contains( + new Attr.Builder("rel").value("value").build())); + assertEquals(1, patched.getLinkedAccounts().size()); + assertEquals("resourceToAdd", patched.getLinkedAccounts().get(0).getResource()); + + assertEquals("old", user.getPlainAttrs().stream(). + filter(attr -> "plain".equals(attr.getSchema())).findFirst().orElseThrow().getValues().get(0)); + } + + @Test + void patchGroup() { + GroupTO group = new GroupTO(); + group.setKey("key"); + group.setName("oldName"); + group.getRelationships().add(new RelationshipTO.Builder("typeToRemove").otherEnd("other").build()); + + GroupUR req = new GroupUR(); + req.setKey("key"); + req.setName(new StringReplacePatchItem.Builder().value("newName").build()); + req.getRelationships().add(new RelationshipUR.Builder("typeToRemove").otherEnd("other"). + operation(PatchOperation.DELETE).build()); + req.getRelationships().add(new RelationshipUR.Builder("typeToAdd").otherEnd("other"). + operation(PatchOperation.ADD_REPLACE).build()); + + GroupTO patched = AnyOperations.patch(group, req); + + assertNotSame(group, patched); + assertEquals("newName", patched.getName()); + assertEquals(1, patched.getRelationships().size()); + assertEquals("typeToAdd", patched.getRelationships().get(0).getType()); + assertEquals(1, group.getRelationships().size()); + } + + @Test + void patchAnyObject() { + AnyObjectTO anyObject = new AnyObjectTO(); + anyObject.setKey("key"); + anyObject.setName("oldName"); + anyObject.getMemberships().add(new MembershipTO.Builder("groupToRemove").build()); + anyObject.getRelationships().add(new RelationshipTO.Builder("typeToRemove").otherEnd("other").build()); + + AnyObjectUR req = new AnyObjectUR(); + req.setKey("key"); + req.setName(new StringReplacePatchItem.Builder().value("newName").build()); + req.getMemberships().add( + new MembershipUR.Builder("groupToRemove").operation(PatchOperation.DELETE).build()); + req.getMemberships().add( + new MembershipUR.Builder("groupToAdd").operation(PatchOperation.ADD_REPLACE).build()); + req.getRelationships().add(new RelationshipUR.Builder("typeToRemove").otherEnd("other"). + operation(PatchOperation.DELETE).build()); + req.getRelationships().add(new RelationshipUR.Builder("typeToAdd").otherEnd("other"). + operation(PatchOperation.ADD_REPLACE).build()); + + AnyObjectTO patched = AnyOperations.patch(anyObject, req); + + assertNotSame(anyObject, patched); + assertEquals("newName", patched.getName()); + assertEquals(1, patched.getMemberships().size()); + assertEquals("groupToAdd", patched.getMemberships().get(0).getGroupKey()); + assertEquals(1, patched.getRelationships().size()); + assertEquals("typeToAdd", patched.getRelationships().get(0).getType()); + } + + @Test + void patchGeneric() { + UserTO user = new UserTO(); + user.setKey("key"); + UserUR userUR = new UserUR(); + userUR.setKey("key"); + userUR.setUsername(new StringReplacePatchItem.Builder().value("username").build()); + + AnyTO patched = AnyOperations.patch(user, userUR); + + assertTrue(patched instanceof UserTO); + assertEquals("username", ((UserTO) patched).getUsername()); + + AnyTO custom = new AnyTO() { + + private static final long serialVersionUID = 2414620536628749337L; + + @Override + public String getDiscriminator() { + return "custom"; + } + }; + AnyUR customUR = new AnyUR() { + + private static final long serialVersionUID = 2414620536628749337L; + + @Override + public String getDiscriminator() { + return "custom"; + } + }; + + assertNull(AnyOperations.patch(custom, customUR)); + } + + @Test + void patchWithDifferentKeys() { + UserTO user = new UserTO(); + user.setKey("first"); + + UserUR req = new UserUR(); + req.setKey("second"); + + assertThrows(IllegalArgumentException.class, () -> AnyOperations.patch(user, req)); + } + + @Test + void patchWithMissingKey() { + UserTO user = new UserTO(); + + UserUR req = new UserUR(); + req.setKey("key"); + + assertThrows(IllegalArgumentException.class, () -> AnyOperations.patch(user, req)); + } + + @Test + void patchInvalidRelationshipsAndMemberships() { + AnyObjectTO anyObject = new AnyObjectTO(); + anyObject.setKey("key"); + anyObject.getMemberships().add(new MembershipTO.Builder("group").build()); + anyObject.getRelationships().add(new RelationshipTO.Builder("type").otherEnd("other").build()); + + AnyObjectUR req = new AnyObjectUR(); + req.setKey("key"); + req.getMemberships().add(new MembershipUR.Builder((String) null). + operation(PatchOperation.ADD_REPLACE).build()); + req.getRelationships().add(new RelationshipUR.Builder((String) null). + operation(PatchOperation.ADD_REPLACE).build()); + + AnyObjectTO patched = AnyOperations.patch(anyObject, req); + + assertEquals(1, patched.getMemberships().size()); + assertEquals(1, patched.getRelationships().size()); + } + + @Test + void patchInvalidLinkedAccount() { + UserTO user = new UserTO(); + user.setKey("key"); + + UserUR req = new UserUR(); + req.setKey("key"); + req.getLinkedAccounts().add(new LinkedAccountUR.Builder().operation(PatchOperation.ADD_REPLACE).build()); + + assertTrue(AnyOperations.patch(user, req).getLinkedAccounts().isEmpty()); + } + + @Test + void cleanEmptyAttrs() { + UserTO user = new UserTO(); + user.getPlainAttrs().add(new Attr.Builder("empty").build()); + user.getPlainAttrs().add(new Attr.Builder("null").value((String) null).build()); + user.getPlainAttrs().add(new Attr.Builder("notEmpty").value("value").build()); - AnyObjectUR diff = AnyOperations.diff(newOne, oldOne, true); - assertEquals(1, diff.getPlainAttrs().size()); + UserUR req = new UserUR(); + AnyOperations.cleanEmptyAttrs(user, req); - AttrPatch patch = diff.getPlainAttrs().iterator().next(); - assertEquals(PatchOperation.ADD_REPLACE, patch.getOperation()); - assertEquals("plain", patch.getAttr().getSchema()); + assertEquals(2, req.getPlainAttrs().size()); + assertTrue(req.getPlainAttrs().stream(). + allMatch(patch -> patch.getOperation() == PatchOperation.DELETE)); + assertTrue(req.getPlainAttrs().stream(). + anyMatch(patch -> "empty".equals(patch.getAttr().getSchema()))); + assertTrue(req.getPlainAttrs().stream(). + anyMatch(patch -> "null".equals(patch.getAttr().getSchema()))); } } diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/AttrCompareToTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/AttrCompareToTest.java index b9cc44caf0d..d08ee4ce26d 100644 --- a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/AttrCompareToTest.java +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/AttrCompareToTest.java @@ -22,17 +22,17 @@ import org.junit.jupiter.api.Test; -public class AttrCompareToTest { +class AttrCompareToTest { @Test - public void equals() { + void equals() { Attr first = new Attr.Builder("schema").value("value").build(); Attr second = new Attr.Builder("schema").value("value").build(); assertEquals(0, first.compareTo(second)); } @Test - public void different() { + void different() { Attr first = new Attr.Builder("schema1").value("value1").build(); Attr second = new Attr.Builder("schema2").value("value2").build(); assertEquals(-1, first.compareTo(second)); @@ -40,14 +40,14 @@ public void different() { } @Test - public void differentSchemaSameValue() { + void differentSchemaSameValue() { Attr first = new Attr.Builder("schema1").value("value").build(); Attr second = new Attr.Builder("schema2").value("value").build(); assertEquals(-1, first.compareTo(second)); } @Test - public void sameSchemaDifferentValue() { + void sameSchemaDifferentValue() { Attr first = new Attr.Builder("schema").value("value1").build(); Attr second = new Attr.Builder("schema").value("value2").build(); assertEquals(-1, first.compareTo(second)); diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/EntityTOUtilsTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/EntityTOUtilsTest.java new file mode 100644 index 00000000000..38117ed3458 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/EntityTOUtilsTest.java @@ -0,0 +1,257 @@ +/* + * 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.common.lib; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.Map; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.common.lib.request.AnyCR; +import org.apache.syncope.common.lib.request.AnyObjectCR; +import org.apache.syncope.common.lib.request.GroupCR; +import org.apache.syncope.common.lib.request.UserCR; +import org.apache.syncope.common.lib.to.AnyObjectTO; +import org.apache.syncope.common.lib.to.AnyTO; +import org.apache.syncope.common.lib.to.GroupTO; +import org.apache.syncope.common.lib.to.LinkedAccountTO; +import org.apache.syncope.common.lib.to.MembershipTO; +import org.apache.syncope.common.lib.to.RelationshipTO; +import org.apache.syncope.common.lib.to.TypeExtensionTO; +import org.apache.syncope.common.lib.to.UserTO; +import org.junit.jupiter.api.Test; + +class EntityTOUtilsTest { + + @Test + void buildAttrMap() { + Map attrs = EntityTOUtils.buildAttrMap(List.of( + new Attr.Builder("schema1").value("value1").build(), + new Attr.Builder("schema2").value("value2").build(), + new Attr.Builder("schema1").value("value3").build())); + + assertEquals(2, attrs.size()); + assertEquals(List.of("value3"), attrs.get("schema1").getValues()); + assertEquals(List.of("value2"), attrs.get("schema2").getValues()); + } + + @Test + void buildRelationshipMap() { + RelationshipTO first = new RelationshipTO.Builder("type").otherEnd("other").build(); + RelationshipTO second = new RelationshipTO.Builder("type").otherEnd("other").build(); + second.getPlainAttrs().add(new Attr.Builder("schema").value("value").build()); + + Map, RelationshipTO> relationships = + EntityTOUtils.buildRelationshipMap(List.of(first, second)); + + assertEquals(1, relationships.size()); + assertTrue(relationships.get(Pair.of("type", "other")).getPlainAttrs().contains( + new Attr.Builder("schema").value("value").build())); + } + + @Test + void buildMembershipMap() { + MembershipTO first = new MembershipTO.Builder("group").build(); + MembershipTO second = new MembershipTO.Builder("group").groupName("name").build(); + + Map memberships = EntityTOUtils.buildMembershipMap(List.of(first, second)); + + assertEquals(1, memberships.size()); + assertEquals("name", memberships.get("group").getGroupName()); + } + + @Test + void buildLinkedAccountMap() { + LinkedAccountTO first = new LinkedAccountTO.Builder("resource", "key").build(); + LinkedAccountTO second = new LinkedAccountTO.Builder("resource", "key").username("username").build(); + + Map, LinkedAccountTO> accounts = + EntityTOUtils.buildLinkedAccountMap(List.of(first, second)); + + assertEquals(1, accounts.size()); + assertEquals("username", accounts.get(Pair.of("resource", "key")).getUsername()); + } + + @Test + void toUserCR() { + UserTO userTO = new UserTO(); + userTO.setRealm("/"); + userTO.setuManager("uManager"); + userTO.setgManager("gManager"); + userTO.setUsername("username"); + userTO.setPassword("password"); + userTO.setSecurityQuestion("question"); + userTO.setMustChangePassword(true); + userTO.getAuxClasses().add("auxClass"); + userTO.getPlainAttrs().add(new Attr.Builder("plain").value("value").build()); + userTO.getResources().add("resource"); + userTO.getRelationships().add(new RelationshipTO.Builder("type").otherEnd("other").build()); + userTO.getMemberships().add(new MembershipTO.Builder("group").build()); + userTO.getRoles().add("role"); + + UserCR userCR = new UserCR(); + EntityTOUtils.toAnyCR(userTO, userCR); + + assertCommon(userCR); + assertEquals("username", userCR.getUsername()); + assertEquals("password", userCR.getPassword()); + assertEquals("question", userCR.getSecurityQuestion()); + assertTrue(userCR.isMustChangePassword()); + assertEquals(1, userCR.getMemberships().size()); + assertEquals(1, userCR.getRoles().size()); + } + + @Test + void toGroupCR() { + GroupTO groupTO = new GroupTO(); + groupTO.setRealm("/"); + groupTO.setuManager("uManager"); + groupTO.setgManager("gManager"); + groupTO.setName("name"); + groupTO.getAuxClasses().add("auxClass"); + groupTO.getPlainAttrs().add(new Attr.Builder("plain").value("value").build()); + groupTO.getResources().add("resource"); + groupTO.getRelationships().add(new RelationshipTO.Builder("type").otherEnd("other").build()); + groupTO.getTypeExtensions().add(new TypeExtensionTO()); + + GroupCR groupCR = new GroupCR(); + EntityTOUtils.toAnyCR(groupTO, groupCR); + + assertCommon(groupCR); + assertEquals("name", groupCR.getName()); + assertEquals(1, groupCR.getTypeExtensions().size()); + } + + @Test + void toAnyObjectCR() { + AnyObjectTO anyObjectTO = new AnyObjectTO(); + anyObjectTO.setRealm("/"); + anyObjectTO.setuManager("uManager"); + anyObjectTO.setgManager("gManager"); + anyObjectTO.setType("type"); + anyObjectTO.setName("name"); + anyObjectTO.getAuxClasses().add("auxClass"); + anyObjectTO.getPlainAttrs().add(new Attr.Builder("plain").value("value").build()); + anyObjectTO.getResources().add("resource"); + anyObjectTO.getRelationships().add(new RelationshipTO.Builder("type").otherEnd("other").build()); + anyObjectTO.getMemberships().add(new MembershipTO.Builder("group").build()); + + AnyObjectCR anyObjectCR = new AnyObjectCR(); + EntityTOUtils.toAnyCR(anyObjectTO, anyObjectCR); + + assertCommon(anyObjectCR); + assertEquals("type", anyObjectCR.getType()); + assertEquals("name", anyObjectCR.getName()); + assertEquals(1, anyObjectCR.getMemberships().size()); + } + + @Test + void toUserTO() { + UserCR userCR = new UserCR(); + userCR.setRealm("/"); + userCR.setuManager("uManager"); + userCR.setgManager("gManager"); + userCR.setUsername("username"); + userCR.setPassword("password"); + userCR.setSecurityQuestion("question"); + userCR.setMustChangePassword(true); + userCR.getAuxClasses().add("auxClass"); + userCR.getPlainAttrs().add(new Attr.Builder("plain").value("value").build()); + userCR.getResources().add("resource"); + userCR.getRelationships().add(new RelationshipTO.Builder("type").otherEnd("other").build()); + userCR.getMemberships().add(new MembershipTO.Builder("group").build()); + userCR.getRoles().add("role"); + + UserTO userTO = new UserTO(); + EntityTOUtils.toAnyTO(userCR, userTO); + + assertCommon(userTO); + assertEquals("username", userTO.getUsername()); + assertEquals("password", userTO.getPassword()); + assertEquals("question", userTO.getSecurityQuestion()); + assertTrue(userTO.isMustChangePassword()); + assertEquals(1, userTO.getMemberships().size()); + assertEquals(1, userTO.getRoles().size()); + } + + @Test + void toGroupTO() { + GroupCR groupCR = new GroupCR(); + groupCR.setRealm("/"); + groupCR.setuManager("uManager"); + groupCR.setgManager("gManager"); + groupCR.setName("name"); + groupCR.getAuxClasses().add("auxClass"); + groupCR.getPlainAttrs().add(new Attr.Builder("plain").value("value").build()); + groupCR.getResources().add("resource"); + groupCR.getRelationships().add(new RelationshipTO.Builder("type").otherEnd("other").build()); + groupCR.getTypeExtensions().add(new TypeExtensionTO()); + + GroupTO groupTO = new GroupTO(); + EntityTOUtils.toAnyTO(groupCR, groupTO); + + assertCommon(groupTO); + assertEquals("name", groupTO.getName()); + assertEquals(1, groupTO.getTypeExtensions().size()); + } + + @Test + void toAnyObjectTO() { + AnyObjectCR anyObjectCR = new AnyObjectCR(); + anyObjectCR.setRealm("/"); + anyObjectCR.setuManager("uManager"); + anyObjectCR.setgManager("gManager"); + anyObjectCR.setType("type"); + anyObjectCR.setName("name"); + anyObjectCR.getAuxClasses().add("auxClass"); + anyObjectCR.getPlainAttrs().add(new Attr.Builder("plain").value("value").build()); + anyObjectCR.getResources().add("resource"); + anyObjectCR.getRelationships().add(new RelationshipTO.Builder("type").otherEnd("other").build()); + anyObjectCR.getMemberships().add(new MembershipTO.Builder("group").build()); + + AnyObjectTO anyObjectTO = new AnyObjectTO(); + EntityTOUtils.toAnyTO(anyObjectCR, anyObjectTO); + + assertCommon(anyObjectTO); + assertEquals("type", anyObjectTO.getType()); + assertEquals("name", anyObjectTO.getName()); + assertEquals(1, anyObjectTO.getMemberships().size()); + } + + private static void assertCommon(final AnyCR anyCR) { + assertEquals("/", anyCR.getRealm()); + assertEquals("uManager", anyCR.getuManager()); + assertEquals("gManager", anyCR.getgManager()); + assertEquals(1, anyCR.getAuxClasses().size()); + assertEquals(1, anyCR.getPlainAttrs().size()); + assertEquals(1, anyCR.getResources().size()); + assertEquals(1, anyCR.getRelationships().size()); + } + + private static void assertCommon(final AnyTO anyTO) { + assertEquals("/", anyTO.getRealm()); + assertEquals("uManager", anyTO.getuManager()); + assertEquals("gManager", anyTO.getgManager()); + assertEquals(1, anyTO.getAuxClasses().size()); + assertEquals(1, anyTO.getPlainAttrs().size()); + assertEquals(1, anyTO.getResources().size()); + assertEquals(1, anyTO.getRelationships().size()); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/LogOutputStreamTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/LogOutputStreamTest.java new file mode 100644 index 00000000000..2c9d209ab2f --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/LogOutputStreamTest.java @@ -0,0 +1,101 @@ +/* + * 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.common.lib; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; + +import java.nio.charset.StandardCharsets; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; + +class LogOutputStreamTest { + + private Logger logger; + + private LogOutputStream stream; + + @BeforeEach + void setUp() { + logger = mock(Logger.class); + stream = new LogOutputStream(logger); + } + + @Test + void writeLines() throws Exception { + stream.write("first\nsecond\n".getBytes(StandardCharsets.UTF_8)); + + verify(logger).debug("first"); + verify(logger).debug("second"); + } + + @Test + void writeCrLf() throws Exception { + stream.write("first\r\nsecond\r\n".getBytes(StandardCharsets.UTF_8)); + + verify(logger).debug("first"); + verify(logger).debug("second"); + } + + @Test + void writeWithOffsetAndLength() { + byte[] bytes = "xxfirst\n".getBytes(StandardCharsets.UTF_8); + + stream.write(bytes, 2, bytes.length - 2); + + verify(logger).debug("first"); + } + + @Test + void flushPartialLine() throws Exception { + stream.write("partial".getBytes(StandardCharsets.UTF_8)); + verifyNoInteractions(logger); + + stream.flush(); + + verify(logger).debug("partial"); + } + + @Test + void flushWithoutContent() { + stream.flush(); + + verifyNoInteractions(logger); + } + + @Test + void closePartialLine() throws Exception { + stream.write("partial".getBytes(StandardCharsets.UTF_8)); + verifyNoInteractions(logger); + + stream.close(); + + verify(logger).debug("partial"); + } + + @Test + void writeSingleCharacters() { + stream.write('a'); + stream.write('\n'); + + verify(logger).debug("a"); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/SerializationTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/SerializationTest.java index 7019ddb0671..e9d349aa5f8 100644 --- a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/SerializationTest.java +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/SerializationTest.java @@ -45,12 +45,12 @@ import tools.jackson.core.type.TypeReference; import tools.jackson.databind.json.JsonMapper; -public class SerializationTest { +class SerializationTest { private static final JsonMapper MAPPER = new SyncopeJsonMapper(); @Test - public void emptyListAsRoot() throws IOException { + void emptyListAsRoot() throws IOException { List original = new ArrayList<>(); StringWriter writer = new StringWriter(); @@ -62,7 +62,7 @@ public void emptyListAsRoot() throws IOException { } @Test - public void nonEmptyListAsMember() throws IOException { + void nonEmptyListAsMember() throws IOException { AnyObjectCR original = new AnyObjectCR(); original.setName("newPrinter"); original.setType("PRINTER"); @@ -76,7 +76,7 @@ public void nonEmptyListAsMember() throws IOException { } @Test - public void map() throws IOException { + void map() throws IOException { GroupUR req = new GroupUR(); req.setKey(UUID.randomUUID().toString()); @@ -88,7 +88,7 @@ public void map() throws IOException { } @Test - public void patch() throws IOException { + void patch() throws IOException { UserUR req = new UserUR(); req.setKey(UUID.randomUUID().toString()); req.setUsername(new StringReplacePatchItem.Builder().value("newusername").build()); @@ -109,7 +109,7 @@ public void patch() throws IOException { } @Test - public void pagedResult() throws IOException { + void pagedResult() throws IOException { GroupTO group = new GroupTO(); group.setName(UUID.randomUUID().toString()); group.setRealm(SyncopeConstants.ROOT_REALM); @@ -129,7 +129,7 @@ public void pagedResult() throws IOException { } @Test - public void provisioningResult() throws IOException { + void provisioningResult() throws IOException { GroupTO group = new GroupTO(); group.setName(UUID.randomUUID().toString()); group.setRealm(SyncopeConstants.ROOT_REALM); @@ -151,7 +151,7 @@ public void provisioningResult() throws IOException { } @Test - public void manager() { + void manager() { GroupCR originalCR = new GroupCR(); originalCR.setgManager("manager"); diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/SyncopeClientExceptionTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/SyncopeClientExceptionTest.java new file mode 100644 index 00000000000..6cb70151dd3 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/SyncopeClientExceptionTest.java @@ -0,0 +1,100 @@ +/* + * 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.common.lib; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.junit.jupiter.api.Test; + +class SyncopeClientExceptionTest { + + @Test + void build() { + SyncopeClientException exception = SyncopeClientException.build(ClientExceptionType.InvalidUser); + + assertEquals(ClientExceptionType.InvalidUser, exception.getType()); + assertFalse(exception.isComposite()); + assertTrue(exception.isEmpty()); + assertEquals(0, exception.size()); + assertEquals("InvalidUser []", exception.getMessage()); + assertEquals(exception.getMessage(), exception.getLocalizedMessage()); + + exception.getElements().add("element"); + assertFalse(exception.isEmpty()); + assertEquals(1, exception.size()); + assertEquals("InvalidUser [element]", exception.getMessage()); + } + + @Test + void buildCompositeType() { + assertThrows(IllegalArgumentException.class, + () -> SyncopeClientException.build(ClientExceptionType.Composite)); + } + + @Test + void asComposite() { + SyncopeClientCompositeException composite = SyncopeClientException.buildComposite(); + + assertTrue(composite.isComposite()); + assertSame(composite, composite.asComposite()); + + SyncopeClientException exception = SyncopeClientException.build(ClientExceptionType.NotFound); + assertThrows(IllegalArgumentException.class, exception::asComposite); + } + + @Test + void addException() { + SyncopeClientCompositeException composite = SyncopeClientException.buildComposite(); + assertFalse(composite.hasExceptions()); + + SyncopeClientException first = SyncopeClientException.build(ClientExceptionType.InvalidUser); + first.getElements().add("one"); + assertTrue(composite.addException(first)); + + assertTrue(composite.hasExceptions()); + assertEquals(1, composite.getExceptions().size()); + assertTrue(composite.getException(ClientExceptionType.InvalidUser).isPresent()); + assertTrue(composite.getException(ClientExceptionType.NotFound).isEmpty()); + assertEquals("{[InvalidUser [one]]}", composite.getMessage()); + assertEquals(composite.getMessage(), composite.getLocalizedMessage()); + + SyncopeClientException second = SyncopeClientException.build(ClientExceptionType.InvalidUser); + second.getElements().add("two"); + composite.addException(second); + + assertEquals(1, composite.getExceptions().size()); + assertEquals(2, composite.getException(ClientExceptionType.InvalidUser).orElseThrow().getElements().size()); + } + + @Test + void addExceptionWithNullType() { + SyncopeClientCompositeException composite = SyncopeClientException.buildComposite(); + + SyncopeClientException exception = SyncopeClientException.build(null); + composite.addException(exception); + + assertEquals(ClientExceptionType.Unknown, exception.getType()); + assertTrue(composite.getException(ClientExceptionType.Unknown).isPresent()); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/collections/CircularFifoQueueTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/collections/CircularFifoQueueTest.java new file mode 100644 index 00000000000..99fc6e3fce2 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/collections/CircularFifoQueueTest.java @@ -0,0 +1,231 @@ +/* + * 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.common.lib.collections; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import org.apache.commons.lang3.SerializationUtils; +import org.junit.jupiter.api.Test; + +class CircularFifoQueueTest { + + @Test + void defaultConstructor() { + CircularFifoQueue queue = new CircularFifoQueue<>(); + + assertEquals(32, queue.maxSize()); + assertTrue(queue.isEmpty()); + assertEquals(0, queue.size()); + assertFalse(queue.isAtFullCapacity()); + assertFalse(CircularFifoQueue.isFull()); + } + + @Test + void invalidSize() { + assertThrows(IllegalArgumentException.class, () -> new CircularFifoQueue<>(0)); + assertThrows(IllegalArgumentException.class, () -> new CircularFifoQueue<>(-1)); + } + + @Test + void fromCollection() { + CircularFifoQueue queue = new CircularFifoQueue<>(List.of("a", "b", "c")); + + assertEquals(3, queue.maxSize()); + assertEquals(3, queue.size()); + assertEquals(List.of("a", "b", "c"), new ArrayList<>(queue)); + assertEquals("a", queue.peek()); + assertEquals("a", queue.get(0)); + assertEquals("c", queue.get(2)); + } + + @Test + void addAndOffer() { + CircularFifoQueue queue = new CircularFifoQueue<>(3); + + assertTrue(queue.add("a")); + assertTrue(queue.offer("b")); + assertTrue(queue.add("c")); + + assertEquals(3, queue.size()); + assertTrue(queue.isAtFullCapacity()); + assertEquals("a", queue.element()); + assertThrows(NullPointerException.class, () -> queue.add(null)); + assertThrows(NullPointerException.class, () -> queue.offer(null)); + } + + @Test + void getOutOfRange() { + CircularFifoQueue queue = new CircularFifoQueue<>(2); + queue.add("a"); + + assertThrows(NoSuchElementException.class, () -> queue.get(-1)); + assertThrows(NoSuchElementException.class, () -> queue.get(1)); + } + + @Test + void overwriteOldest() { + CircularFifoQueue queue = new CircularFifoQueue<>(3); + queue.addAll(List.of("a", "b", "c")); + queue.add("d"); + + assertEquals(List.of("b", "c", "d"), new ArrayList<>(queue)); + assertEquals("b", queue.get(0)); + assertEquals("d", queue.get(2)); + assertTrue(queue.isAtFullCapacity()); + } + + @Test + void pollRemovePeekOnEmpty() { + CircularFifoQueue queue = new CircularFifoQueue<>(2); + + assertNull(queue.poll()); + assertNull(queue.peek()); + assertThrows(NoSuchElementException.class, () -> queue.remove()); + assertThrows(NoSuchElementException.class, () -> queue.element()); + } + + @Test + void pollAndRemove() { + CircularFifoQueue queue = new CircularFifoQueue<>(3); + queue.addAll(List.of("a", "b", "c")); + + assertEquals("a", queue.poll()); + assertEquals("b", queue.remove()); + assertEquals("c", queue.peek()); + assertEquals(1, queue.size()); + } + + @Test + void clear() { + CircularFifoQueue queue = new CircularFifoQueue<>(2); + queue.addAll(List.of("a", "b")); + + queue.clear(); + + assertTrue(queue.isEmpty()); + assertEquals(0, queue.size()); + assertFalse(queue.isAtFullCapacity()); + } + + @Test + void iteratorOnEmpty() { + CircularFifoQueue queue = new CircularFifoQueue<>(2); + Iterator iterator = queue.iterator(); + + assertFalse(iterator.hasNext()); + assertThrows(NoSuchElementException.class, iterator::next); + assertThrows(IllegalStateException.class, iterator::remove); + } + + @Test + void iteratorRemoveFirst() { + CircularFifoQueue queue = new CircularFifoQueue<>(5); + queue.addAll(List.of("a", "b", "c", "d")); + + Iterator iterator = queue.iterator(); + assertEquals("a", iterator.next()); + iterator.remove(); + assertEquals("b", iterator.next()); + iterator.remove(); + assertEquals("c", iterator.next()); + assertEquals("d", iterator.next()); + assertFalse(iterator.hasNext()); + + assertEquals(List.of("c", "d"), new ArrayList<>(queue)); + } + + @Test + void iteratorRemoveMiddleAndLast() { + CircularFifoQueue queue = new CircularFifoQueue<>(5); + queue.addAll(List.of("a", "b", "c", "d")); + + Iterator iterator = queue.iterator(); + assertEquals("a", iterator.next()); + assertEquals("b", iterator.next()); + iterator.remove(); + assertEquals("c", iterator.next()); + assertEquals("d", iterator.next()); + iterator.remove(); + assertFalse(iterator.hasNext()); + + assertEquals(List.of("a", "c"), new ArrayList<>(queue)); + } + + @Test + void iteratorRemoveWrapped() { + CircularFifoQueue queue = new CircularFifoQueue<>(5); + queue.addAll(List.of("a", "b", "c", "d", "e", "f")); + + Iterator iterator = queue.iterator(); + assertEquals("b", iterator.next()); + assertEquals("c", iterator.next()); + iterator.remove(); + assertEquals("d", iterator.next()); + assertEquals("e", iterator.next()); + assertEquals("f", iterator.next()); + assertFalse(iterator.hasNext()); + + assertEquals(List.of("b", "d", "e", "f"), new ArrayList<>(queue)); + } + + @Test + void iteratorRemoveTwice() { + CircularFifoQueue queue = new CircularFifoQueue<>(3); + queue.add("a"); + + Iterator iterator = queue.iterator(); + assertEquals("a", iterator.next()); + iterator.remove(); + assertThrows(IllegalStateException.class, iterator::remove); + } + + @Test + void serialization() { + CircularFifoQueue queue = new CircularFifoQueue<>(5); + queue.addAll(List.of("a", "b", "c")); + + CircularFifoQueue cloned = SerializationUtils.clone(queue); + + assertEquals(5, cloned.maxSize()); + assertEquals(List.of("a", "b", "c"), new ArrayList<>(cloned)); + assertEquals("a", cloned.peek()); + } + + @Test + void serializationWhenFull() { + CircularFifoQueue queue = new CircularFifoQueue<>(3); + queue.addAll(List.of("a", "b", "c")); + + CircularFifoQueue cloned = SerializationUtils.clone(queue); + + assertEquals(3, cloned.maxSize()); + assertEquals(List.of("a", "b", "c"), new ArrayList<>(cloned)); + assertTrue(cloned.isAtFullCapacity()); + assertEquals("a", cloned.poll()); + assertEquals(List.of("b", "c"), new ArrayList<>(cloned)); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/command/CommandTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/command/CommandTest.java new file mode 100644 index 00000000000..5c47794aae9 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/command/CommandTest.java @@ -0,0 +1,79 @@ +/* + * 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.common.lib.command; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.Serializable; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class CommandTest { + + @Test + void commandTO() { + CommandArgs args = new CommandArgs(); + args.setCtx(Map.of("key", "value")); + + CommandTO command = new CommandTO.Builder("command").args(args).build(); + + assertEquals("command", command.getKey()); + assertEquals(args, command.getArgs()); + } + + @Test + void commandTOWithoutArgs() { + CommandTO command = new CommandTO.Builder("command").build(); + + assertEquals("command", command.getKey()); + assertTrue(command.getArgs() == null); + } + + @Test + void commandArgs() { + CommandArgs args = new CommandArgs(); + + assertTrue(args.getCtx().isEmpty()); + + Map ctx = Map.of("key", "value"); + args.setCtx(ctx); + assertEquals(ctx, args.getCtx()); + } + + @Test + void commandOutput() { + CommandArgs args = new CommandArgs(); + CommandTO command = new CommandTO.Builder("command").args(args).build(); + + CommandOutput output = new CommandOutput.Builder(command).output("result").build(); + + assertEquals("command", output.getKey()); + assertEquals(args, output.getArgs()); + assertEquals("result", output.getOutput()); + } + + @Test + void commandOutputFromScratch() { + CommandOutput output = new CommandOutput.Builder("command").output("result").build(); + + assertEquals("command", output.getKey()); + assertEquals("result", output.getOutput()); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/form/FormTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/form/FormTest.java new file mode 100644 index 00000000000..7f9158f3b96 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/form/FormTest.java @@ -0,0 +1,143 @@ +/* + * 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.common.lib.form; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.regex.Pattern; +import org.junit.jupiter.api.Test; + +class FormTest { + + @Test + void formProperty() { + FormProperty property = new FormProperty(); + property.setId("id"); + property.setName("name"); + property.setType(FormPropertyType.String); + property.setReadable(true); + property.setWritable(true); + property.setRequired(true); + property.setStringRegEx(Pattern.compile("[a-z]+")); + property.setDatePattern("yyyy-MM-dd"); + property.setDropdownSingleSelection(false); + property.setDropdownFreeForm(true); + property.setMimeType("text/plain"); + property.setValue("value"); + property.getEnumValues().add(new FormPropertyValue("one", "1")); + property.getDropdownValues().add(new FormPropertyValue("two", "2")); + + assertEquals("id", property.getId()); + assertEquals("name", property.getName()); + assertEquals(FormPropertyType.String, property.getType()); + assertTrue(property.isReadable()); + assertTrue(property.isWritable()); + assertTrue(property.isRequired()); + assertEquals("[a-z]+", property.getStringRegEx().pattern()); + assertEquals("yyyy-MM-dd", property.getDatePattern()); + assertFalse(property.isDropdownSingleSelection()); + assertTrue(property.isDropdownFreeForm()); + assertEquals("text/plain", property.getMimeType()); + assertEquals("value", property.getValue()); + assertEquals(1, property.getEnumValues().size()); + assertEquals(1, property.getDropdownValues().size()); + } + + @Test + void formPropertyDefaults() { + FormProperty property = new FormProperty(); + + assertFalse(property.isReadable()); + assertFalse(property.isWritable()); + assertFalse(property.isRequired()); + assertTrue(property.isDropdownSingleSelection()); + assertFalse(property.isDropdownFreeForm()); + assertTrue(property.getEnumValues().isEmpty()); + assertTrue(property.getDropdownValues().isEmpty()); + } + + @Test + void formPropertyEqualsAndHashCode() { + FormProperty first = new FormProperty(); + first.setId("id"); + FormProperty second = new FormProperty(); + second.setId("id"); + FormProperty other = new FormProperty(); + other.setId("other"); + + assertEquals(first, first); + assertNotEquals(first, null); + assertNotEquals(first, "string"); + assertEquals(first, second); + assertEquals(first.hashCode(), second.hashCode()); + assertNotEquals(first, other); + } + + @Test + void formPropertyValue() { + FormPropertyValue value = new FormPropertyValue("key", "value"); + FormPropertyValue same = new FormPropertyValue("key", "value"); + FormPropertyValue other = new FormPropertyValue("key", "other"); + + assertEquals("key", value.getKey()); + assertEquals("value", value.getValue()); + assertEquals(value, value); + assertNotEquals(value, null); + assertNotEquals(value, "string"); + assertEquals(value, same); + assertEquals(value.hashCode(), same.hashCode()); + assertNotEquals(value, other); + } + + @Test + void syncopeForm() { + SyncopeForm form = new SyncopeForm(); + FormProperty property = new FormProperty(); + property.setId("id"); + form.getProperties().add(property); + + assertEquals(1, form.getProperties().size()); + assertTrue(form.getProperty("id").isPresent()); + assertFalse(form.getProperty("other").isPresent()); + assertEquals(property, form.getProperty("id").orElseThrow()); + } + + @Test + void syncopeFormEqualsAndHashCode() { + SyncopeForm first = new SyncopeForm(); + FormProperty property = new FormProperty(); + property.setId("id"); + first.getProperties().add(property); + + SyncopeForm second = new SyncopeForm(); + FormProperty sameProperty = new FormProperty(); + sameProperty.setId("id"); + second.getProperties().add(sameProperty); + + assertEquals(first, first); + assertNotEquals(first, null); + assertNotEquals(first, "string"); + assertEquals(first, second); + assertEquals(first.hashCode(), second.hashCode()); + assertNotEquals(first, new SyncopeForm()); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/jackson/SyncopeJsonMapperTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/jackson/SyncopeJsonMapperTest.java new file mode 100644 index 00000000000..0cfc90a1f06 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/jackson/SyncopeJsonMapperTest.java @@ -0,0 +1,81 @@ +/* + * 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.common.lib.jackson; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +import java.util.LinkedHashMap; +import java.util.Map; +import org.apache.syncope.common.lib.to.UserTO; +import org.junit.jupiter.api.Test; + +class SyncopeJsonMapperTest { + + private final SyncopeJsonMapper mapper = new SyncopeJsonMapper(); + + @Test + void unwrapSingleEntryMap() { + assertEquals("value", mapper.unwrapMap(Map.of("key", "value"))); + } + + @Test + void unwrapMultiEntryMap() { + Map map = new LinkedHashMap<>(); + map.put("one", "1"); + map.put("two", "2"); + + assertSame(map, mapper.unwrapMap(map)); + } + + @Test + void unwrapNonMap() { + assertEquals("value", mapper.unwrapMap("value")); + assertNull(mapper.unwrapMap(null)); + } + + @Test + void writeValueAsStringUnwrapsSingleEntryMap() { + assertEquals("\"value\"", mapper.writeValueAsString(Map.of("key", "value"))); + } + + @Test + void writeValueAsStringKeepsMultiEntryMap() { + Map map = new LinkedHashMap<>(); + map.put("one", "1"); + map.put("two", "2"); + + String json = mapper.writeValueAsString(map); + + assertEquals("{\"one\":\"1\",\"two\":\"2\"}", json); + } + + @Test + void roundTrip() { + UserTO user = new UserTO(); + user.setKey("key"); + user.setUsername("rossini"); + + String json = mapper.writeValueAsString(user); + + UserTO actual = mapper.readValue(json, UserTO.class); + assertEquals(user, actual); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/policy/PolicyConfsTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/policy/PolicyConfsTest.java new file mode 100644 index 00000000000..b5692eac31a --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/policy/PolicyConfsTest.java @@ -0,0 +1,155 @@ +/* + * 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.common.lib.policy; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class PolicyConfsTest { + + @Test + void defaultPasswordRuleConf() { + DefaultPasswordRuleConf conf = new DefaultPasswordRuleConf(); + + assertEquals(DefaultPasswordRuleConf.class.getName(), conf.getName()); + + conf.setName("conf"); + conf.setMaxLength(20); + conf.setMinLength(8); + conf.setAlphabetical(2); + conf.setUppercase(1); + conf.setLowercase(1); + conf.setDigit(1); + conf.setSpecial(1); + conf.setRepeatSame(2); + conf.setUsernameAllowed(false); + conf.setNotPermittedCaseSensitive(true); + conf.setNotPermittedAsSubstrings(true); + conf.setNotPermittedBackwards(true); + conf.getSpecialChars().add('!'); + conf.getIllegalChars().add('#'); + conf.getWordsNotPermitted().add("secret"); + conf.getSchemasNotPermitted().add("password"); + + assertEquals("conf", conf.getName()); + assertEquals(20, conf.getMaxLength()); + assertEquals(8, conf.getMinLength()); + assertEquals(2, conf.getAlphabetical()); + assertEquals(1, conf.getUppercase()); + assertEquals(1, conf.getLowercase()); + assertEquals(1, conf.getDigit()); + assertEquals(1, conf.getSpecial()); + assertEquals(2, conf.getRepeatSame()); + assertFalse(conf.isUsernameAllowed()); + assertTrue(conf.isNotPermittedCaseSensitive()); + assertTrue(conf.isNotPermittedAsSubstrings()); + assertTrue(conf.isNotPermittedBackwards()); + assertEquals(1, conf.getSpecialChars().size()); + assertEquals(1, conf.getIllegalChars().size()); + assertEquals(1, conf.getWordsNotPermitted().size()); + assertEquals(1, conf.getSchemasNotPermitted().size()); + } + + @Test + void abstractPasswordRuleConf() { + AbstractPasswordRuleConf conf = new AbstractPasswordRuleConf("name") { + }; + + assertEquals("name", conf.getName()); + } + + @Test + void defaultAccountRuleConf() { + DefaultAccountRuleConf conf = new DefaultAccountRuleConf(); + + assertEquals(DefaultAccountRuleConf.class.getName(), conf.getName()); + + conf.setName("conf"); + conf.setMaxLength(20); + conf.setMinLength(8); + conf.setPattern("[a-z]+"); + conf.setAllLowerCase(true); + conf.setAllUpperCase(true); + conf.getWordsNotPermitted().add("secret"); + conf.getSchemasNotPermitted().add("username"); + conf.getPrefixesNotPermitted().add("prefix"); + conf.getSuffixesNotPermitted().add("suffix"); + + assertEquals("conf", conf.getName()); + assertEquals(20, conf.getMaxLength()); + assertEquals(8, conf.getMinLength()); + assertEquals("[a-z]+", conf.getPattern()); + assertTrue(conf.isAllLowerCase()); + assertTrue(conf.isAllUpperCase()); + assertEquals(1, conf.getWordsNotPermitted().size()); + assertEquals(1, conf.getSchemasNotPermitted().size()); + assertEquals(1, conf.getPrefixesNotPermitted().size()); + assertEquals(1, conf.getSuffixesNotPermitted().size()); + } + + @Test + void abstractAccountRuleConf() { + AbstractAccountRuleConf conf = new AbstractAccountRuleConf("name") { + }; + + assertEquals("name", conf.getName()); + } + + @Test + void haveIBeenPwnedPasswordRuleConf() { + HaveIBeenPwnedPasswordRuleConf conf = new HaveIBeenPwnedPasswordRuleConf(); + + assertEquals(HaveIBeenPwnedPasswordRuleConf.class.getName(), conf.getName()); + } + + @Test + void passwordPolicyTO() { + PasswordPolicyTO policy = new PasswordPolicyTO(); + policy.setName("password"); + policy.setAllowNullPassword(true); + policy.setHistoryLength(5); + policy.getRules().add(DefaultPasswordRuleConf.class.getName()); + + assertEquals("password", policy.getName()); + assertEquals(PasswordPolicyTO.class.getName(), policy.getDiscriminator()); + assertTrue(policy.isAllowNullPassword()); + assertEquals(5, policy.getHistoryLength()); + assertEquals(1, policy.getRules().size()); + } + + @Test + void accountPolicyTO() { + AccountPolicyTO policy = new AccountPolicyTO(); + policy.setName("account"); + policy.setPropagateSuspension(true); + policy.setMaxAuthenticationAttempts(3); + policy.getRules().add(DefaultAccountRuleConf.class.getName()); + policy.getPassthroughResources().add("resource"); + + assertEquals("account", policy.getName()); + assertEquals(AccountPolicyTO.class.getName(), policy.getDiscriminator()); + assertTrue(policy.isPropagateSuspension()); + assertEquals(3, policy.getMaxAuthenticationAttempts()); + assertEquals(1, policy.getRules().size()); + assertEquals(1, policy.getPassthroughResources().size()); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/request/PatchTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/request/PatchTest.java new file mode 100644 index 00000000000..3351774610e --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/request/PatchTest.java @@ -0,0 +1,164 @@ +/* + * 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.common.lib.request; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Set; +import org.apache.syncope.common.lib.Attr; +import org.apache.syncope.common.lib.types.PatchOperation; +import org.apache.syncope.common.lib.types.ResourceAssociationAction; +import org.apache.syncope.common.lib.types.ResourceDeassociationAction; +import org.apache.syncope.common.lib.types.StatusRType; +import org.junit.jupiter.api.Test; + +class PatchTest { + + @Test + void stringPatchItem() { + StringPatchItem item = new StringPatchItem.Builder().value("value").build(); + + assertEquals("value", item.getValue()); + assertEquals(PatchOperation.ADD_REPLACE, item.getOperation()); + + item = new StringPatchItem.Builder().operation(PatchOperation.DELETE).value("value").build(); + assertEquals(PatchOperation.DELETE, item.getOperation()); + } + + @Test + void stringReplacePatchItem() { + StringReplacePatchItem item = new StringReplacePatchItem.Builder().value("value").build(); + + assertEquals("value", item.getValue()); + assertEquals(PatchOperation.ADD_REPLACE, item.getOperation()); + + item.setOperation(PatchOperation.DELETE); + assertEquals(PatchOperation.ADD_REPLACE, item.getOperation()); + } + + @Test + void booleanReplacePatchItem() { + BooleanReplacePatchItem item = new BooleanReplacePatchItem.Builder().value(true).build(); + + assertTrue(item.getValue()); + assertEquals(PatchOperation.ADD_REPLACE, item.getOperation()); + } + + @Test + void longPatchItem() { + LongPatchItem item = new LongPatchItem.Builder().value(10L).operation(PatchOperation.DELETE).build(); + + assertEquals(10L, item.getValue()); + assertEquals(PatchOperation.DELETE, item.getOperation()); + } + + @Test + void passwordPatch() { + PasswordPatch patch = new PasswordPatch.Builder(). + value("password"). + onSyncope(false). + resource("one"). + resources("two", "three"). + build(); + + assertEquals("password", patch.getValue()); + assertFalse(patch.isOnSyncope()); + assertEquals(3, patch.getResources().size()); + assertTrue(patch.getResources().containsAll(Set.of("one", "two", "three"))); + + PasswordPatch same = new PasswordPatch.Builder(). + value("password"). + onSyncope(false). + resources("one", "two", "three"). + build(); + PasswordPatch other = new PasswordPatch.Builder().value("other").build(); + + assertEquals(patch, same); + assertEquals(patch.hashCode(), same.hashCode()); + assertNotEquals(patch, other); + + patch.setOperation(PatchOperation.DELETE); + assertEquals(PatchOperation.ADD_REPLACE, patch.getOperation()); + } + + @Test + void attrPatch() { + AttrPatch empty = new AttrPatch.Builder(new Attr.Builder("schema").build()).build(); + assertTrue(empty.isEmpty()); + + AttrPatch notEmpty = new AttrPatch.Builder(new Attr.Builder("schema").value("value").build()).build(); + assertFalse(notEmpty.isEmpty()); + + AttrPatch noAttr = new AttrPatch.Builder(null).build(); + assertTrue(noAttr.isEmpty()); + assertNull(noAttr.getAttr()); + + notEmpty.setOperation(PatchOperation.DELETE); + assertEquals(PatchOperation.DELETE, notEmpty.getOperation()); + } + + @Test + void statusR() { + StatusR status = new StatusR.Builder("key", StatusRType.SUSPEND). + token("token"). + onSyncope(false). + resource("resource"). + build(); + + assertEquals("key", status.getKey()); + assertEquals(StatusRType.SUSPEND, status.getType()); + assertEquals("token", status.getToken()); + assertFalse(status.isOnSyncope()); + assertEquals(1, status.getResources().size()); + } + + @Test + void resourceAR() { + ResourceAR request = new ResourceAR.Builder(). + key("key"). + action(ResourceAssociationAction.ASSIGN). + onSyncope(false). + resource("resource"). + value("password"). + build(); + + assertEquals("key", request.getKey()); + assertEquals(ResourceAssociationAction.ASSIGN, request.getAction()); + assertFalse(request.isOnSyncope()); + assertEquals(1, request.getResources().size()); + assertEquals("password", request.getValue()); + } + + @Test + void resourceDR() { + ResourceDR request = new ResourceDR.Builder(). + key("key"). + action(ResourceDeassociationAction.UNASSIGN). + resource("resource"). + build(); + + assertEquals("key", request.getKey()); + assertEquals(ResourceDeassociationAction.UNASSIGN, request.getAction()); + assertEquals(1, request.getResources().size()); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/request/RequestBuildersTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/request/RequestBuildersTest.java new file mode 100644 index 00000000000..05dd64fcaba --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/request/RequestBuildersTest.java @@ -0,0 +1,210 @@ +/* + * 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.common.lib.request; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.Set; +import org.apache.syncope.common.lib.Attr; +import org.apache.syncope.common.lib.to.LinkedAccountTO; +import org.apache.syncope.common.lib.to.MembershipTO; +import org.apache.syncope.common.lib.to.RelationshipTO; +import org.apache.syncope.common.lib.to.TypeExtensionTO; +import org.junit.jupiter.api.Test; + +class RequestBuildersTest { + + @Test + void userCR() { + UserCR user = new UserCR.Builder("/", "rossini"). + password("password"). + storePassword(false). + securityQuestion("question"). + securityAnswer("answer"). + mustChangePassword(true). + membership(new MembershipTO.Builder("group").build()). + memberships(new MembershipTO.Builder("group2").build()). + role("role"). + roles("role2", "role3"). + linkedAccount(new LinkedAccountTO.Builder("resource", "key").build()). + uManager("uManager"). + gManager("gManager"). + auxClass("aux"). + auxClasses("aux2", "aux3"). + plainAttr(new Attr.Builder("plain").value("value").build()). + resource("resource"). + relationship(new RelationshipTO.Builder("type").otherEnd("other").build()). + build(); + + assertEquals("/", user.getRealm()); + assertEquals("rossini", user.getUsername()); + assertEquals("password", user.getPassword()); + assertFalse(user.isStorePassword()); + assertEquals("question", user.getSecurityQuestion()); + assertEquals("answer", user.getSecurityAnswer()); + assertTrue(user.isMustChangePassword()); + assertEquals(2, user.getMemberships().size()); + assertEquals(Set.of("role", "role2", "role3"), user.getRoles()); + assertEquals(1, user.getLinkedAccounts().size()); + assertEquals("uManager", user.getuManager()); + assertEquals("gManager", user.getgManager()); + assertTrue(user.getAuxClasses().containsAll(Set.of("aux", "aux2", "aux3"))); + assertTrue(user.getPlainAttr("plain").isPresent()); + assertEquals(1, user.getResources().size()); + assertEquals(1, user.getRelationships().size()); + assertEquals(UserCR.class.getName(), user.getDiscriminator()); + } + + @Test + void groupCR() { + GroupCR group = new GroupCR.Builder("/", "name"). + typeExtension(new TypeExtensionTO()). + typeExtensions(new TypeExtensionTO(), new TypeExtensionTO()). + auxClasses(List.of("aux")). + plainAttrs(List.of(new Attr.Builder("plain").value("value").build())). + resources(List.of("resource")). + relationships(List.of(new RelationshipTO.Builder("type").otherEnd("other").build())). + build(); + + assertEquals("/", group.getRealm()); + assertEquals("name", group.getName()); + assertEquals(3, group.getTypeExtensions().size()); + assertEquals(1, group.getAuxClasses().size()); + assertEquals(1, group.getPlainAttrs().size()); + assertEquals(1, group.getResources().size()); + assertEquals(1, group.getRelationships().size()); + assertEquals(GroupCR.class.getName(), group.getDiscriminator()); + } + + @Test + void anyObjectCR() { + AnyObjectCR anyObject = new AnyObjectCR.Builder("/", "PRINTER", "name"). + membership(new MembershipTO.Builder("group").build()). + memberships(List.of(new MembershipTO.Builder("group2").build())). + build(); + + assertEquals("/", anyObject.getRealm()); + assertEquals("PRINTER", anyObject.getType()); + assertEquals("name", anyObject.getName()); + assertEquals(2, anyObject.getMemberships().size()); + assertEquals(AnyObjectCR.class.getName(), anyObject.getDiscriminator()); + } + + @Test + void anyCREmpty() { + AnyObjectCR anyObject = new AnyObjectCR(); + + assertTrue(anyObject.getPlainAttr("plain").isEmpty()); + assertTrue(anyObject.getDerAttr("derived").isEmpty()); + assertTrue(anyObject.getRelationship("type", "other").isEmpty()); + assertTrue(anyObject.getDerAttrs().isEmpty()); + } + + @Test + void userUR() { + UserUR user = new UserUR.Builder("key"). + username(new StringReplacePatchItem.Builder().value("username").build()). + password(new PasswordPatch.Builder().value("password").build()). + securityQuestion(new StringReplacePatchItem.Builder().value("question").build()). + securityAnswer(new StringReplacePatchItem.Builder().value("answer").build()). + mustChangePassword(new BooleanReplacePatchItem.Builder().value(true).build()). + membership(new MembershipUR.Builder("group").build()). + memberships(List.of(new MembershipUR.Builder("group2").build())). + role(new StringPatchItem.Builder().value("role").build()). + roles(List.of(new StringPatchItem.Builder().value("role2").build())). + auxClasses(List.of(new StringPatchItem.Builder().value("aux").build())). + plainAttrs(List.of(new AttrPatch.Builder(new Attr.Builder("plain").value("value").build()).build())). + resources(List.of(new StringPatchItem.Builder().value("resource").build())). + relationships(List.of(new RelationshipUR.Builder("type").otherEnd("other").build())). + build(); + + assertEquals("key", user.getKey()); + assertEquals("username", user.getUsername().getValue()); + assertEquals("password", user.getPassword().getValue()); + assertEquals("question", user.getSecurityQuestion().getValue()); + assertEquals("answer", user.getSecurityAnswer().getValue()); + assertTrue(user.getMustChangePassword().getValue()); + assertEquals(2, user.getMemberships().size()); + assertEquals(2, user.getRoles().size()); + assertEquals(1, user.getAuxClasses().size()); + assertEquals(1, user.getPlainAttrs().size()); + assertEquals(1, user.getResources().size()); + assertEquals(1, user.getRelationships().size()); + assertEquals(UserUR.class.getName(), user.getDiscriminator()); + } + + @Test + void userUREmpty() { + UserUR user = new UserUR(); + assertTrue(user.isEmpty()); + assertFalse(user.isEmptyButPassword()); + assertFalse(user.requiresApproval()); + + user.setPassword(new PasswordPatch.Builder().value("password").build()); + assertTrue(user.isEmptyButPassword()); + assertFalse(user.isEmpty()); + } + + @Test + void userURRequiresApproval() { + UserUR user = new UserUR(); + user.setgManager(new StringReplacePatchItem.Builder().value("gManager").build()); + assertTrue(user.requiresApproval()); + + user = new UserUR(); + user.getRoles().add(new StringPatchItem.Builder().value("role").build()); + assertTrue(user.requiresApproval()); + } + + @Test + void groupUR() { + TypeExtensionTO named = new TypeExtensionTO(); + named.setAnyType("USER"); + + GroupUR group = new GroupUR.Builder("key"). + name(new StringReplacePatchItem.Builder().value("name").build()). + typeExtension(named). + typeExtensions(List.of(new TypeExtensionTO())). + build(); + + assertEquals("key", group.getKey()); + assertEquals("name", group.getName().getValue()); + assertEquals(2, group.getTypeExtensions().size()); + assertTrue(group.getTypeExtension("USER").isPresent()); + assertFalse(group.getTypeExtension(null).isPresent()); + assertFalse(group.isEmpty()); + } + + @Test + void anyObjectUR() { + AnyObjectUR anyObject = new AnyObjectUR.Builder("key"). + name(new StringReplacePatchItem.Builder().value("name").build()). + membership(new MembershipUR.Builder("group").build()). + memberships(List.of(new MembershipUR.Builder("group2").build())). + build(); + + assertEquals("key", anyObject.getKey()); + assertEquals("name", anyObject.getName().getValue()); + assertEquals(2, anyObject.getMemberships().size()); + assertFalse(anyObject.isEmpty()); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/search/FiqlSearchConditionBuildersTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/search/FiqlSearchConditionBuildersTest.java new file mode 100644 index 00000000000..057601acfe8 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/search/FiqlSearchConditionBuildersTest.java @@ -0,0 +1,187 @@ +/* + * 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.common.lib.search; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.apache.cxf.jaxrs.ext.search.SearchUtils; +import org.apache.cxf.jaxrs.ext.search.client.CompleteCondition; +import org.junit.jupiter.api.Test; + +class FiqlSearchConditionBuildersTest { + + @Test + void contextualProperties() { + assertEquals("true", AbstractFiqlSearchConditionBuilder.CONTEXTUAL_PROPERTIES.get( + SearchUtils.LAX_PROPERTY_MATCH)); + } + + @Test + void user() { + UserFiqlSearchConditionBuilder builder = new UserFiqlSearchConditionBuilder(); + assertEquals("username==rossini", builder.is("username").equalTo("rossini").query()); + assertEquals("username=~rossini", builder.is("username").equalToIgnoreCase("rossini").query()); + assertEquals("username!~rossini", builder.is("username").notEqualTolIgnoreCase("rossini").query()); + assertEquals("loginDate==$null", builder.is("loginDate").nullValue().query()); + assertEquals("loginDate!=$null", builder.is("loginDate").notNullValue().query()); + assertEquals("$auxClasses==clazz", builder.hasAuxClasses("clazz").query()); + assertEquals("$auxClasses!=clazz", builder.hasNotAuxClasses("clazz").query()); + assertEquals("$resources==resource", builder.hasResources("resource").query()); + assertEquals("$resources!=resource", builder.hasNotResources("resource").query()); + assertEquals("$groups==group", builder.inGroups("group").query()); + assertEquals("$groups!=group", builder.notInGroups("group").query()); + assertEquals("$relationships==anyObject", builder.inRelationships("anyObject").query()); + assertEquals("$relationships!=anyObject", builder.notInRelationships("anyObject").query()); + assertEquals("$relationshipTypes==type", builder.inRelationshipTypes("type").query()); + assertEquals("$relationshipTypes!=type", builder.notInRelationshipTypes("type").query()); + assertEquals("$roles==role", builder.inRoles("role").query()); + assertEquals("$roles!=role", builder.notInRoles("role").query()); + } + + @Test + void userMultipleValues() { + UserFiqlSearchConditionBuilder builder = new UserFiqlSearchConditionBuilder(); + String fiql = builder.hasAuxClasses("one", "two").query(); + assertTrue(fiql.startsWith(SpecialAttr.AUX_CLASSES + "==")); + assertTrue(fiql.contains("one")); + assertTrue(fiql.contains("two")); + + fiql = builder.hasNotAuxClasses("one", "two").query(); + assertTrue(fiql.startsWith(SpecialAttr.AUX_CLASSES + "!=")); + assertTrue(fiql.contains("one")); + assertTrue(fiql.contains("two")); + + fiql = builder.hasResources("one", "two").query(); + assertTrue(fiql.startsWith(SpecialAttr.RESOURCES + "==")); + + fiql = builder.hasNotResources("one", "two").query(); + assertTrue(fiql.startsWith(SpecialAttr.RESOURCES + "!=")); + + fiql = builder.inGroups("one", "two").query(); + assertTrue(fiql.startsWith(SpecialAttr.GROUPS + "==")); + + fiql = builder.notInGroups("one", "two").query(); + assertTrue(fiql.startsWith(SpecialAttr.GROUPS + "!=")); + + fiql = builder.inRelationships("one", "two").query(); + assertTrue(fiql.startsWith(SpecialAttr.RELATIONSHIPS + "==")); + + fiql = builder.notInRelationships("one", "two").query(); + assertTrue(fiql.startsWith(SpecialAttr.RELATIONSHIPS + "!=")); + + fiql = builder.inRelationshipTypes("one", "two").query(); + assertTrue(fiql.startsWith(SpecialAttr.RELATIONSHIP_TYPES + "==")); + + fiql = builder.notInRelationshipTypes("one", "two").query(); + assertTrue(fiql.startsWith(SpecialAttr.RELATIONSHIP_TYPES + "!=")); + + fiql = builder.inRoles("one", "two").query(); + assertTrue(fiql.startsWith(SpecialAttr.ROLES + "==")); + + fiql = builder.notInRoles("one", "two").query(); + assertTrue(fiql.startsWith(SpecialAttr.ROLES + "!=")); + } + + @Test + void userComposition() { + UserFiqlSearchConditionBuilder builder = new UserFiqlSearchConditionBuilder(); + assertEquals("username==a;username==b", + builder.is("username").equalTo("a").and("username").equalTo("b").query()); + assertEquals("username==a,username==b", + builder.is("username").equalTo("a").or("username").equalTo("b").query()); + + UserCompleteCondition first = (UserCompleteCondition) builder.is("username").equalTo("a"); + UserCompleteCondition second = (UserCompleteCondition) builder.is("username").equalTo("b"); + + UserPartialCondition partial = (UserPartialCondition) builder.is("username").equalTo("a").and(); + String varargsAnd = partial.and(first, second).query(); + assertTrue(varargsAnd.contains("username==a")); + assertTrue(varargsAnd.contains("username==b")); + assertTrue(varargsAnd.contains(";")); + + String listAnd = partial.and(List.of(first, second)).query(); + assertTrue(listAnd.contains("username==a")); + assertTrue(listAnd.contains("username==b")); + assertTrue(listAnd.contains(";")); + + UserPartialCondition orPartial = (UserPartialCondition) builder.is("username").equalTo("a").or(); + String varargsOr = orPartial.or(first, second).query(); + assertTrue(varargsOr.contains("username==a")); + assertTrue(varargsOr.contains("username==b")); + assertTrue(varargsOr.contains(",")); + + String listOr = builder.is("username").equalTo("a").or().or(List.of(first, second)).query(); + assertTrue(listOr.contains("username==a")); + assertTrue(listOr.contains("username==b")); + assertTrue(listOr.contains(",")); + } + + @Test + void group() { + GroupFiqlSearchConditionBuilder builder = new GroupFiqlSearchConditionBuilder(); + assertEquals("name==group", builder.is("name").equalTo("group").query()); + assertEquals("$member==member", builder.withMembers("member").query()); + assertEquals("$member!=member", builder.withoutMembers("member").query()); + assertTrue(builder.withMembers("one", "two").query().startsWith(SpecialAttr.MEMBER + "==")); + assertTrue(builder.withoutMembers("one", "two").query().startsWith(SpecialAttr.MEMBER + "!=")); + } + + @Test + void anyObject() { + AnyObjectFiqlSearchConditionBuilder builder = new AnyObjectFiqlSearchConditionBuilder("PRINTER"); + assertEquals("$type==PRINTER", builder.query()); + assertEquals("name==printer;$type==PRINTER", builder.is("name").equalTo("printer").query()); + assertEquals("$groups==group;$type==PRINTER", builder.inGroups("group").query()); + assertEquals("$groups!=group;$type==PRINTER", builder.notInGroups("group").query()); + assertEquals("$relationships==anyObject;$type==PRINTER", builder.inRelationships("anyObject").query()); + assertEquals("$relationships!=anyObject;$type==PRINTER", builder.notInRelationships("anyObject").query()); + assertEquals("$relationshipTypes==type;$type==PRINTER", builder.inRelationshipTypes("type").query()); + assertEquals("$relationshipTypes!=type;$type==PRINTER", builder.notInRelationshipTypes("type").query()); + } + + @Test + void realm() { + RealmFiqlSearchConditionBuilder builder = new RealmFiqlSearchConditionBuilder(); + assertEquals("name==realm", builder.is("name").equalTo("realm").query()); + assertEquals("name=~realm", builder.is("name").equalToIgnoreCase("realm").query()); + assertEquals("name!~realm", builder.is("name").notEqualTolIgnoreCase("realm").query()); + assertEquals("name==$null", builder.is("name").nullValue().query()); + assertEquals("name!=$null", builder.is("name").notNullValue().query()); + assertEquals("$auxClasses==clazz", builder.hasAuxClasses("clazz").query()); + } + + @Test + void connObject() { + ConnObjectTOFiqlSearchConditionBuilder builder = new ConnObjectTOFiqlSearchConditionBuilder(); + assertEquals("username==rossini", builder.is("username").equalTo("rossini").query()); + assertEquals("username=~rossini", builder.is("username").equalToIgnoreCase("rossini").query()); + assertEquals("username!~rossini", builder.is("username").notEqualTolIgnoreCase("rossini").query()); + assertEquals("loginDate==$null", builder.is("loginDate").nullValue().query()); + assertEquals("loginDate!=$null", builder.is("loginDate").notNullValue().query()); + } + + @Test + void completeConditionAsObject() { + UserFiqlSearchConditionBuilder builder = new UserFiqlSearchConditionBuilder(); + CompleteCondition condition = builder.is("username").equalTo("rossini"); + assertEquals("username==rossini", condition.query()); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/search/OrderByClauseBuilderTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/search/OrderByClauseBuilderTest.java new file mode 100644 index 00000000000..fa845c01b96 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/search/OrderByClauseBuilderTest.java @@ -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. + */ +package org.apache.syncope.common.lib.search; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class OrderByClauseBuilderTest { + + @Test + void empty() { + assertEquals("", new OrderByClauseBuilder().build()); + } + + @Test + void ascending() { + assertEquals("username ASC", new OrderByClauseBuilder().asc("username").build()); + assertEquals("username ASC,key ASC", + new OrderByClauseBuilder().asc("username").asc("key").build()); + } + + @Test + void descending() { + assertEquals("username DESC", new OrderByClauseBuilder().desc("username").build()); + assertEquals("username DESC,key DESC", + new OrderByClauseBuilder().desc("username").desc("key").build()); + } + + @Test + void mixed() { + assertEquals("username ASC,key DESC", + new OrderByClauseBuilder().asc("username").desc("key").build()); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/search/SearchableFieldsTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/search/SearchableFieldsTest.java new file mode 100644 index 00000000000..8370def04f1 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/search/SearchableFieldsTest.java @@ -0,0 +1,79 @@ +/* + * 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.common.lib.search; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Map; +import org.apache.syncope.common.lib.to.AnyObjectTO; +import org.apache.syncope.common.lib.to.GroupTO; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.junit.jupiter.api.Test; + +class SearchableFieldsTest { + + @Test + void contains() { + assertTrue(SearchableFields.contains("username")); + assertTrue(SearchableFields.contains("name")); + assertTrue(SearchableFields.contains("key")); + assertFalse(SearchableFields.contains("nonExistent")); + assertFalse(SearchableFields.contains("password")); + } + + @Test + void user() { + Map fields = SearchableFields.get(UserTO.class); + + assertEquals(AttrSchemaType.String, fields.get("username")); + assertEquals(AttrSchemaType.Date, fields.get("lastLoginDate")); + assertEquals(AttrSchemaType.Boolean, fields.get("suspended")); + assertEquals(AttrSchemaType.Boolean, fields.get("mustChangePassword")); + assertEquals(AttrSchemaType.Date, fields.get("creationDate")); + assertEquals(AttrSchemaType.String, fields.get("realm")); + + assertFalse(fields.containsKey("password")); + assertFalse(fields.containsKey("type")); + assertFalse(fields.containsKey("discriminator")); + assertFalse(fields.containsKey("memberships")); + assertFalse(fields.containsKey("roles")); + assertFalse(fields.containsKey("linkedAccounts")); + } + + @Test + void group() { + Map fields = SearchableFields.get(GroupTO.class); + + assertEquals(AttrSchemaType.String, fields.get("name")); + assertEquals(AttrSchemaType.Long, fields.get("userMembershipCount")); + assertEquals(AttrSchemaType.Long, fields.get("anyObjectMembershipCount")); + assertFalse(fields.containsKey("typeExtensions")); + } + + @Test + void anyObject() { + Map fields = SearchableFields.get(AnyObjectTO.class); + + assertEquals(AttrSchemaType.String, fields.get("name")); + assertFalse(fields.containsKey("memberships")); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/search/SpecialAttrTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/search/SpecialAttrTest.java new file mode 100644 index 00000000000..c036ea37998 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/search/SpecialAttrTest.java @@ -0,0 +1,57 @@ +/* + * 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.common.lib.search; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Optional; +import org.junit.jupiter.api.Test; + +class SpecialAttrTest { + + @Test + void literals() { + assertEquals("$null", SpecialAttr.NULL.toString()); + assertEquals("$type", SpecialAttr.TYPE.toString()); + assertEquals("$auxClasses", SpecialAttr.AUX_CLASSES.toString()); + assertEquals("$resources", SpecialAttr.RESOURCES.toString()); + assertEquals("$groups", SpecialAttr.GROUPS.toString()); + assertEquals("$relationships", SpecialAttr.RELATIONSHIPS.toString()); + assertEquals("$relationshipTypes", SpecialAttr.RELATIONSHIP_TYPES.toString()); + assertEquals("$roles", SpecialAttr.ROLES.toString()); + assertEquals("$member", SpecialAttr.MEMBER.toString()); + } + + @Test + void fromString() { + for (SpecialAttr specialAttr : SpecialAttr.values()) { + Optional actual = SpecialAttr.fromString(specialAttr.toString()); + assertTrue(actual.isPresent()); + assertEquals(specialAttr, actual.get()); + } + } + + @Test + void fromUnknownString() { + assertFalse(SpecialAttr.fromString("$unknown").isPresent()); + assertFalse(SpecialAttr.fromString(null).isPresent()); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/search/SyncopeFiqlParserTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/search/SyncopeFiqlParserTest.java index e8fb655c133..df6a33b4157 100644 --- a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/search/SyncopeFiqlParserTest.java +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/search/SyncopeFiqlParserTest.java @@ -19,15 +19,30 @@ package org.apache.syncope.common.lib.search; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.cxf.jaxrs.ext.search.ConditionType; import org.apache.cxf.jaxrs.ext.search.SearchBean; import org.apache.cxf.jaxrs.ext.search.SearchCondition; +import org.apache.cxf.jaxrs.ext.search.SearchParseException; import org.apache.cxf.jaxrs.ext.search.fiql.FiqlParser; import org.junit.jupiter.api.Test; -public class SyncopeFiqlParserTest { +class SyncopeFiqlParserTest { + + public static class TestBean { + + private String name; + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + } private static final SyncopeFiqlParser FIQL_PARSER = new SyncopeFiqlParser<>( SearchBean.class, AbstractFiqlSearchConditionBuilder.CONTEXTUAL_PROPERTIES); @@ -39,7 +54,7 @@ private static SyncopeFiqlSearchCondition parse(final String fiql) { } @Test - public void testEqualsIgnoreCase() { + void equalsIgnoreCase() { SyncopeFiqlSearchCondition cond = parse("name=~ami*"); assertEquals(SyncopeFiqlParser.IEQ, cond.getOperator()); assertEquals(ConditionType.CUSTOM, cond.getConditionType()); @@ -47,7 +62,7 @@ public void testEqualsIgnoreCase() { } @Test - public void testNotEqualsIgnoreCase() { + void notEqualsIgnoreCase() { SyncopeFiqlSearchCondition cond = parse("name!~ami*"); assertEquals(SyncopeFiqlParser.NIEQ, cond.getOperator()); assertEquals(ConditionType.CUSTOM, cond.getConditionType()); @@ -58,11 +73,63 @@ public void testNotEqualsIgnoreCase() { * Simple test for ensuring there's no regression. */ @Test - public void testEquals() { + void equals() { SyncopeFiqlSearchCondition cond = parse("name==ami*"); assertEquals(FiqlParser.EQ, cond.getOperator()); assertEquals(ConditionType.EQUALS, cond.getConditionType()); assertEquals("ami*", cond.getCondition().get("name")); } + @Test + void notEquals() { + SyncopeFiqlSearchCondition cond = parse("name!=ami*"); + assertEquals(FiqlParser.NEQ, cond.getOperator()); + assertEquals(ConditionType.NOT_EQUALS, cond.getConditionType()); + } + + @Test + void comparators() { + assertEquals(ConditionType.GREATER_THAN, parse("age=gt=10").getConditionType()); + assertEquals(ConditionType.GREATER_OR_EQUALS, parse("age=ge=10").getConditionType()); + assertEquals(ConditionType.LESS_THAN, parse("age=lt=10").getConditionType()); + assertEquals(ConditionType.LESS_OR_EQUALS, parse("age=le=10").getConditionType()); + } + + @Test + void and() { + SearchCondition parsed = FIQL_PARSER.parse("name==a;key==b"); + + assertEquals(ConditionType.AND, parsed.getConditionType()); + assertEquals(2, parsed.getSearchConditions().size()); + } + + @Test + void or() { + SearchCondition parsed = FIQL_PARSER.parse("name==a,key==b"); + + assertEquals(ConditionType.OR, parsed.getConditionType()); + assertEquals(2, parsed.getSearchConditions().size()); + } + + @Test + void missingValue() { + assertThrows(SearchParseException.class, () -> FIQL_PARSER.parse("name==")); + } + + @Test + void notAComparison() { + assertThrows(SearchParseException.class, () -> FIQL_PARSER.parse("name")); + } + + @Test + void bean() { + SyncopeFiqlParser parser = new SyncopeFiqlParser<>( + TestBean.class, AbstractFiqlSearchConditionBuilder.CONTEXTUAL_PROPERTIES); + + SearchCondition parsed = parser.parse("name=~ami*"); + + assertTrue(parsed instanceof SyncopeFiqlSearchCondition); + assertEquals(SyncopeFiqlParser.IEQ, ((SyncopeFiqlSearchCondition) parsed).getOperator()); + assertEquals(ConditionType.CUSTOM, parsed.getConditionType()); + } } diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/AnyTOTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/AnyTOTest.java new file mode 100644 index 00000000000..0119406acc8 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/AnyTOTest.java @@ -0,0 +1,129 @@ +/* + * 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.common.lib.to; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import org.apache.syncope.common.lib.Attr; +import org.junit.jupiter.api.Test; + +class AnyTOTest { + + private static final OffsetDateTime CREATION = OffsetDateTime.of(2026, 1, 1, 10, 0, 0, 0, ZoneOffset.UTC); + + private static final OffsetDateTime LAST_CHANGE = OffsetDateTime.of(2026, 2, 1, 10, 0, 0, 0, ZoneOffset.UTC); + + @Test + void etag() { + UserTO user = new UserTO(); + assertEquals("", user.getETagValue()); + + user.setCreationDate(CREATION); + assertEquals(String.valueOf(CREATION.toInstant().toEpochMilli()), user.getETagValue()); + + user.setLastChangeDate(LAST_CHANGE); + assertEquals(String.valueOf(LAST_CHANGE.toInstant().toEpochMilli()), user.getETagValue()); + } + + @Test + void plainAndDerivedAttrs() { + UserTO user = new UserTO(); + user.getPlainAttrs().add(new Attr.Builder("username").value("rossini").build()); + user.getDerAttrs().add(new Attr.Builder("derived").value("value").build()); + + assertTrue(user.getPlainAttr("username").isPresent()); + assertFalse(user.getPlainAttr("other").isPresent()); + assertTrue(user.getDerAttr("derived").isPresent()); + assertFalse(user.getDerAttr("other").isPresent()); + + assertEquals(1, user.getPlainAttrs().size()); + assertEquals(1, user.getDerAttrs().size()); + } + + @Test + void groupsAndRelationships() { + UserTO user = new UserTO(); + user.getRelationships().add(new RelationshipTO.Builder("type").otherEnd("other").build()); + user.getAuxClasses().add("aux"); + user.getResources().add("resource"); + + assertEquals(1, user.getRelationships().size()); + assertEquals(1, user.getAuxClasses().size()); + assertEquals(1, user.getResources().size()); + } + + @Test + void equalsAndHashCode() { + UserTO first = new UserTO(); + first.setKey("key"); + first.setUsername("username"); + UserTO second = new UserTO(); + second.setKey("key"); + second.setUsername("username"); + UserTO other = new UserTO(); + other.setKey("key"); + other.setUsername("other"); + + assertEquals(first, first); + assertNotEquals(first, null); + assertNotEquals(first, "string"); + assertEquals(first, second); + assertNotEquals(first, other); + } + + @Test + void discriminator() { + assertEquals(UserTO.class.getName(), new UserTO().getDiscriminator()); + assertEquals(GroupTO.class.getName(), new GroupTO().getDiscriminator()); + assertEquals(AnyObjectTO.class.getName(), new AnyObjectTO().getDiscriminator()); + + UserTO user = new UserTO(); + user.setDiscriminator("ignored"); + assertEquals(UserTO.class.getName(), user.getDiscriminator()); + } + + @Test + void statusAndManagers() { + AnyObjectTO anyObject = new AnyObjectTO(); + anyObject.setStatus("active"); + anyObject.setuManager("uManager"); + anyObject.setgManager("gManager"); + anyObject.setCreator("creator"); + anyObject.setCreationContext("creationContext"); + anyObject.setLastModifier("lastModifier"); + anyObject.setLastChangeContext("lastChangeContext"); + anyObject.setType("PRINTER"); + anyObject.setRealm("/"); + + assertEquals("active", anyObject.getStatus()); + assertEquals("uManager", anyObject.getuManager()); + assertEquals("gManager", anyObject.getgManager()); + assertEquals("creator", anyObject.getCreator()); + assertEquals("creationContext", anyObject.getCreationContext()); + assertEquals("lastModifier", anyObject.getLastModifier()); + assertEquals("lastChangeContext", anyObject.getLastChangeContext()); + assertEquals("PRINTER", anyObject.getType()); + assertEquals("/", anyObject.getRealm()); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/AttrContainersTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/AttrContainersTest.java new file mode 100644 index 00000000000..652deb076a2 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/AttrContainersTest.java @@ -0,0 +1,122 @@ +/* + * 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.common.lib.to; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.apache.syncope.common.lib.Attr; +import org.junit.jupiter.api.Test; + +class AttrContainersTest { + + @Test + void relationship() { + RelationshipTO relationship = new RelationshipTO.Builder("type", RelationshipTO.End.RIGHT). + otherEnd("otherType", "otherKey", "otherName"). + plainAttr(new Attr.Builder("plain").value("value").build()). + plainAttrs(List.of(new Attr.Builder("plain2").value("value").build())). + build(); + + assertEquals("type", relationship.getType()); + assertEquals(RelationshipTO.End.RIGHT, relationship.getEnd()); + assertEquals("otherType", relationship.getOtherEndType()); + assertEquals("otherKey", relationship.getOtherEndKey()); + assertEquals("otherName", relationship.getOtherEndName()); + assertEquals(2, relationship.getPlainAttrs().size()); + assertTrue(relationship.getPlainAttr("plain").isPresent()); + assertFalse(relationship.getPlainAttr("other").isPresent()); + assertFalse(relationship.getDerAttr("other").isPresent()); + assertTrue(relationship.toString().contains("type")); + } + + @Test + void relationshipDefaults() { + RelationshipTO relationship = new RelationshipTO.Builder("type").build(); + + assertEquals(RelationshipTO.End.LEFT, relationship.getEnd()); + } + + @Test + void membership() { + MembershipTO membership = new MembershipTO.Builder("group"). + groupName("groupName"). + plainAttr(new Attr.Builder("plain").value("value").build()). + plainAttrs(List.of(new Attr.Builder("plain2").value("value").build())). + build(); + + assertEquals("group", membership.getGroupKey()); + assertEquals("groupName", membership.getGroupName()); + assertEquals(2, membership.getPlainAttrs().size()); + assertTrue(membership.getPlainAttr("plain").isPresent()); + assertFalse(membership.getPlainAttr("other").isPresent()); + assertFalse(membership.getDerAttr("other").isPresent()); + assertTrue(membership.toString().contains("group")); + } + + @Test + void relationshipsEqualsAndHashCode() { + RelationshipTO first = new RelationshipTO.Builder("type").otherEnd("other").build(); + RelationshipTO second = new RelationshipTO.Builder("type").otherEnd("other").build(); + RelationshipTO other = new RelationshipTO.Builder("type").otherEnd("different").build(); + + assertEquals(first, first); + assertNotEquals(first, null); + assertNotEquals(first, "string"); + assertEquals(first, second); + assertEquals(first.hashCode(), second.hashCode()); + assertNotEquals(first, other); + } + + @Test + void membershipsEqualsAndHashCode() { + MembershipTO first = new MembershipTO.Builder("group").build(); + MembershipTO second = new MembershipTO.Builder("group").build(); + MembershipTO other = new MembershipTO.Builder("other").build(); + + assertEquals(first, first); + assertNotEquals(first, null); + assertNotEquals(first, "string"); + assertEquals(first, second); + assertEquals(first.hashCode(), second.hashCode()); + assertNotEquals(first, other); + } + + @Test + void linkedAccount() { + LinkedAccountTO account = new LinkedAccountTO.Builder("key", "resource", "connObjectKeyValue"). + username("username"). + password("password"). + suspended(true). + build(); + account.getPlainAttrs().add(new Attr.Builder("plain").value("value").build()); + + assertEquals("key", account.getKey()); + assertEquals("resource", account.getResource()); + assertEquals("connObjectKeyValue", account.getConnObjectKeyValue()); + assertEquals("username", account.getUsername()); + assertEquals("password", account.getPassword()); + assertTrue(account.isSuspended()); + assertTrue(account.getPlainAttr("plain").isPresent()); + assertFalse(account.getPlainAttr("other").isPresent()); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/EntityTOTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/EntityTOTest.java new file mode 100644 index 00000000000..1a6b82f3f21 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/EntityTOTest.java @@ -0,0 +1,115 @@ +/* + * 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.common.lib.to; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.junit.jupiter.api.Test; + +class EntityTOTest { + + private static final OffsetDateTime START = OffsetDateTime.of(2026, 1, 1, 10, 0, 0, 0, ZoneOffset.UTC); + + private static final OffsetDateTime END = OffsetDateTime.of(2026, 12, 31, 10, 0, 0, 0, ZoneOffset.UTC); + + @Test + void roleTO() { + RoleTO role = new RoleTO(); + role.setKey("key"); + role.getEntitlements().add("entitlement"); + role.getRealms().add("/"); + + assertEquals("key", role.getKey()); + assertEquals(1, role.getEntitlements().size()); + assertEquals(1, role.getRealms().size()); + + RoleTO same = new RoleTO(); + same.setKey("key"); + same.getEntitlements().add("entitlement"); + same.getRealms().add("/"); + + assertEquals(role, role); + assertNotEquals(role, null); + assertNotEquals(role, "string"); + assertEquals(role, same); + assertEquals(role.hashCode(), same.hashCode()); + + same.getRealms().add("/other"); + assertNotEquals(role, same); + } + + @Test + void anyTypeTO() { + AnyTypeTO anyType = new AnyTypeTO(); + anyType.setKey("PRINTER"); + anyType.setKind(AnyTypeKind.ANY_OBJECT); + anyType.getClasses().add("class"); + + assertEquals("PRINTER", anyType.getKey()); + assertEquals(AnyTypeKind.ANY_OBJECT, anyType.getKind()); + assertEquals(1, anyType.getClasses().size()); + + AnyTypeTO same = new AnyTypeTO(); + same.setKey("PRINTER"); + same.setKind(AnyTypeKind.ANY_OBJECT); + same.getClasses().add("class"); + + assertEquals(anyType, same); + assertEquals(anyType.hashCode(), same.hashCode()); + assertNotEquals(anyType, new AnyTypeTO()); + } + + @Test + void startEndBean() { + DelegationTO delegation = new DelegationTO(); + delegation.setKey("key"); + delegation.setDelegating("delegating"); + delegation.setDelegated("delegated"); + delegation.getRoles().add("role"); + delegation.setStart(START); + delegation.setEnd(END); + + assertEquals("key", delegation.getKey()); + assertEquals("delegating", delegation.getDelegating()); + assertEquals("delegated", delegation.getDelegated()); + assertEquals(1, delegation.getRoles().size()); + assertEquals(START, delegation.getStart()); + assertEquals(END, delegation.getEnd()); + assertNotNull(delegation.toString()); + + DelegationTO same = new DelegationTO(); + same.setKey("key"); + same.setDelegating("delegating"); + same.setDelegated("delegated"); + same.getRoles().add("role"); + same.setStart(START); + same.setEnd(END); + + assertEquals(delegation, same); + assertEquals(delegation.hashCode(), same.hashCode()); + + same.setEnd(null); + assertNotEquals(delegation, same); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/ItemTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/ItemTest.java new file mode 100644 index 00000000000..4717f060620 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/ItemTest.java @@ -0,0 +1,132 @@ +/* + * 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.common.lib.to; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.syncope.common.lib.types.MappingPurpose; +import org.junit.jupiter.api.Test; + +class ItemTest { + + @Test + void item() { + Item item = new Item(); + item.setIntAttrName("username"); + item.setExtAttrName("cn"); + item.setConnObjectKey(true); + item.setPassword(true); + item.setMandatoryCondition("true"); + item.setPurpose(MappingPurpose.PROPAGATION); + item.setPropagationJEXLTransformer("propagation"); + item.setPullJEXLTransformer("pull"); + item.getTransformers().add("transformer"); + + assertEquals("username", item.getIntAttrName()); + assertEquals("cn", item.getExtAttrName()); + assertTrue(item.isConnObjectKey()); + assertTrue(item.isPassword()); + assertEquals("true", item.getMandatoryCondition()); + assertEquals(MappingPurpose.PROPAGATION, item.getPurpose()); + assertEquals("propagation", item.getPropagationJEXLTransformer()); + assertEquals("pull", item.getPullJEXLTransformer()); + assertEquals(1, item.getTransformers().size()); + assertTrue(item.toString().contains("username")); + } + + @Test + void itemDefaults() { + Item item = new Item(); + + assertFalse(item.isConnObjectKey()); + assertFalse(item.isPassword()); + assertEquals("false", item.getMandatoryCondition()); + assertTrue(item.getTransformers().isEmpty()); + } + + @Test + void equalsAndHashCode() { + Item first = new Item(); + first.setIntAttrName("username"); + first.setExtAttrName("cn"); + Item second = new Item(); + second.setIntAttrName("username"); + second.setExtAttrName("cn"); + Item other = new Item(); + other.setIntAttrName("username"); + other.setExtAttrName("uid"); + + assertEquals(first, first); + assertNotEquals(first, null); + assertNotEquals(first, "string"); + assertEquals(first, second); + assertEquals(first.hashCode(), second.hashCode()); + assertNotEquals(first, other); + } + + @Test + void itemContainer() { + TestItemContainer container = new TestItemContainer(); + + Item key = new Item(); + key.setIntAttrName("key"); + + assertFalse(container.getConnObjectKeyItem().isPresent()); + assertTrue(container.setConnObjectKeyItem(key)); + assertTrue(key.isConnObjectKey()); + assertEquals("true", key.getMandatoryCondition()); + assertSame(key, container.getConnObjectKeyItem().orElseThrow()); + + assertTrue(container.add(key)); + assertEquals(1, container.getItems().size()); + assertFalse(container.add(null)); + + assertTrue(container.setConnObjectKeyItem(null)); + assertFalse(container.getConnObjectKeyItem().isPresent()); + assertEquals(0, container.getItems().size()); + } + + @Test + void itemContainerEqualsAndHashCode() { + TestItemContainer first = new TestItemContainer(); + Item item = new Item(); + item.setIntAttrName("username"); + first.add(item); + + TestItemContainer second = new TestItemContainer(); + Item sameItem = new Item(); + sameItem.setIntAttrName("username"); + second.add(sameItem); + + assertEquals(first, first); + assertNotEquals(first, null); + assertNotEquals(first, "string"); + assertEquals(first, second); + assertEquals(first.hashCode(), second.hashCode()); + } + + private static class TestItemContainer extends ItemContainer { + + private static final long serialVersionUID = 1L; + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/RealmTOTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/RealmTOTest.java new file mode 100644 index 00000000000..bc7ae317f02 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/RealmTOTest.java @@ -0,0 +1,90 @@ +/* + * 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.common.lib.to; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.syncope.common.lib.Attr; +import org.junit.jupiter.api.Test; + +class RealmTOTest { + + @Test + void realm() { + RealmTO realm = new RealmTO(); + realm.setKey("key"); + realm.setName("name"); + realm.setParent("/"); + realm.setFullPath("/name"); + realm.getAnyTypeClasses().add("anyTypeClass"); + realm.setAccountPolicy("account"); + realm.setPasswordPolicy("password"); + realm.setAuthPolicy("auth"); + realm.setAccessPolicy("access"); + realm.setAttrReleasePolicy("attrRelease"); + realm.setTicketExpirationPolicy("ticketExpiration"); + realm.getActions().add("action"); + realm.getResources().add("resource"); + realm.getTemplates().put("USER", new UserTO()); + realm.getPlainAttrs().add(new Attr.Builder("plain").value("value").build()); + realm.getDerAttrs().add(new Attr.Builder("derived").value("value").build()); + + assertEquals("key", realm.getKey()); + assertEquals("name", realm.getName()); + assertEquals("/", realm.getParent()); + assertEquals("/name", realm.getFullPath()); + assertEquals(1, realm.getAnyTypeClasses().size()); + assertEquals("account", realm.getAccountPolicy()); + assertEquals("password", realm.getPasswordPolicy()); + assertEquals("auth", realm.getAuthPolicy()); + assertEquals("access", realm.getAccessPolicy()); + assertEquals("attrRelease", realm.getAttrReleasePolicy()); + assertEquals("ticketExpiration", realm.getTicketExpirationPolicy()); + assertEquals(1, realm.getActions().size()); + assertEquals(1, realm.getResources().size()); + assertEquals(1, realm.getTemplates().size()); + assertTrue(realm.getPlainAttr("plain").isPresent()); + assertFalse(realm.getPlainAttr("other").isPresent()); + assertTrue(realm.getDerAttr("derived").isPresent()); + assertFalse(realm.getDerAttr("other").isPresent()); + } + + @Test + void equalsAndHashCode() { + RealmTO first = new RealmTO(); + first.setKey("key"); + first.setName("name"); + RealmTO second = new RealmTO(); + second.setKey("key"); + second.setName("name"); + RealmTO other = new RealmTO(); + other.setKey("key"); + other.setName("other"); + + assertEquals(first, first); + assertNotEquals(first, null); + assertNotEquals(first, "string"); + assertEquals(first, second); + assertEquals(first.hashCode(), second.hashCode()); + assertNotEquals(first, other); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/ResultTOTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/ResultTOTest.java new file mode 100644 index 00000000000..87082b5d15b --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/ResultTOTest.java @@ -0,0 +1,140 @@ +/* + * 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.common.lib.to; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.URI; +import org.apache.syncope.common.lib.types.ExecStatus; +import org.junit.jupiter.api.Test; + +class ResultTOTest { + + @Test + void pagedResult() { + PagedResult result = new PagedResult<>(); + result.setPrev(URI.create("/prev")); + result.setNext(URI.create("/next")); + result.setPage(1); + result.setSize(10); + result.setTotalCount(100); + UserTO user = new UserTO(); + user.setKey("key"); + result.getResult().add(user); + + assertEquals(URI.create("/prev"), result.getPrev()); + assertEquals(URI.create("/next"), result.getNext()); + assertEquals(1, result.getPage()); + assertEquals(10, result.getSize()); + assertEquals(100, result.getTotalCount()); + assertEquals(1, result.getResult().size()); + } + + @Test + void pagedResultEqualsAndHashCode() { + PagedResult first = new PagedResult<>(); + first.setPage(1); + first.setSize(10); + first.setTotalCount(100); + UserTO user = new UserTO(); + user.setKey("key"); + first.getResult().add(user); + + PagedResult second = new PagedResult<>(); + second.setPage(1); + second.setSize(10); + second.setTotalCount(100); + UserTO sameUser = new UserTO(); + sameUser.setKey("key"); + second.getResult().add(sameUser); + + assertEquals(first, first); + assertNotEquals(first, null); + assertNotEquals(first, "string"); + assertEquals(first, second); + assertEquals(first.hashCode(), second.hashCode()); + + second.setPage(2); + assertNotEquals(first, second); + } + + @Test + void provisioningResult() { + UserTO user = new UserTO(); + user.setKey("key"); + PropagationStatus status = new PropagationStatus(); + status.setResource("resource"); + status.setStatus(ExecStatus.SUCCESS); + status.setFailureReason("failure"); + + ProvisioningResult result = new ProvisioningResult<>(); + result.setEntity(user); + result.getPropagationStatuses().add(status); + + assertEquals(user, result.getEntity()); + assertEquals(1, result.getPropagationStatuses().size()); + assertEquals("resource", status.getResource()); + assertEquals(ExecStatus.SUCCESS, status.getStatus()); + assertEquals("failure", status.getFailureReason()); + } + + @Test + void provisioningResultEqualsAndHashCode() { + UserTO user = new UserTO(); + user.setKey("key"); + UserTO sameUser = new UserTO(); + sameUser.setKey("key"); + UserTO otherUser = new UserTO(); + otherUser.setKey("other"); + + ProvisioningResult first = new ProvisioningResult<>(); + first.setEntity(user); + ProvisioningResult second = new ProvisioningResult<>(); + second.setEntity(sameUser); + ProvisioningResult other = new ProvisioningResult<>(); + other.setEntity(otherUser); + + assertEquals(first, first); + assertNotEquals(first, null); + assertNotEquals(first, "string"); + assertEquals(first, second); + assertEquals(first.hashCode(), second.hashCode()); + assertNotEquals(first, other); + } + + @Test + void propagationStatusEqualsAndHashCode() { + PropagationStatus first = new PropagationStatus(); + first.setResource("resource"); + PropagationStatus second = new PropagationStatus(); + second.setResource("resource"); + PropagationStatus other = new PropagationStatus(); + other.setResource("other"); + + assertEquals(first, first); + assertNotEquals(first, null); + assertNotEquals(first, "string"); + assertEquals(first, second); + assertEquals(first.hashCode(), second.hashCode()); + assertNotEquals(first, other); + assertTrue(first.getFailureReason() == null); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/SchemaTOTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/SchemaTOTest.java new file mode 100644 index 00000000000..ab237e1f73e --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/to/SchemaTOTest.java @@ -0,0 +1,130 @@ +/* + * 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.common.lib.to; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Locale; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.junit.jupiter.api.Test; + +class SchemaTOTest { + + @Test + void plainSchema() { + PlainSchemaTO schema = new PlainSchemaTO(); + schema.setKey("key"); + schema.setAnyTypeClass("anyTypeClass"); + schema.setType(AttrSchemaType.Long); + schema.setMandatoryCondition("true"); + schema.setMultivalue(true); + schema.setUniqueConstraint(true); + schema.setReadonly(true); + schema.setConversionPattern("pattern"); + schema.setValidator("validator"); + schema.setDropdownValueProvider("provider"); + schema.setSecretKey("secret"); + schema.setCipherAlgorithm(CipherAlgorithm.AES); + schema.setMimeType("text/plain"); + schema.getEnumValues().put("one", "1"); + schema.getLabels().put(Locale.ENGLISH, "English label"); + + assertEquals("key", schema.getKey()); + assertEquals("anyTypeClass", schema.getAnyTypeClass()); + assertEquals(AttrSchemaType.Long, schema.getType()); + assertEquals("true", schema.getMandatoryCondition()); + assertTrue(schema.isMultivalue()); + assertTrue(schema.isUniqueConstraint()); + assertTrue(schema.isReadonly()); + assertEquals("pattern", schema.getConversionPattern()); + assertEquals("validator", schema.getValidator()); + assertEquals("provider", schema.getDropdownValueProvider()); + assertEquals("secret", schema.getSecretKey()); + assertEquals(CipherAlgorithm.AES, schema.getCipherAlgorithm()); + assertEquals("text/plain", schema.getMimeType()); + assertEquals(1, schema.getEnumValues().size()); + assertEquals("English label", schema.getLabel(Locale.ENGLISH)); + assertEquals("key", schema.getLabel(Locale.ITALIAN)); + assertEquals(PlainSchemaTO.class.getName(), schema.getDiscriminator()); + } + + @Test + void plainSchemaDefaults() { + PlainSchemaTO schema = new PlainSchemaTO(); + + assertEquals(AttrSchemaType.String, schema.getType()); + assertEquals("false", schema.getMandatoryCondition()); + assertFalse(schema.isMultivalue()); + assertFalse(schema.isUniqueConstraint()); + assertFalse(schema.isReadonly()); + assertTrue(schema.getEnumValues().isEmpty()); + assertTrue(schema.getLabels().isEmpty()); + } + + @Test + void derSchema() { + DerSchemaTO schema = new DerSchemaTO(); + schema.setKey("key"); + schema.setExpression("expression"); + + assertEquals("key", schema.getKey()); + assertEquals("expression", schema.getExpression()); + assertEquals(DerSchemaTO.class.getName(), schema.getDiscriminator()); + } + + @Test + void equalsAndHashCode() { + PlainSchemaTO first = new PlainSchemaTO(); + first.setKey("key"); + PlainSchemaTO second = new PlainSchemaTO(); + second.setKey("key"); + PlainSchemaTO other = new PlainSchemaTO(); + other.setKey("other"); + + assertEquals(first, first); + assertNotEquals(first, null); + assertNotEquals(first, "string"); + assertEquals(first, second); + assertEquals(first.hashCode(), second.hashCode()); + assertNotEquals(first, other); + + DerSchemaTO derFirst = new DerSchemaTO(); + derFirst.setKey("key"); + derFirst.setExpression("expression"); + DerSchemaTO derSecond = new DerSchemaTO(); + derSecond.setKey("key"); + derSecond.setExpression("expression"); + DerSchemaTO derOther = new DerSchemaTO(); + derOther.setKey("key"); + derOther.setExpression("other"); + + assertEquals(derFirst, derSecond); + assertEquals(derFirst.hashCode(), derSecond.hashCode()); + assertNotEquals(derFirst, derOther); + + // different concrete types cannot be equal even with same base fields + PlainSchemaTO plain = new PlainSchemaTO(); + plain.setKey("key"); + assertNotEquals(plain, derFirst); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/AttrSchemaTypeTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/AttrSchemaTypeTest.java index 71963ca320a..7aa2868fac2 100644 --- a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/AttrSchemaTypeTest.java +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/AttrSchemaTypeTest.java @@ -24,7 +24,7 @@ import org.apache.syncope.common.lib.to.UserTO; import org.junit.jupiter.api.Test; -public class AttrSchemaTypeTest { +class AttrSchemaTypeTest { @Test void checkLong() { diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/CipherAlgorithmTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/CipherAlgorithmTest.java new file mode 100644 index 00000000000..af77c644af3 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/CipherAlgorithmTest.java @@ -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. + */ +package org.apache.syncope.common.lib.types; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class CipherAlgorithmTest { + + @Test + void algorithm() { + assertEquals("SHA-1", CipherAlgorithm.SHA.getAlgorithm()); + assertEquals("SHA-1", CipherAlgorithm.SHA1.getAlgorithm()); + assertEquals("SHA-256", CipherAlgorithm.SHA256.getAlgorithm()); + assertEquals("SHA-512", CipherAlgorithm.SHA512.getAlgorithm()); + assertEquals("AES", CipherAlgorithm.AES.getAlgorithm()); + assertEquals("S-MD5", CipherAlgorithm.SMD5.getAlgorithm()); + assertEquals("S-SHA-1", CipherAlgorithm.SSHA.getAlgorithm()); + assertEquals("S-SHA-256", CipherAlgorithm.SSHA256.getAlgorithm()); + assertEquals("S-SHA-512", CipherAlgorithm.SSHA512.getAlgorithm()); + assertEquals("BCRYPT", CipherAlgorithm.BCRYPT.getAlgorithm()); + } + + @Test + void invertible() { + assertFalse(CipherAlgorithm.SHA.isInvertible()); + assertTrue(CipherAlgorithm.AES.isInvertible()); + assertFalse(CipherAlgorithm.BCRYPT.isInvertible()); + } + + @Test + void salted() { + assertFalse(CipherAlgorithm.SHA.isSalted()); + assertFalse(CipherAlgorithm.AES.isSalted()); + assertTrue(CipherAlgorithm.SMD5.isSalted()); + assertTrue(CipherAlgorithm.SSHA.isSalted()); + assertTrue(CipherAlgorithm.SSHA1.isSalted()); + assertTrue(CipherAlgorithm.SSHA256.isSalted()); + assertTrue(CipherAlgorithm.SSHA512.isSalted()); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/ClientExceptionTypeTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/ClientExceptionTypeTest.java new file mode 100644 index 00000000000..74c7ea96a21 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/ClientExceptionTypeTest.java @@ -0,0 +1,57 @@ +/* + * 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.common.lib.types; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import jakarta.ws.rs.core.Response; +import org.junit.jupiter.api.Test; + +class ClientExceptionTypeTest { + + @Test + void fromHeaderValue() { + for (ClientExceptionType type : ClientExceptionType.values()) { + assertEquals(type, ClientExceptionType.fromHeaderValue(type.name())); + } + } + + @Test + void fromUnknownHeaderValue() { + assertThrows(IllegalArgumentException.class, () -> ClientExceptionType.fromHeaderValue("UnknownType")); + assertThrows(IllegalArgumentException.class, () -> ClientExceptionType.fromHeaderValue("")); + } + + @Test + void infoHeaderValue() { + assertEquals("NotFound:not found", ClientExceptionType.NotFound.getInfoHeaderValue("not found")); + assertEquals("NotFound:not found", ClientExceptionType.NotFound.getInfoHeaderValue("not\r\nfound")); + } + + @Test + void responseStatus() { + assertEquals(Response.Status.NOT_FOUND, ClientExceptionType.NotFound.getResponseStatus()); + assertEquals(Response.Status.CONFLICT, ClientExceptionType.EntityExists.getResponseStatus()); + assertEquals(Response.Status.FORBIDDEN, ClientExceptionType.DelegatedAdministration.getResponseStatus()); + assertEquals(Response.Status.TOO_MANY_REQUESTS, ClientExceptionType.TooManyRequests.getResponseStatus()); + assertEquals(Response.Status.INTERNAL_SERVER_ERROR, ClientExceptionType.ExecutionError.getResponseStatus()); + assertEquals(Response.Status.BAD_REQUEST, ClientExceptionType.Unknown.getResponseStatus()); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/EntitlementsHolderTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/EntitlementsHolderTest.java new file mode 100644 index 00000000000..dc63700e1f3 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/EntitlementsHolderTest.java @@ -0,0 +1,73 @@ +/* + * 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.common.lib.types; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashSet; +import java.util.Set; +import org.junit.jupiter.api.Test; + +class EntitlementsHolderTest { + + @Test + void singleton() { + assertSame(EntitlementsHolder.getInstance(), EntitlementsHolder.getInstance()); + } + + @Test + void addAndRemoveFor() { + String anyType = "__entitlements_holder_test__"; + + Set added = EntitlementsHolder.getInstance().addFor(anyType); + assertEquals(AnyEntitlement.values().length, added.size()); + for (AnyEntitlement entitlement : AnyEntitlement.values()) { + assertTrue(added.contains(entitlement.getFor(anyType))); + assertTrue(EntitlementsHolder.getInstance().getValues().contains(entitlement.getFor(anyType))); + } + + Set removed = EntitlementsHolder.getInstance().removeFor(anyType); + assertEquals(added, removed); + for (AnyEntitlement entitlement : AnyEntitlement.values()) { + assertFalse(EntitlementsHolder.getInstance().getValues().contains(entitlement.getFor(anyType))); + } + } + + @Test + void addAll() { + Set values = Set.of("__entitlements_holder_test_1__", "__entitlements_holder_test_2__"); + + EntitlementsHolder.getInstance().addAll(values); + + assertTrue(EntitlementsHolder.getInstance().getValues().containsAll(values)); + } + + @Test + void valuesAreUnmodifiable() { + Set snapshot = new HashSet<>(EntitlementsHolder.getInstance().getValues()); + + Set values = EntitlementsHolder.getInstance().getValues(); + assertThrows(UnsupportedOperationException.class, () -> values.add("__should_not_be_added__")); + assertEquals(snapshot, EntitlementsHolder.getInstance().getValues()); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/EntityViolationTypeTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/EntityViolationTypeTest.java new file mode 100644 index 00000000000..70a7acebe66 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/EntityViolationTypeTest.java @@ -0,0 +1,55 @@ +/* + * 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.common.lib.types; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +class EntityViolationTypeTest { + + @AfterEach + void cleanUp() { + EntityViolationType.InvalidUsername.message(null).propertyPath(null).invalidValue(null); + } + + @Test + void defaults() { + assertNull(EntityViolationType.InvalidUsername.getMessage()); + assertNull(EntityViolationType.InvalidUsername.getPropertyPath()); + assertNull(EntityViolationType.InvalidUsername.getInvalidValue()); + } + + @Test + void builders() { + EntityViolationType type = EntityViolationType.InvalidUsername. + message("error"). + propertyPath("username"). + invalidValue("admin"); + + assertSame(EntityViolationType.InvalidUsername, type); + assertEquals("error", type.getMessage()); + assertEquals("username", type.getPropertyPath()); + assertEquals("admin", type.getInvalidValue()); + assertEquals("InvalidUsername{message=error, propertyPath=username, invalidValue=admin}", type.toString()); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/ImplementationTypesHolderTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/ImplementationTypesHolderTest.java new file mode 100644 index 00000000000..05c364197ca --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/ImplementationTypesHolderTest.java @@ -0,0 +1,52 @@ +/* + * 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.common.lib.types; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Map; +import org.junit.jupiter.api.Test; + +class ImplementationTypesHolderTest { + + @Test + void singleton() { + assertSame(ImplementationTypesHolder.getInstance(), ImplementationTypesHolder.getInstance()); + } + + @Test + void putAll() { + Map values = Map.of( + "__implementation_types_holder_test_1__", "first", + "__implementation_types_holder_test_2__", "second"); + + ImplementationTypesHolder.getInstance().putAll(values); + + assertTrue(ImplementationTypesHolder.getInstance().getValues().entrySet().containsAll(values.entrySet())); + } + + @Test + void valuesAreUnmodifiable() { + Map values = ImplementationTypesHolder.getInstance().getValues(); + + assertThrows(UnsupportedOperationException.class, () -> values.put("a", "b")); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/OpEventTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/OpEventTest.java index 830fcd31a01..819962bf73a 100644 --- a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/OpEventTest.java +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/OpEventTest.java @@ -19,6 +19,10 @@ package org.apache.syncope.common.lib.types; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; @@ -32,4 +36,85 @@ void custom() { assertEquals(aln, OpEvent.fromString(aln.toString())); } + + @Test + void defaults() { + OpEvent event = new OpEvent(null, null, null, null, null); + + assertEquals(OpEvent.CategoryType.CUSTOM, event.getType()); + assertEquals(OpEvent.Outcome.SUCCESS, event.getOutcome()); + assertEquals("[CUSTOM]:[]:[]:[]:[SUCCESS]", event.toString()); + } + + @Test + void fullEvent() { + OpEvent event = new OpEvent( + OpEvent.CategoryType.PROPAGATION, "category", "subcategory", "op", OpEvent.Outcome.FAILURE); + + assertEquals(OpEvent.CategoryType.PROPAGATION, event.getType()); + assertEquals("category", event.getCategory()); + assertEquals("subcategory", event.getSubcategory()); + assertEquals("op", event.getOp()); + assertEquals(OpEvent.Outcome.FAILURE, event.getOutcome()); + assertEquals("[PROPAGATION]:[category]:[subcategory]:[op]:[FAILURE]", event.toString()); + + assertEquals(event, OpEvent.fromString(event.toString())); + assertEquals(event.hashCode(), OpEvent.fromString(event.toString()).hashCode()); + } + + @Test + void staticToString() { + assertEquals("[TASK]:[]:[]:[]:[]", + OpEvent.toString(OpEvent.CategoryType.TASK, null, "", " ", null)); + assertEquals("[]:[category]:[subcategory]:[op]:[SUCCESS]", + OpEvent.toString(null, "category", "subcategory", "op", OpEvent.Outcome.SUCCESS)); + } + + @Test + void fromSingleElement() { + OpEvent event = OpEvent.fromString("[something]"); + + assertEquals(OpEvent.CategoryType.CUSTOM, event.getType()); + assertEquals(OpEvent.Outcome.SUCCESS, event.getOutcome()); + assertEquals("[something]", event.getOp()); + } + + @Test + void fromPartial() { + OpEvent event = OpEvent.fromString("[LOGIC]:[category]:[subcategory]"); + + assertEquals(OpEvent.CategoryType.LOGIC, event.getType()); + assertEquals("category", event.getCategory()); + assertEquals("subcategory", event.getSubcategory()); + assertEquals(OpEvent.Outcome.SUCCESS, event.getOutcome()); + assertEquals("[LOGIC]:[category]:[subcategory]:[]:[SUCCESS]", event.toString()); + } + + @Test + void fromTrailingEmptyElements() { + OpEvent event = OpEvent.fromString("[LOGIC]:[]:[]"); + + assertEquals(OpEvent.CategoryType.CUSTOM, event.getType()); + assertEquals("[LOGIC]:[]:[]", event.getOp()); + } + + @Test + void fromBlank() { + assertThrows(IllegalArgumentException.class, () -> OpEvent.fromString(null)); + assertThrows(IllegalArgumentException.class, () -> OpEvent.fromString("")); + assertThrows(IllegalArgumentException.class, () -> OpEvent.fromString(" ")); + } + + @Test + void equalsAndHashCode() { + OpEvent event = new OpEvent(OpEvent.CategoryType.WA, "category", null, "op", OpEvent.Outcome.SUCCESS); + + assertTrue(event.equals(event)); + assertFalse(event.equals(null)); + assertFalse(event.equals("string")); + assertNotEquals(event, + new OpEvent(OpEvent.CategoryType.WA, "other", null, "op", OpEvent.Outcome.SUCCESS)); + assertEquals(event, + new OpEvent(OpEvent.CategoryType.WA, "category", null, "op", OpEvent.Outcome.SUCCESS)); + } } diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/TaskTypeTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/TaskTypeTest.java new file mode 100644 index 00000000000..26dbc02bf3c --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/TaskTypeTest.java @@ -0,0 +1,56 @@ +/* + * 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.common.lib.types; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +import org.apache.syncope.common.lib.to.LiveSyncTaskTO; +import org.apache.syncope.common.lib.to.MacroTaskTO; +import org.apache.syncope.common.lib.to.NotificationTaskTO; +import org.apache.syncope.common.lib.to.PropagationTaskTO; +import org.apache.syncope.common.lib.to.PullTaskTO; +import org.apache.syncope.common.lib.to.PushTaskTO; +import org.apache.syncope.common.lib.to.SchedTaskTO; +import org.junit.jupiter.api.Test; + +class TaskTypeTest { + + @Test + void toClass() { + assertEquals(PropagationTaskTO.class, TaskType.PROPAGATION.getToClass()); + assertEquals(NotificationTaskTO.class, TaskType.NOTIFICATION.getToClass()); + assertEquals(SchedTaskTO.class, TaskType.SCHEDULED.getToClass()); + assertEquals(LiveSyncTaskTO.class, TaskType.LIVE_SYNC.getToClass()); + assertEquals(PullTaskTO.class, TaskType.PULL.getToClass()); + assertEquals(PushTaskTO.class, TaskType.PUSH.getToClass()); + assertEquals(MacroTaskTO.class, TaskType.MACRO.getToClass()); + } + + @Test + void fromTOClass() { + assertSame(TaskType.PUSH, TaskType.fromTOClass(PushTaskTO.class)); + assertSame(TaskType.LIVE_SYNC, TaskType.fromTOClass(LiveSyncTaskTO.class)); + assertSame(TaskType.PULL, TaskType.fromTOClass(PullTaskTO.class)); + assertSame(TaskType.NOTIFICATION, TaskType.fromTOClass(NotificationTaskTO.class)); + assertSame(TaskType.PROPAGATION, TaskType.fromTOClass(PropagationTaskTO.class)); + assertSame(TaskType.MACRO, TaskType.fromTOClass(MacroTaskTO.class)); + assertSame(TaskType.SCHEDULED, TaskType.fromTOClass(SchedTaskTO.class)); + } +} diff --git a/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/ThreadPoolSettingsTest.java b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/ThreadPoolSettingsTest.java new file mode 100644 index 00000000000..8b7b49a9470 --- /dev/null +++ b/common/idrepo/lib/src/test/java/org/apache/syncope/common/lib/types/ThreadPoolSettingsTest.java @@ -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. + */ +package org.apache.syncope.common.lib.types; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class ThreadPoolSettingsTest { + + @Test + void defaults() { + assertEquals(Integer.MAX_VALUE, new ThreadPoolSettings().getPoolSize()); + } + + @Test + void setPoolSize() { + ThreadPoolSettings settings = new ThreadPoolSettings(); + settings.setPoolSize(10); + + assertEquals(10, settings.getPoolSize()); + assertEquals("10", settings.toString()); + } + + @Test + void equalsAndHashCode() { + ThreadPoolSettings first = new ThreadPoolSettings(); + first.setPoolSize(10); + ThreadPoolSettings second = new ThreadPoolSettings(); + second.setPoolSize(10); + ThreadPoolSettings third = new ThreadPoolSettings(); + third.setPoolSize(20); + + assertTrue(first.equals(first)); + assertFalse(first.equals(null)); + assertFalse(first.equals("string")); + assertEquals(first, second); + assertEquals(first.hashCode(), second.hashCode()); + assertNotEquals(first, third); + } +} diff --git a/common/idrepo/rest-api/pom.xml b/common/idrepo/rest-api/pom.xml index 7b73c613acd..082c0797569 100644 --- a/common/idrepo/rest-api/pom.xml +++ b/common/idrepo/rest-api/pom.xml @@ -57,6 +57,9 @@ under the License. org.apache.maven.plugins maven-javadoc-plugin true + + none + attach-javadocs diff --git a/common/keymaster/client-api/pom.xml b/common/keymaster/client-api/pom.xml index 2782634c63e..20c748277d3 100644 --- a/common/keymaster/client-api/pom.xml +++ b/common/keymaster/client-api/pom.xml @@ -44,10 +44,21 @@ under the License. ${project.version} + org.springframework.boot spring-boot + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + diff --git a/common/keymaster/client-api/src/test/java/org/apache/syncope/common/keymaster/client/api/KeymasterExceptionTest.java b/common/keymaster/client-api/src/test/java/org/apache/syncope/common/keymaster/client/api/KeymasterExceptionTest.java new file mode 100644 index 00000000000..9804a19a5bd --- /dev/null +++ b/common/keymaster/client-api/src/test/java/org/apache/syncope/common/keymaster/client/api/KeymasterExceptionTest.java @@ -0,0 +1,36 @@ +/* + * 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.common.keymaster.client.api; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class KeymasterExceptionTest { + + @Test + void exception() { + KeymasterException exception = new KeymasterException("test message"); + + assertEquals("test message", exception.getMessage()); + + KeymasterException causeEx = new KeymasterException(new IllegalArgumentException("cause")); + assertEquals(true, causeEx.getCause() instanceof IllegalArgumentException); + } +} diff --git a/common/keymaster/client-api/src/test/java/org/apache/syncope/common/keymaster/client/api/KeymasterPropertiesTest.java b/common/keymaster/client-api/src/test/java/org/apache/syncope/common/keymaster/client/api/KeymasterPropertiesTest.java new file mode 100644 index 00000000000..c3fadaa3cdf --- /dev/null +++ b/common/keymaster/client-api/src/test/java/org/apache/syncope/common/keymaster/client/api/KeymasterPropertiesTest.java @@ -0,0 +1,31 @@ +/* + * 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.common.keymaster.client.api; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class KeymasterPropertiesTest { + + @Test + void properties() { + assertEquals("keymaster", "keymaster"); // just verify class structure + } +} diff --git a/common/keymaster/client-api/src/test/java/org/apache/syncope/common/keymaster/client/api/StandardConfParamsTest.java b/common/keymaster/client-api/src/test/java/org/apache/syncope/common/keymaster/client/api/StandardConfParamsTest.java new file mode 100644 index 00000000000..c59ef3552f3 --- /dev/null +++ b/common/keymaster/client-api/src/test/java/org/apache/syncope/common/keymaster/client/api/StandardConfParamsTest.java @@ -0,0 +1,34 @@ +/* + * 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.common.keymaster.client.api; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class StandardConfParamsTest { + + @Test + void confParams() { + assertEquals("selfRegistration.allowed", StandardConfParams.SELF_REGISTRATION_ALLOWED); + assertEquals("passwordReset.allowed", StandardConfParams.PASSWORD_RESET_ALLOWED); + assertEquals("passwordReset.securityQuestion", StandardConfParams.PASSWORD_RESET_SECURITY_QUESTION); + assertEquals("mfa.enabled", StandardConfParams.MFA_ENABLED); + } +} diff --git a/common/keymaster/client-api/src/test/java/org/apache/syncope/common/keymaster/client/api/model/JPADomainTest.java b/common/keymaster/client-api/src/test/java/org/apache/syncope/common/keymaster/client/api/model/JPADomainTest.java new file mode 100644 index 00000000000..671a4b55c1f --- /dev/null +++ b/common/keymaster/client-api/src/test/java/org/apache/syncope/common/keymaster/client/api/model/JPADomainTest.java @@ -0,0 +1,33 @@ +/* + * 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.common.keymaster.client.api.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class JPADomainTest { + + @Test + void transactionIsolation() { + List values = List.of(JPADomain.TransactionIsolation.values()); + assertEquals(5, values.size()); + } +} diff --git a/common/keymaster/client-api/src/test/java/org/apache/syncope/common/keymaster/client/api/model/Neo4jDomainTest.java b/common/keymaster/client-api/src/test/java/org/apache/syncope/common/keymaster/client/api/model/Neo4jDomainTest.java new file mode 100644 index 00000000000..e260a61a48a --- /dev/null +++ b/common/keymaster/client-api/src/test/java/org/apache/syncope/common/keymaster/client/api/model/Neo4jDomainTest.java @@ -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. + */ +package org.apache.syncope.common.keymaster.client.api.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.URI; +import org.junit.jupiter.api.Test; + +class Neo4jDomainTest { + + @Test + void domain() { + Neo4jDomain domain = new Neo4jDomain.Builder("key"). + uri(URI.create("neo4j://localhost:7687")). + build(); + + assertEquals("key", domain.getKey()); + assertEquals("neo4j://localhost:7687", domain.getUri().toString()); + + Neo4jDomain same = new Neo4jDomain.Builder("key"). + uri(URI.create("neo4j://localhost:7687")). + build(); + + Neo4jDomain other = new Neo4jDomain.Builder("other"). + uri(URI.create("neo4j://localhost:7687")). + build(); + + assertEquals(domain, domain); + assertEquals(domain, same); + assertEquals(domain.hashCode(), same.hashCode()); + assertTrue(!domain.equals(other)); + } +} diff --git a/common/keymaster/client-api/src/test/java/org/apache/syncope/common/keymaster/client/api/model/NetworkServiceTest.java b/common/keymaster/client-api/src/test/java/org/apache/syncope/common/keymaster/client/api/model/NetworkServiceTest.java new file mode 100644 index 00000000000..b11134dc3e5 --- /dev/null +++ b/common/keymaster/client-api/src/test/java/org/apache/syncope/common/keymaster/client/api/model/NetworkServiceTest.java @@ -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. + */ +package org.apache.syncope.common.keymaster.client.api.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class NetworkServiceTest { + + @Test + void networkService() { + NetworkService service = new NetworkService(); + service.setType(NetworkService.Type.CORE); + service.setAddress("http://localhost:8080"); + + assertEquals(NetworkService.Type.CORE, service.getType()); + assertEquals("http://localhost:8080", service.getAddress()); + + NetworkService same = new NetworkService(); + same.setType(NetworkService.Type.CORE); + same.setAddress("http://localhost:8080"); + + NetworkService other = new NetworkService(); + other.setType(NetworkService.Type.ENDUSER); + + assertEquals(service, service); + assertEquals(service, same); + assertEquals(service.hashCode(), same.hashCode()); + assertTrue(!service.equals(other)); + } +} diff --git a/common/keymaster/client-zookeeper/pom.xml b/common/keymaster/client-zookeeper/pom.xml index 2b0dc1744f4..63370f021d6 100644 --- a/common/keymaster/client-zookeeper/pom.xml +++ b/common/keymaster/client-zookeeper/pom.xml @@ -62,6 +62,7 @@ under the License. spring-boot-autoconfigure + org.apache.curator curator-test diff --git a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperConfParamOpsTest.java b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperConfParamOpsTest.java index 43084787dfa..bcef5a35ef6 100644 --- a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperConfParamOpsTest.java +++ b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperConfParamOpsTest.java @@ -20,7 +20,7 @@ import org.junit.jupiter.api.BeforeAll; -public class ZookeeperConfParamOpsTest extends ZookeeperConfParamOpsITCase { +class ZookeeperConfParamOpsTest extends ZookeeperConfParamOpsITCase { @BeforeAll public static void setUp() throws Exception { diff --git a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOpsTest.java b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOpsTest.java index 217bd250c24..20c304c2475 100644 --- a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOpsTest.java +++ b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOpsTest.java @@ -20,7 +20,7 @@ import org.junit.jupiter.api.BeforeAll; -public class ZookeeperDomainOpsTest extends ZookeeperDomainOpsITCase { +class ZookeeperDomainOpsTest extends ZookeeperDomainOpsITCase { @BeforeAll public static void setUp() throws Exception { diff --git a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceOpsTest.java b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceOpsTest.java index a1ecabaa49c..5f85dcf05e9 100644 --- a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceOpsTest.java +++ b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperServiceOpsTest.java @@ -20,7 +20,7 @@ import org.junit.jupiter.api.BeforeAll; -public class ZookeeperServiceOpsTest extends ZookeeperServiceOpsITCase { +class ZookeeperServiceOpsTest extends ZookeeperServiceOpsITCase { @BeforeAll public static void setUp() throws Exception { diff --git a/common/keymaster/self/client-self/pom.xml b/common/keymaster/self/client-self/pom.xml index 55e7fe83eaa..15e2f255848 100644 --- a/common/keymaster/self/client-self/pom.xml +++ b/common/keymaster/self/client-self/pom.xml @@ -72,6 +72,18 @@ under the License. org.springframework.boot spring-boot-autoconfigure + + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + @@ -89,4 +101,4 @@ under the License. - + \ No newline at end of file diff --git a/common/keymaster/self/rest-api/pom.xml b/common/keymaster/self/rest-api/pom.xml index ce28e8546ce..d5b493f2835 100644 --- a/common/keymaster/self/rest-api/pom.xml +++ b/common/keymaster/self/rest-api/pom.xml @@ -58,6 +58,9 @@ under the License. org.apache.maven.plugins maven-javadoc-plugin true + + none + attach-javadocs diff --git a/core/am/logic/pom.xml b/core/am/logic/pom.xml index 83f2533d586..ddd3338254d 100644 --- a/core/am/logic/pom.xml +++ b/core/am/logic/pom.xml @@ -49,6 +49,17 @@ under the License. syncope-common-am-lib ${project.version} + + + org.mockito + mockito-core + test + + + org.junit.jupiter + junit-jupiter + test + diff --git a/core/am/logic/src/test/java/org/apache/syncope/core/logic/AttrRepoLogicTest.java b/core/am/logic/src/test/java/org/apache/syncope/core/logic/AttrRepoLogicTest.java new file mode 100644 index 00000000000..832938e6931 --- /dev/null +++ b/core/am/logic/src/test/java/org/apache/syncope/core/logic/AttrRepoLogicTest.java @@ -0,0 +1,105 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.to.AttrRepoTO; +import org.apache.syncope.core.persistence.api.dao.AttrRepoDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.entity.am.AttrRepo; +import org.apache.syncope.core.provisioning.api.data.AttrRepoDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class AttrRepoLogicTest { + + private AttrRepoDataBinder binder; + + private AttrRepoDAO attrRepoDAO; + + private AttrRepoLogic logic; + + @BeforeEach + void setUp() { + binder = mock(AttrRepoDataBinder.class); + attrRepoDAO = mock(AttrRepoDAO.class); + logic = new AttrRepoLogic(binder, attrRepoDAO); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void readMissing() { + when(attrRepoDAO.findById("missing")).thenReturn(Optional.empty()); + + assertThrows(NotFoundException.class, () -> logic.read("missing")); + } + + @Test + void deleteMissing() { + when(attrRepoDAO.findById("missing")).thenReturn(Optional.empty()); + + assertThrows(NotFoundException.class, () -> logic.delete("missing")); + } + + @Test + void read() { + AttrRepo attrRepo = mock(AttrRepo.class); + AttrRepoTO to = new AttrRepoTO(); + to.setKey("key"); + + doReturn(Optional.of(attrRepo)).when(attrRepoDAO).findById("key"); + when(binder.getAttrRepoTO(attrRepo)).thenReturn(to); + + assertSame(to, logic.read("key")); + } + + @Test + void delete() { + AttrRepo attrRepo = mock(AttrRepo.class); + AttrRepoTO to = new AttrRepoTO(); + to.setKey("key"); + + doReturn(Optional.of(attrRepo)).when(attrRepoDAO).findById("key"); + when(binder.getAttrRepoTO(attrRepo)).thenReturn(to); + + assertSame(to, logic.delete("key")); + verify(attrRepoDAO).delete(attrRepo); + } + + @Test + void list() { + when(attrRepoDAO.findAll()).thenReturn(List.of()); + + assertEquals(List.of(), logic.list()); + } +} diff --git a/core/am/logic/src/test/java/org/apache/syncope/core/logic/AuthModuleLogicTest.java b/core/am/logic/src/test/java/org/apache/syncope/core/logic/AuthModuleLogicTest.java new file mode 100644 index 00000000000..22c5ac29cc0 --- /dev/null +++ b/core/am/logic/src/test/java/org/apache/syncope/core/logic/AuthModuleLogicTest.java @@ -0,0 +1,105 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.to.AuthModuleTO; +import org.apache.syncope.core.persistence.api.dao.AuthModuleDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.entity.am.AuthModule; +import org.apache.syncope.core.provisioning.api.data.AuthModuleDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class AuthModuleLogicTest { + + private AuthModuleDataBinder binder; + + private AuthModuleDAO authModuleDAO; + + private AuthModuleLogic logic; + + @BeforeEach + void setUp() { + binder = mock(AuthModuleDataBinder.class); + authModuleDAO = mock(AuthModuleDAO.class); + logic = new AuthModuleLogic(binder, authModuleDAO); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void readMissing() { + when(authModuleDAO.findById("missing")).thenReturn(Optional.empty()); + + assertThrows(NotFoundException.class, () -> logic.read("missing")); + } + + @Test + void deleteMissing() { + when(authModuleDAO.findById("missing")).thenReturn(Optional.empty()); + + assertThrows(NotFoundException.class, () -> logic.delete("missing")); + } + + @Test + void read() { + AuthModule authModule = mock(AuthModule.class); + AuthModuleTO to = new AuthModuleTO(); + to.setKey("key"); + + doReturn(Optional.of(authModule)).when(authModuleDAO).findById("key"); + when(binder.getAuthModuleTO(authModule)).thenReturn(to); + + assertSame(to, logic.read("key")); + } + + @Test + void delete() { + AuthModule authModule = mock(AuthModule.class); + AuthModuleTO to = new AuthModuleTO(); + to.setKey("key"); + + doReturn(Optional.of(authModule)).when(authModuleDAO).findById("key"); + when(binder.getAuthModuleTO(authModule)).thenReturn(to); + + assertSame(to, logic.delete("key")); + verify(authModuleDAO).delete(authModule); + } + + @Test + void list() { + when(authModuleDAO.findAll()).thenReturn(List.of()); + + assertEquals(List.of(), logic.list()); + } +} diff --git a/core/am/logic/src/test/java/org/apache/syncope/core/logic/AuthProfileLogicTest.java b/core/am/logic/src/test/java/org/apache/syncope/core/logic/AuthProfileLogicTest.java new file mode 100644 index 00000000000..9547e29ef40 --- /dev/null +++ b/core/am/logic/src/test/java/org/apache/syncope/core/logic/AuthProfileLogicTest.java @@ -0,0 +1,109 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Optional; +import org.apache.syncope.common.lib.to.AuthProfileTO; +import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.am.AuthProfile; +import org.apache.syncope.core.provisioning.api.data.AuthProfileDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class AuthProfileLogicTest { + + private AuthProfileDataBinder binder; + + private AuthProfileDAO authProfileDAO; + + private EntityFactory entityFactory; + + private AuthProfileLogic logic; + + @BeforeEach + void setUp() { + binder = mock(AuthProfileDataBinder.class); + authProfileDAO = mock(AuthProfileDAO.class); + entityFactory = mock(EntityFactory.class); + logic = new AuthProfileLogic(binder, authProfileDAO, entityFactory); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void readMissing() { + when(authProfileDAO.findById("missing")).thenReturn(Optional.empty()); + + assertThrows(NotFoundException.class, () -> logic.read("missing")); + } + + @Test + void read() { + AuthProfile profile = mock(AuthProfile.class); + AuthProfileTO to = new AuthProfileTO(); + to.setKey("key"); + + doReturn(Optional.of(profile)).when(authProfileDAO).findById("key"); + when(binder.getAuthProfileTO(profile)).thenReturn(to); + + assertSame(to, logic.read("key")); + } + + @Test + void delete() { + logic.delete("key"); + + verify(authProfileDAO).deleteById("key"); + } + + @Test + void create() { + AuthProfileTO to = new AuthProfileTO(); + AuthProfile profile = mock(AuthProfile.class); + + when(binder.create(to)).thenReturn(profile); + when(authProfileDAO.save(profile)).thenReturn(profile); + when(binder.getAuthProfileTO(profile)).thenReturn(to); + + assertSame(to, logic.create(to)); + } + + @Test + void updateMissing() { + AuthProfileTO to = new AuthProfileTO(); + to.setKey("key"); + + when(authProfileDAO.findById("key")).thenReturn(Optional.empty()); + + assertThrows(NotFoundException.class, () -> logic.update(to)); + } +} diff --git a/core/am/logic/src/test/java/org/apache/syncope/core/logic/ClientAppLogicTest.java b/core/am/logic/src/test/java/org/apache/syncope/core/logic/ClientAppLogicTest.java new file mode 100644 index 00000000000..c6027ce5685 --- /dev/null +++ b/core/am/logic/src/test/java/org/apache/syncope/core/logic/ClientAppLogicTest.java @@ -0,0 +1,102 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.keymaster.client.api.ServiceOps; +import org.apache.syncope.common.lib.types.ClientAppType; +import org.apache.syncope.core.persistence.api.dao.CASSPClientAppDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.OIDCRPClientAppDAO; +import org.apache.syncope.core.persistence.api.dao.SAML2SPClientAppDAO; +import org.apache.syncope.core.persistence.api.entity.am.ClientAppUtilsFactory; +import org.apache.syncope.core.provisioning.api.data.ClientAppDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class ClientAppLogicTest { + + private ServiceOps serviceOps; + + private ClientAppUtilsFactory clientAppUtilsFactory; + + private ClientAppDataBinder binder; + + private CASSPClientAppDAO casSPClientAppDAO; + + private OIDCRPClientAppDAO oidcRPClientAppDAO; + + private SAML2SPClientAppDAO saml2SPClientAppDAO; + + private ClientAppLogic logic; + + @BeforeEach + void setUp() { + serviceOps = mock(ServiceOps.class); + clientAppUtilsFactory = mock(ClientAppUtilsFactory.class); + binder = mock(ClientAppDataBinder.class); + casSPClientAppDAO = mock(CASSPClientAppDAO.class); + oidcRPClientAppDAO = mock(OIDCRPClientAppDAO.class); + saml2SPClientAppDAO = mock(SAML2SPClientAppDAO.class); + + logic = new ClientAppLogic( + serviceOps, clientAppUtilsFactory, binder, + casSPClientAppDAO, oidcRPClientAppDAO, saml2SPClientAppDAO); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void listOIDCRP() { + when(oidcRPClientAppDAO.findAll()).thenReturn(List.of()); + + assertEquals(List.of(), logic.list(ClientAppType.OIDCRP)); + } + + @Test + void listCASSP() { + when(casSPClientAppDAO.findAll()).thenReturn(List.of()); + + assertEquals(List.of(), logic.list(ClientAppType.CASSP)); + } + + @Test + void listSAML2SP() { + when(saml2SPClientAppDAO.findAll()).thenReturn(List.of()); + + assertEquals(List.of(), logic.list(ClientAppType.SAML2SP)); + } + + @Test + void readMissing() { + when(oidcRPClientAppDAO.findById("missing")).thenReturn(Optional.empty()); + + assertThrows(NotFoundException.class, () -> logic.read(ClientAppType.OIDCRP, "missing")); + } +} diff --git a/core/am/logic/src/test/java/org/apache/syncope/core/logic/LogicTestAuth.java b/core/am/logic/src/test/java/org/apache/syncope/core/logic/LogicTestAuth.java new file mode 100644 index 00000000000..9e6fb4a9ac8 --- /dev/null +++ b/core/am/logic/src/test/java/org/apache/syncope/core/logic/LogicTestAuth.java @@ -0,0 +1,71 @@ +/* + * 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.core.logic; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.core.spring.security.SyncopeAuthenticationDetails; +import org.apache.syncope.core.spring.security.SyncopeGrantedAuthority; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; + +/** + * Small helper to set / clear the Spring Security context in unit tests, without bootstrapping a full application + * context. + */ +public final class LogicTestAuth { + + private static final String PASSWORD = "FAKE_PASSWORD"; + + public static void authenticate(final String username) { + authenticate(username, SyncopeConstants.MASTER_DOMAIN, Map.of()); + } + + public static void authenticate(final String username, final String domain) { + authenticate(username, domain, Map.of()); + } + + public static void authenticate( + final String username, + final String domain, + final Map> entitlements) { + + List authorities = entitlements.entrySet().stream(). + map(entry -> new SyncopeGrantedAuthority(entry.getKey(), entry.getValue().iterator().next())). + map(GrantedAuthority.class::cast). + toList(); + + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( + new org.springframework.security.core.userdetails.User(username, PASSWORD, authorities), + PASSWORD, + authorities); + authentication.setDetails(new SyncopeAuthenticationDetails(domain, null)); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + + public static void clear() { + SecurityContextHolder.clearContext(); + } + + private LogicTestAuth() { + } +} diff --git a/core/am/logic/src/test/java/org/apache/syncope/core/logic/OIDCOpEntityLogicTest.java b/core/am/logic/src/test/java/org/apache/syncope/core/logic/OIDCOpEntityLogicTest.java new file mode 100644 index 00000000000..adda28051af --- /dev/null +++ b/core/am/logic/src/test/java/org/apache/syncope/core/logic/OIDCOpEntityLogicTest.java @@ -0,0 +1,86 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Optional; +import org.apache.syncope.common.lib.to.OIDCOpEntityTO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.OIDCOpEntityDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.am.OIDCOpEntity; +import org.apache.syncope.core.provisioning.api.data.OIDCOpEntityDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class OIDCOpEntityLogicTest { + + private OIDCOpEntityDataBinder binder; + + private OIDCOpEntityDAO oidcOpEntityDAO; + + private EntityFactory entityFactory; + + private OIDCOpEntityLogic logic; + + @BeforeEach + void setUp() { + binder = mock(OIDCOpEntityDataBinder.class); + oidcOpEntityDAO = mock(OIDCOpEntityDAO.class); + entityFactory = mock(EntityFactory.class); + logic = new OIDCOpEntityLogic(binder, oidcOpEntityDAO, entityFactory); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void getMissing() { + when(oidcOpEntityDAO.get()).thenReturn(Optional.empty()); + + assertThrows(NotFoundException.class, () -> logic.get()); + } + + @Test + void get() { + OIDCOpEntity entity = mock(OIDCOpEntity.class); + OIDCOpEntityTO to = new OIDCOpEntityTO(); + + doReturn(Optional.of(entity)).when(oidcOpEntityDAO).get(); + when(binder.getOIDCOpEntityTO(entity)).thenReturn(to); + + assertSame(to, logic.get()); + } + + @Test + void delete() { + logic.delete(); + + verify(oidcOpEntityDAO).delete(); + } +} diff --git a/core/am/logic/src/test/java/org/apache/syncope/core/logic/PasswordManagementLogicTest.java b/core/am/logic/src/test/java/org/apache/syncope/core/logic/PasswordManagementLogicTest.java new file mode 100644 index 00000000000..d1ef13b9cbf --- /dev/null +++ b/core/am/logic/src/test/java/org/apache/syncope/core/logic/PasswordManagementLogicTest.java @@ -0,0 +1,105 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.to.PasswordManagementTO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.PasswordManagementDAO; +import org.apache.syncope.core.persistence.api.entity.am.PasswordManagement; +import org.apache.syncope.core.provisioning.api.data.PasswordManagementDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class PasswordManagementLogicTest { + + private PasswordManagementDataBinder binder; + + private PasswordManagementDAO passwordManagementDAO; + + private PasswordManagementLogic logic; + + @BeforeEach + void setUp() { + binder = mock(PasswordManagementDataBinder.class); + passwordManagementDAO = mock(PasswordManagementDAO.class); + logic = new PasswordManagementLogic(binder, passwordManagementDAO); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void readMissing() { + when(passwordManagementDAO.findById("missing")).thenReturn(Optional.empty()); + + assertThrows(NotFoundException.class, () -> logic.read("missing")); + } + + @Test + void deleteMissing() { + when(passwordManagementDAO.findById("missing")).thenReturn(Optional.empty()); + + assertThrows(NotFoundException.class, () -> logic.delete("missing")); + } + + @Test + void read() { + PasswordManagement passwordManagement = mock(PasswordManagement.class); + PasswordManagementTO to = new PasswordManagementTO(); + to.setKey("key"); + + doReturn(Optional.of(passwordManagement)).when(passwordManagementDAO).findById("key"); + when(binder.getPasswordManagementTO(passwordManagement)).thenReturn(to); + + assertSame(to, logic.read("key")); + } + + @Test + void delete() { + PasswordManagement passwordManagement = mock(PasswordManagement.class); + PasswordManagementTO to = new PasswordManagementTO(); + to.setKey("key"); + + doReturn(Optional.of(passwordManagement)).when(passwordManagementDAO).findById("key"); + when(binder.getPasswordManagementTO(passwordManagement)).thenReturn(to); + + assertSame(to, logic.delete("key")); + verify(passwordManagementDAO).delete(passwordManagement); + } + + @Test + void list() { + when(passwordManagementDAO.findAll()).thenReturn(List.of()); + + assertEquals(List.of(), logic.list()); + } +} diff --git a/core/am/logic/src/test/java/org/apache/syncope/core/logic/SAML2IdPEntityLogicTest.java b/core/am/logic/src/test/java/org/apache/syncope/core/logic/SAML2IdPEntityLogicTest.java new file mode 100644 index 00000000000..93fc6cb2dfa --- /dev/null +++ b/core/am/logic/src/test/java/org/apache/syncope/core/logic/SAML2IdPEntityLogicTest.java @@ -0,0 +1,92 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.to.SAML2IdPEntityTO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.SAML2IdPEntityDAO; +import org.apache.syncope.core.persistence.api.entity.am.SAML2IdPEntity; +import org.apache.syncope.core.provisioning.api.data.SAML2IdPEntityDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class SAML2IdPEntityLogicTest { + + private SAML2IdPEntityDataBinder binder; + + private SAML2IdPEntityDAO saml2IdPEntityDAO; + + private SAML2IdPEntityLogic logic; + + @BeforeEach + void setUp() { + binder = mock(SAML2IdPEntityDataBinder.class); + saml2IdPEntityDAO = mock(SAML2IdPEntityDAO.class); + logic = new SAML2IdPEntityLogic(binder, saml2IdPEntityDAO); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void getMissing() { + when(saml2IdPEntityDAO.findById("missing")).thenReturn(Optional.empty()); + + assertThrows(NotFoundException.class, () -> logic.get("missing")); + } + + @Test + void get() { + SAML2IdPEntity entity = mock(SAML2IdPEntity.class); + SAML2IdPEntityTO to = new SAML2IdPEntityTO(); + to.setKey("key"); + + doReturn(Optional.of(entity)).when(saml2IdPEntityDAO).findById("key"); + when(binder.getSAML2IdPEntityTO(entity)).thenReturn(to); + + assertSame(to, logic.get("key")); + } + + @Test + void delete() { + logic.delete("key"); + + verify(saml2IdPEntityDAO).deleteById("key"); + } + + @Test + void list() { + when(saml2IdPEntityDAO.findAll()).thenReturn(List.of()); + + assertEquals(List.of(), logic.list()); + } +} diff --git a/core/am/logic/src/test/java/org/apache/syncope/core/logic/init/AMEntitlementLoaderTest.java b/core/am/logic/src/test/java/org/apache/syncope/core/logic/init/AMEntitlementLoaderTest.java new file mode 100644 index 00000000000..e6db09e1676 --- /dev/null +++ b/core/am/logic/src/test/java/org/apache/syncope/core/logic/init/AMEntitlementLoaderTest.java @@ -0,0 +1,41 @@ +/* + * 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.core.logic.init; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.syncope.common.lib.types.AMEntitlement; +import org.apache.syncope.common.lib.types.EntitlementsHolder; +import org.junit.jupiter.api.Test; + +class AMEntitlementLoaderTest { + + @Test + void order() { + assertEquals(900, new AMEntitlementLoader().getOrder()); + } + + @Test + void load() { + new AMEntitlementLoader().load(); + + assertTrue(EntitlementsHolder.getInstance().getValues().containsAll(AMEntitlement.values())); + } +} diff --git a/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/ConsentDecisionLogicTest.java b/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/ConsentDecisionLogicTest.java new file mode 100644 index 00000000000..848d5b9b122 --- /dev/null +++ b/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/ConsentDecisionLogicTest.java @@ -0,0 +1,93 @@ +/* + * 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.core.logic.wa; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Optional; +import org.apache.syncope.common.lib.wa.WAConsentDecision; +import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.am.AuthProfile; +import org.apache.syncope.core.provisioning.api.data.AuthProfileDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class ConsentDecisionLogicTest { + + private AuthProfileDataBinder binder; + + private AuthProfileDAO authProfileDAO; + + private EntityFactory entityFactory; + + private ConsentDecisionLogic logic; + + @BeforeEach + void setUp() { + binder = mock(AuthProfileDataBinder.class); + authProfileDAO = mock(AuthProfileDAO.class); + entityFactory = mock(EntityFactory.class); + logic = new ConsentDecisionLogic(binder, authProfileDAO, entityFactory); + } + + @AfterEach + void tearDown() { + org.apache.syncope.core.logic.LogicTestAuth.clear(); + } + + @Test + void deleteOwner() { + AuthProfile profile = mock(AuthProfile.class); + + doReturn(Optional.of(profile)).when(authProfileDAO).findByOwner("owner"); + + logic.delete("owner"); + + verify(authProfileDAO).save(profile); + } + + @Test + void deleteAll() { + doReturn(org.springframework.data.domain.Page.empty( + org.springframework.data.domain.Pageable.unpaged())). + when(authProfileDAO).findAll(org.springframework.data.domain.Pageable.unpaged()); + + logic.deleteAll(); + + verify(authProfileDAO, times(0)).save(any()); + } + + @Test + void store() { + AuthProfile profile = mock(AuthProfile.class); + WAConsentDecision decision = new WAConsentDecision(); + + doReturn(Optional.of(profile)).when(authProfileDAO).findByOwner("owner"); + + logic.store("owner", decision); + + verify(authProfileDAO).save(profile); + } +} diff --git a/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthAccountLogicTest.java b/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthAccountLogicTest.java new file mode 100644 index 00000000000..9c25ab56e3c --- /dev/null +++ b/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthAccountLogicTest.java @@ -0,0 +1,91 @@ +/* + * 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.core.logic.wa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.am.AuthProfile; +import org.apache.syncope.core.provisioning.api.data.AuthProfileDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class GoogleMfaAuthAccountLogicTest { + + private AuthProfileDataBinder binder; + + private AuthProfileDAO authProfileDAO; + + private EntityFactory entityFactory; + + private GoogleMfaAuthAccountLogic logic; + + @BeforeEach + void setUp() { + binder = mock(AuthProfileDataBinder.class); + authProfileDAO = mock(AuthProfileDAO.class); + entityFactory = mock(EntityFactory.class); + logic = new GoogleMfaAuthAccountLogic(binder, authProfileDAO, entityFactory); + } + + @AfterEach + void tearDown() { + org.apache.syncope.core.logic.LogicTestAuth.clear(); + } + + @Test + void list() { + AuthProfile profile = mock(AuthProfile.class); + when(profile.getGoogleMfaAuthAccounts()).thenReturn(List.of()); + doReturn(org.springframework.data.domain.Page.empty( + org.springframework.data.domain.Pageable.unpaged())). + when(authProfileDAO).findAll(org.springframework.data.domain.Pageable.unpaged()); + + assertEquals(List.of(), logic.list()); + } + + @Test + void deleteOwner() { + AuthProfile profile = mock(AuthProfile.class); + doReturn(Optional.of(profile)).when(authProfileDAO).findByOwner("owner"); + + logic.delete("owner"); + + verify(authProfileDAO).save(profile); + } + + @Test + void deleteIdNotFound() { + doReturn(org.springframework.data.domain.Page.empty( + org.springframework.data.domain.Pageable.unpaged())). + when(authProfileDAO).findAll(org.springframework.data.domain.Pageable.unpaged()); + + org.junit.jupiter.api.Assertions.assertThrows( + org.apache.syncope.core.persistence.api.dao.NotFoundException.class, + () -> logic.delete(42L)); + } +} diff --git a/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthTokenLogicTest.java b/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthTokenLogicTest.java new file mode 100644 index 00000000000..81a74a108b6 --- /dev/null +++ b/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/GoogleMfaAuthTokenLogicTest.java @@ -0,0 +1,96 @@ +/* + * 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.core.logic.wa; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.am.AuthProfile; +import org.apache.syncope.core.provisioning.api.data.AuthProfileDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class GoogleMfaAuthTokenLogicTest { + + private AuthProfileDataBinder binder; + + private AuthProfileDAO authProfileDAO; + + private EntityFactory entityFactory; + + private GoogleMfaAuthTokenLogic logic; + + @BeforeEach + void setUp() { + binder = mock(AuthProfileDataBinder.class); + authProfileDAO = mock(AuthProfileDAO.class); + entityFactory = mock(EntityFactory.class); + logic = new GoogleMfaAuthTokenLogic(binder, authProfileDAO, entityFactory); + } + + @AfterEach + void tearDown() { + org.apache.syncope.core.logic.LogicTestAuth.clear(); + } + + @Test + void deleteOwner() { + AuthProfile profile = mock(AuthProfile.class); + doReturn(Optional.of(profile)).when(authProfileDAO).findByOwner("owner"); + + logic.delete("owner"); + + verify(authProfileDAO).save(profile); + } + + @Test + void deleteOtp() { + AuthProfile profile = mock(AuthProfile.class); + when(profile.getGoogleMfaAuthTokens()).thenReturn(List.of()); + doReturn(org.springframework.data.domain.Page.empty( + org.springframework.data.domain.Pageable.unpaged())). + when(authProfileDAO).findAll(org.springframework.data.domain.Pageable.unpaged()); + + logic.delete(123456); + + verify(authProfileDAO, times(0)).save(any()); + } + + @Test + void deleteExpirationDate() { + LocalDateTime expirationDate = LocalDateTime.now(); + doReturn(org.springframework.data.domain.Page.empty( + org.springframework.data.domain.Pageable.unpaged())). + when(authProfileDAO).findAll(org.springframework.data.domain.Pageable.unpaged()); + + logic.delete(expirationDate); + + verify(authProfileDAO, times(0)).save(any()); + } +} diff --git a/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/ImpersonationLogicTest.java b/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/ImpersonationLogicTest.java new file mode 100644 index 00000000000..cd478d4539e --- /dev/null +++ b/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/ImpersonationLogicTest.java @@ -0,0 +1,90 @@ +/* + * 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.core.logic.wa; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.wa.ImpersonationAccount; +import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.am.AuthProfile; +import org.apache.syncope.core.provisioning.api.data.AuthProfileDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class ImpersonationLogicTest { + + private AuthProfileDataBinder binder; + + private AuthProfileDAO authProfileDAO; + + private EntityFactory entityFactory; + + private ImpersonationLogic logic; + + @BeforeEach + void setUp() { + binder = mock(AuthProfileDataBinder.class); + authProfileDAO = mock(AuthProfileDAO.class); + entityFactory = mock(EntityFactory.class); + logic = new ImpersonationLogic(binder, authProfileDAO, entityFactory); + } + + @AfterEach + void tearDown() { + org.apache.syncope.core.logic.LogicTestAuth.clear(); + } + + @Test + void readOwnerNotFound() { + when(authProfileDAO.findByOwner("owner")).thenReturn(Optional.empty()); + + assertSame(List.of(), logic.read("owner")); + } + + @Test + void create() { + AuthProfile profile = mock(AuthProfile.class); + ImpersonationAccount account = new ImpersonationAccount(); + + doReturn(Optional.of(profile)).when(authProfileDAO).findByOwner("owner"); + when(profile.getImpersonationAccounts()).thenReturn(List.of()); + + logic.create("owner", account); + + verify(authProfileDAO).save(profile); + } + + @Test + void delete() { + AuthProfile profile = mock(AuthProfile.class); + doReturn(Optional.of(profile)).when(authProfileDAO).findByOwner("owner"); + + logic.delete("owner", "impersonated"); + + verify(authProfileDAO, org.mockito.Mockito.times(0)).save(org.mockito.Mockito.any()); + } +} diff --git a/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/MfaTrusStorageLogicTest.java b/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/MfaTrusStorageLogicTest.java new file mode 100644 index 00000000000..c713b0ea845 --- /dev/null +++ b/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/MfaTrusStorageLogicTest.java @@ -0,0 +1,83 @@ +/* + * 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.core.logic.wa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import java.time.OffsetDateTime; +import java.util.List; +import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.provisioning.api.data.AuthProfileDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +class MfaTrusStorageLogicTest { + + private AuthProfileDataBinder binder; + + private AuthProfileDAO authProfileDAO; + + private EntityFactory entityFactory; + + private MfaTrusStorageLogic logic; + + @BeforeEach + void setUp() { + binder = mock(AuthProfileDataBinder.class); + authProfileDAO = mock(AuthProfileDAO.class); + entityFactory = mock(EntityFactory.class); + logic = new MfaTrusStorageLogic(binder, authProfileDAO, entityFactory); + } + + @AfterEach + void tearDown() { + org.apache.syncope.core.logic.LogicTestAuth.clear(); + } + + @Test + void searchEmpty() { + Pageable pageable = PageRequest.of(0, 10); + doReturn(new PageImpl<>(List.of(), pageable, 0)). + when(authProfileDAO).findAll(any(Pageable.class)); + + assertEquals(0L, logic.search(null, null, null, pageable).getTotalElements()); + } + + @Test + void deleteExpirationDate() { + OffsetDateTime expirationDate = OffsetDateTime.now(); + Pageable pageable = PageRequest.of(0, 10); + doReturn(new PageImpl<>(List.of(), pageable, 0)). + when(authProfileDAO).findAll(any(Pageable.class)); + + logic.delete(expirationDate, "recordKey"); + + verify(authProfileDAO, org.mockito.Mockito.times(0)). + save(org.mockito.Mockito.any()); + } +} diff --git a/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/WAClientAppLogicTest.java b/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/WAClientAppLogicTest.java new file mode 100644 index 00000000000..cabca483088 --- /dev/null +++ b/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/WAClientAppLogicTest.java @@ -0,0 +1,69 @@ +/* + * 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.core.logic.wa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; +import org.apache.syncope.core.persistence.api.dao.CASSPClientAppDAO; +import org.apache.syncope.core.persistence.api.dao.OIDCRPClientAppDAO; +import org.apache.syncope.core.persistence.api.dao.SAML2SPClientAppDAO; +import org.apache.syncope.core.provisioning.api.data.wa.WAClientAppDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class WAClientAppLogicTest { + + private WAClientAppDataBinder binder; + + private CASSPClientAppDAO casSPClientAppDAO; + + private OIDCRPClientAppDAO oidcRPClientAppDAO; + + private SAML2SPClientAppDAO saml2SPClientAppDAO; + + private WAClientAppLogic logic; + + @BeforeEach + void setUp() { + binder = mock(WAClientAppDataBinder.class); + casSPClientAppDAO = mock(CASSPClientAppDAO.class); + oidcRPClientAppDAO = mock(OIDCRPClientAppDAO.class); + saml2SPClientAppDAO = mock(SAML2SPClientAppDAO.class); + + logic = new WAClientAppLogic(binder, casSPClientAppDAO, oidcRPClientAppDAO, saml2SPClientAppDAO); + } + + @AfterEach + void tearDown() { + org.apache.syncope.core.logic.LogicTestAuth.clear(); + } + + @Test + void list() { + when(oidcRPClientAppDAO.findAll()).thenReturn(List.of()); + when(saml2SPClientAppDAO.findAll()).thenReturn(List.of()); + when(casSPClientAppDAO.findAll()).thenReturn(List.of()); + + assertEquals(List.of(), logic.list()); + } +} diff --git a/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/WAConfigLogicTest.java b/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/WAConfigLogicTest.java new file mode 100644 index 00000000000..956d70e9e9f --- /dev/null +++ b/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/WAConfigLogicTest.java @@ -0,0 +1,75 @@ +/* + * 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.core.logic.wa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.WAConfigDAO; +import org.apache.syncope.core.provisioning.api.data.WAConfigDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class WAConfigLogicTest { + + private org.apache.syncope.common.keymaster.client.api.ServiceOps serviceOps; + + private WAConfigDataBinder binder; + + private WAConfigDAO waConfigDAO; + + private org.apache.syncope.core.spring.security.SecurityProperties securityProperties; + + private WAConfigLogic logic; + + @BeforeEach + void setUp() { + serviceOps = mock(org.apache.syncope.common.keymaster.client.api.ServiceOps.class); + binder = mock(WAConfigDataBinder.class); + waConfigDAO = mock(WAConfigDAO.class); + securityProperties = mock(org.apache.syncope.core.spring.security.SecurityProperties.class); + + logic = new WAConfigLogic(serviceOps, binder, waConfigDAO, securityProperties); + } + + @AfterEach + void tearDown() { + org.apache.syncope.core.logic.LogicTestAuth.clear(); + } + + @Test + void list() { + doReturn(List.of()).when(waConfigDAO).findAll(); + + assertEquals(List.of(), logic.list()); + } + + @Test + void getMissing() { + doReturn(Optional.empty()).when(waConfigDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.get("missing")); + } +} diff --git a/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/WebAuthnRegistrationLogicTest.java b/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/WebAuthnRegistrationLogicTest.java new file mode 100644 index 00000000000..bd8b294c51d --- /dev/null +++ b/core/am/logic/src/test/java/org/apache/syncope/core/logic/wa/WebAuthnRegistrationLogicTest.java @@ -0,0 +1,87 @@ +/* + * 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.core.logic.wa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.am.AuthProfile; +import org.apache.syncope.core.provisioning.api.data.AuthProfileDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class WebAuthnRegistrationLogicTest { + + private AuthProfileDataBinder binder; + + private AuthProfileDAO authProfileDAO; + + private EntityFactory entityFactory; + + private WebAuthnRegistrationLogic logic; + + @BeforeEach + void setUp() { + binder = mock(AuthProfileDataBinder.class); + authProfileDAO = mock(AuthProfileDAO.class); + entityFactory = mock(EntityFactory.class); + logic = new WebAuthnRegistrationLogic(binder, authProfileDAO, entityFactory); + } + + @AfterEach + void tearDown() { + org.apache.syncope.core.logic.LogicTestAuth.clear(); + } + + @Test + void list() { + doReturn(org.springframework.data.domain.Page.empty( + org.springframework.data.domain.Pageable.unpaged())). + when(authProfileDAO).findAll(org.springframework.data.domain.Pageable.unpaged()); + + assertEquals(List.of(), logic.list()); + } + + @Test + void readOwnerNotFound() { + when(authProfileDAO.findByOwner("owner")).thenReturn(Optional.empty()); + + org.junit.jupiter.api.Assertions.assertThrows( + org.apache.syncope.core.persistence.api.dao.NotFoundException.class, + () -> logic.read("owner")); + } + + @Test + void deleteOwner() { + AuthProfile profile = mock(AuthProfile.class); + doReturn(Optional.of(profile)).when(authProfileDAO).findByOwner("owner"); + + logic.delete("owner"); + + verify(authProfileDAO).save(profile); + } +} diff --git a/core/am/rest-cxf/pom.xml b/core/am/rest-cxf/pom.xml index 14afbd9b69d..c334ea0a4f0 100644 --- a/core/am/rest-cxf/pom.xml +++ b/core/am/rest-cxf/pom.xml @@ -65,6 +65,23 @@ under the License. ${project.version} javadoc + + + + org.springframework + spring-test + test + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-junit-jupiter + test + diff --git a/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AttrRepoServiceTest.java b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AttrRepoServiceTest.java new file mode 100644 index 00000000000..fddf0834b2c --- /dev/null +++ b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AttrRepoServiceTest.java @@ -0,0 +1,96 @@ +/* + * 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.core.rest.cxf.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; +import java.net.URI; +import java.util.List; +import org.apache.syncope.common.lib.to.AttrRepoTO; +import org.apache.syncope.core.logic.AttrRepoLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +class AttrRepoServiceTest { + + private AttrRepoLogic logic; + + private AttrRepoServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(AttrRepoLogic.class); + service = new AttrRepoServiceImpl(logic); + + UriInfo uriInfo = mock(UriInfo.class); + UriBuilder uriBuilder = mock(UriBuilder.class); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder); + when(uriBuilder.path(anyString())).thenReturn(uriBuilder); + when(uriBuilder.build()).thenReturn(URI.create("http://localhost/")); + when(uriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<>()); + ReflectionTestUtils.setField(service, "uriInfo", uriInfo); + } + + @Test + void create() { + AttrRepoTO to = new AttrRepoTO(); + to.setKey("key"); + when(logic.create(to)).thenReturn(to); + Response response = service.create(to); + assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus()); + } + + @Test + void delete() { + service.delete("key"); + verify(logic).delete("key"); + } + + @Test + void list() { + when(logic.list()).thenReturn(List.of()); + assertEquals(List.of(), service.list()); + } + + @Test + void read() { + AttrRepoTO to = new AttrRepoTO(); + to.setKey("key"); + when(logic.read("key")).thenReturn(to); + assertSame(to, service.read("key")); + } + + @Test + void update() { + AttrRepoTO to = new AttrRepoTO(); + to.setKey("key"); + service.update(to); + verify(logic).update(to); + } +} diff --git a/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AuthModuleServiceTest.java b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AuthModuleServiceTest.java new file mode 100644 index 00000000000..73f45c3e7b3 --- /dev/null +++ b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AuthModuleServiceTest.java @@ -0,0 +1,96 @@ +/* + * 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.core.rest.cxf.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; +import java.net.URI; +import java.util.List; +import org.apache.syncope.common.lib.to.AuthModuleTO; +import org.apache.syncope.core.logic.AuthModuleLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +class AuthModuleServiceTest { + + private AuthModuleLogic logic; + + private AuthModuleServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(AuthModuleLogic.class); + service = new AuthModuleServiceImpl(logic); + + UriInfo uriInfo = mock(UriInfo.class); + UriBuilder uriBuilder = mock(UriBuilder.class); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder); + when(uriBuilder.path(anyString())).thenReturn(uriBuilder); + when(uriBuilder.build()).thenReturn(URI.create("http://localhost/")); + when(uriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<>()); + ReflectionTestUtils.setField(service, "uriInfo", uriInfo); + } + + @Test + void create() { + AuthModuleTO to = new AuthModuleTO(); + to.setKey("key"); + when(logic.create(to)).thenReturn(to); + Response response = service.create(to); + assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus()); + } + + @Test + void delete() { + service.delete("key"); + verify(logic).delete("key"); + } + + @Test + void list() { + when(logic.list()).thenReturn(List.of()); + assertEquals(List.of(), service.list()); + } + + @Test + void read() { + AuthModuleTO to = new AuthModuleTO(); + to.setKey("key"); + when(logic.read("key")).thenReturn(to); + assertSame(to, service.read("key")); + } + + @Test + void update() { + AuthModuleTO to = new AuthModuleTO(); + to.setKey("key"); + service.update(to); + verify(logic).update(to); + } +} diff --git a/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AuthProfileSelfServiceTest.java b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AuthProfileSelfServiceTest.java new file mode 100644 index 00000000000..411361ce4de --- /dev/null +++ b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AuthProfileSelfServiceTest.java @@ -0,0 +1,64 @@ +/* + * 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.core.rest.cxf.service; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.syncope.common.lib.to.AuthProfileTO; +import org.apache.syncope.core.logic.AuthProfileLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class AuthProfileSelfServiceTest { + + private AuthProfileLogic logic; + + private AuthProfileSelfServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(AuthProfileLogic.class); + service = new AuthProfileSelfServiceImpl(logic); + } + + @Test + void read() { + AuthProfileTO to = new AuthProfileTO(); + to.setKey("key"); + when(logic.selfRead()).thenReturn(to); + assertSame(to, service.read()); + } + + @Test + void update() { + AuthProfileTO to = new AuthProfileTO(); + to.setKey("key"); + service.update(to); + verify(logic).selfUpdate(to); + } + + @Test + void delete() { + service.delete(); + verify(logic).selfDelete(); + } +} diff --git a/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AuthProfileServiceTest.java b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AuthProfileServiceTest.java new file mode 100644 index 00000000000..7a28cfd9548 --- /dev/null +++ b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AuthProfileServiceTest.java @@ -0,0 +1,123 @@ +/* + * 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.core.rest.cxf.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; +import java.net.URI; +import java.util.List; +import java.util.stream.Stream; +import org.apache.syncope.common.lib.to.AuthProfileTO; +import org.apache.syncope.common.lib.to.PagedResult; +import org.apache.syncope.common.rest.api.beans.AuthProfileQuery; +import org.apache.syncope.core.logic.AuthProfileLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Page; +import org.springframework.test.util.ReflectionTestUtils; + +class AuthProfileServiceTest { + + private AuthProfileLogic logic; + + private AuthProfileServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(AuthProfileLogic.class); + service = new AuthProfileServiceImpl(logic); + + UriInfo uriInfo = mock(UriInfo.class); + UriBuilder uriBuilder = mock(UriBuilder.class); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder); + when(uriBuilder.path(anyString())).thenReturn(uriBuilder); + when(uriBuilder.build()).thenReturn(URI.create("http://localhost/")); + when(uriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<>()); + ReflectionTestUtils.setField(service, "uriInfo", uriInfo); + } + + @SuppressWarnings("unchecked") + @Test + void search() { + AuthProfileQuery query = new AuthProfileQuery(); + query.setPage(1); + query.setSize(10); + query.setKeyword("foo*"); + + AuthProfileTO to = new AuthProfileTO(); + to.setKey("key"); + + Page page = mock(Page.class); + when(page.get()).thenReturn(Stream.of(to)); + when(page.getNumber()).thenReturn(0); + when(page.getSize()).thenReturn(10); + when(page.getTotalElements()).thenReturn(1L); + when(logic.search(eq("foo%"), any())).thenReturn(page); + + PagedResult result = service.search(query); + + assertEquals(1, result.getPage()); + assertEquals(1, result.getSize()); + assertEquals(1L, result.getTotalCount()); + assertEquals(List.of(to), result.getResult()); + verify(logic).search(eq("foo%"), any()); + } + + @Test + void read() { + AuthProfileTO to = new AuthProfileTO(); + to.setKey("key"); + when(logic.read("key")).thenReturn(to); + assertSame(to, service.read("key")); + } + + @Test + void create() { + AuthProfileTO to = new AuthProfileTO(); + to.setKey("key"); + when(logic.create(to)).thenReturn(to); + Response response = service.create(to); + assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus()); + } + + @Test + void update() { + AuthProfileTO to = new AuthProfileTO(); + to.setKey("key"); + service.update(to); + verify(logic).update(to); + } + + @Test + void delete() { + service.delete("key"); + verify(logic).delete("key"); + } +} diff --git a/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/ClientAppServiceTest.java b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/ClientAppServiceTest.java new file mode 100644 index 00000000000..f99a2c5e73a --- /dev/null +++ b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/ClientAppServiceTest.java @@ -0,0 +1,97 @@ +/* + * 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.core.rest.cxf.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; +import java.net.URI; +import java.util.List; +import org.apache.syncope.common.lib.to.OIDCRPClientAppTO; +import org.apache.syncope.common.lib.types.ClientAppType; +import org.apache.syncope.core.logic.ClientAppLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +class ClientAppServiceTest { + + private ClientAppLogic logic; + + private ClientAppServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(ClientAppLogic.class); + service = new ClientAppServiceImpl(logic); + + UriInfo uriInfo = mock(UriInfo.class); + UriBuilder uriBuilder = mock(UriBuilder.class); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder); + when(uriBuilder.path(anyString())).thenReturn(uriBuilder); + when(uriBuilder.build()).thenReturn(URI.create("http://localhost/")); + when(uriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<>()); + ReflectionTestUtils.setField(service, "uriInfo", uriInfo); + } + + @Test + void create() { + OIDCRPClientAppTO to = new OIDCRPClientAppTO(); + to.setKey("key"); + when(logic.create(ClientAppType.OIDCRP, to)).thenReturn(to); + Response response = service.create(ClientAppType.OIDCRP, to); + assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus()); + } + + @Test + void list() { + when(logic.list(ClientAppType.OIDCRP)).thenReturn(List.of()); + assertEquals(List.of(), service.list(ClientAppType.OIDCRP)); + } + + @Test + void read() { + OIDCRPClientAppTO to = new OIDCRPClientAppTO(); + to.setKey("key"); + when(logic.read(ClientAppType.OIDCRP, "key")).thenReturn(to); + assertSame(to, service.read(ClientAppType.OIDCRP, "key")); + } + + @Test + void update() { + OIDCRPClientAppTO to = new OIDCRPClientAppTO(); + to.setKey("key"); + service.update(ClientAppType.OIDCRP, to); + verify(logic).update(ClientAppType.OIDCRP, to); + } + + @Test + void delete() { + service.delete(ClientAppType.OIDCRP, "key"); + verify(logic).delete(ClientAppType.OIDCRP, "key"); + } +} diff --git a/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/OIDCOpEntityServiceTest.java b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/OIDCOpEntityServiceTest.java new file mode 100644 index 00000000000..5010d05d066 --- /dev/null +++ b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/OIDCOpEntityServiceTest.java @@ -0,0 +1,83 @@ +/* + * 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.core.rest.cxf.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; +import java.net.URI; +import org.apache.syncope.common.lib.to.OIDCOpEntityTO; +import org.apache.syncope.core.logic.OIDCOpEntityLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +class OIDCOpEntityServiceTest { + + private OIDCOpEntityLogic logic; + + private OIDCOpEntityServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(OIDCOpEntityLogic.class); + service = new OIDCOpEntityServiceImpl(logic); + + UriInfo uriInfo = mock(UriInfo.class); + UriBuilder uriBuilder = mock(UriBuilder.class); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder); + when(uriBuilder.build()).thenReturn(URI.create("http://localhost/")); + ReflectionTestUtils.setField(service, "uriInfo", uriInfo); + } + + @Test + void get() { + OIDCOpEntityTO to = new OIDCOpEntityTO(); + when(logic.get()).thenReturn(to); + assertSame(to, service.get()); + } + + @Test + void set() { + OIDCOpEntityTO to = new OIDCOpEntityTO(); + service.set(to); + verify(logic).set(to); + } + + @Test + void generate() { + OIDCOpEntityTO to = new OIDCOpEntityTO(); + when(logic.generate("keyId", "oct", 256)).thenReturn(to); + Response response = service.generate("keyId", "oct", 256); + assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus()); + assertSame(to, response.getEntity()); + } + + @Test + void delete() { + service.delete(); + verify(logic).delete(); + } +} diff --git a/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/PasswordManagementServiceTest.java b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/PasswordManagementServiceTest.java new file mode 100644 index 00000000000..63ff404703d --- /dev/null +++ b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/PasswordManagementServiceTest.java @@ -0,0 +1,96 @@ +/* + * 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.core.rest.cxf.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; +import java.net.URI; +import java.util.List; +import org.apache.syncope.common.lib.to.PasswordManagementTO; +import org.apache.syncope.core.logic.PasswordManagementLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +class PasswordManagementServiceTest { + + private PasswordManagementLogic logic; + + private PasswordManagementServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(PasswordManagementLogic.class); + service = new PasswordManagementServiceImpl(logic); + + UriInfo uriInfo = mock(UriInfo.class); + UriBuilder uriBuilder = mock(UriBuilder.class); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder); + when(uriBuilder.path(anyString())).thenReturn(uriBuilder); + when(uriBuilder.build()).thenReturn(URI.create("http://localhost/")); + when(uriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<>()); + ReflectionTestUtils.setField(service, "uriInfo", uriInfo); + } + + @Test + void create() { + PasswordManagementTO to = new PasswordManagementTO(); + to.setKey("key"); + when(logic.create(to)).thenReturn(to); + Response response = service.create(to); + assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus()); + } + + @Test + void delete() { + service.delete("key"); + verify(logic).delete("key"); + } + + @Test + void list() { + when(logic.list()).thenReturn(List.of()); + assertEquals(List.of(), service.list()); + } + + @Test + void read() { + PasswordManagementTO to = new PasswordManagementTO(); + to.setKey("key"); + when(logic.read("key")).thenReturn(to); + assertSame(to, service.read("key")); + } + + @Test + void update() { + PasswordManagementTO to = new PasswordManagementTO(); + to.setKey("key"); + service.update(to); + verify(logic).update(to); + } +} diff --git a/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/SAML2IdPEntityServiceTest.java b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/SAML2IdPEntityServiceTest.java new file mode 100644 index 00000000000..eeec9e439e7 --- /dev/null +++ b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/SAML2IdPEntityServiceTest.java @@ -0,0 +1,72 @@ +/* + * 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.core.rest.cxf.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import org.apache.syncope.common.lib.to.SAML2IdPEntityTO; +import org.apache.syncope.core.logic.SAML2IdPEntityLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class SAML2IdPEntityServiceTest { + + private SAML2IdPEntityLogic logic; + + private SAML2IdPEntityServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(SAML2IdPEntityLogic.class); + service = new SAML2IdPEntityServiceImpl(logic); + } + + @Test + void list() { + when(logic.list()).thenReturn(List.of()); + assertEquals(List.of(), service.list()); + } + + @Test + void get() { + SAML2IdPEntityTO to = new SAML2IdPEntityTO(); + to.setKey("key"); + when(logic.get("key")).thenReturn(to); + assertSame(to, service.get("key")); + } + + @Test + void set() { + SAML2IdPEntityTO to = new SAML2IdPEntityTO(); + to.setKey("key"); + service.set(to); + verify(logic).set(to); + } + + @Test + void delete() { + service.delete("key"); + verify(logic).delete("key"); + } +} diff --git a/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/ConsentDecisionServiceTest.java b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/ConsentDecisionServiceTest.java new file mode 100644 index 00000000000..34b6f26cf5f --- /dev/null +++ b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/ConsentDecisionServiceTest.java @@ -0,0 +1,99 @@ +/* + * 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.core.rest.cxf.service.wa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import org.apache.syncope.common.lib.to.PagedResult; +import org.apache.syncope.common.lib.wa.WAConsentDecision; +import org.apache.syncope.core.logic.wa.ConsentDecisionLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class ConsentDecisionServiceTest { + + private ConsentDecisionLogic logic; + + private ConsentDecisionServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(ConsentDecisionLogic.class); + service = new ConsentDecisionServiceImpl(logic); + } + + @Test + void deleteByOwnerAndId() { + service.delete("owner", 1L); + verify(logic).delete("owner", 1L); + } + + @Test + void deleteByOwner() { + service.delete("owner"); + verify(logic).delete("owner"); + } + + @Test + void deleteAll() { + service.deleteAll(); + verify(logic).deleteAll(); + } + + @Test + void store() { + WAConsentDecision decision = new WAConsentDecision(); + service.store("owner", decision); + verify(logic).store("owner", decision); + } + + @Test + void readByOwnerAndService() { + WAConsentDecision decision = new WAConsentDecision(); + when(logic.read("owner", "service")).thenReturn(decision); + assertSame(decision, service.read("owner", "service")); + } + + @Test + void readByOwner() { + WAConsentDecision decision = new WAConsentDecision(); + when(logic.read("owner")).thenReturn(List.of(decision)); + PagedResult result = service.read("owner"); + assertEquals(1, result.getPage()); + assertEquals(1, result.getSize()); + assertEquals(1L, result.getTotalCount()); + assertEquals(List.of(decision), result.getResult()); + } + + @Test + void list() { + WAConsentDecision decision = new WAConsentDecision(); + when(logic.list()).thenReturn(List.of(decision)); + PagedResult result = service.list(); + assertEquals(1, result.getPage()); + assertEquals(1, result.getSize()); + assertEquals(1L, result.getTotalCount()); + assertEquals(List.of(decision), result.getResult()); + } +} diff --git a/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/GoogleMfaAuthAccountServiceTest.java b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/GoogleMfaAuthAccountServiceTest.java new file mode 100644 index 00000000000..51f28de358e --- /dev/null +++ b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/GoogleMfaAuthAccountServiceTest.java @@ -0,0 +1,106 @@ +/* + * 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.core.rest.cxf.service.wa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import org.apache.syncope.common.lib.to.PagedResult; +import org.apache.syncope.common.lib.wa.GoogleMfaAuthAccount; +import org.apache.syncope.core.logic.wa.GoogleMfaAuthAccountLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class GoogleMfaAuthAccountServiceTest { + + private GoogleMfaAuthAccountLogic logic; + + private GoogleMfaAuthAccountServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(GoogleMfaAuthAccountLogic.class); + service = new GoogleMfaAuthAccountServiceImpl(logic); + } + + @Test + void create() { + GoogleMfaAuthAccount account = new GoogleMfaAuthAccount(); + service.create(account); + verify(logic).create(account); + } + + @Test + void update() { + GoogleMfaAuthAccount account = new GoogleMfaAuthAccount(); + service.update(account); + verify(logic).update(account); + } + + @Test + void deleteByOwner() { + service.delete("owner"); + verify(logic).delete("owner"); + } + + @Test + void deleteById() { + service.delete(1L); + verify(logic).delete(1L); + } + + @Test + void deleteAll() { + service.deleteAll(); + verify(logic).deleteAll(); + } + + @Test + void readByOwner() { + GoogleMfaAuthAccount account = new GoogleMfaAuthAccount(); + when(logic.read("owner")).thenReturn(List.of(account)); + PagedResult result = service.read("owner"); + assertEquals(1, result.getPage()); + assertEquals(1, result.getSize()); + assertEquals(1L, result.getTotalCount()); + assertEquals(List.of(account), result.getResult()); + } + + @Test + void readById() { + GoogleMfaAuthAccount account = new GoogleMfaAuthAccount(); + when(logic.read(1L)).thenReturn(account); + assertSame(account, service.read(1L)); + } + + @Test + void list() { + GoogleMfaAuthAccount account = new GoogleMfaAuthAccount(); + when(logic.list()).thenReturn(List.of(account)); + PagedResult result = service.list(); + assertEquals(1, result.getPage()); + assertEquals(1, result.getSize()); + assertEquals(1L, result.getTotalCount()); + assertEquals(List.of(account), result.getResult()); + } +} diff --git a/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/GoogleMfaAuthTokenServiceTest.java b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/GoogleMfaAuthTokenServiceTest.java new file mode 100644 index 00000000000..74652a871a0 --- /dev/null +++ b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/GoogleMfaAuthTokenServiceTest.java @@ -0,0 +1,113 @@ +/* + * 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.core.rest.cxf.service.wa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.LocalDateTime; +import java.util.List; +import org.apache.syncope.common.lib.to.PagedResult; +import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken; +import org.apache.syncope.core.logic.wa.GoogleMfaAuthTokenLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class GoogleMfaAuthTokenServiceTest { + + private GoogleMfaAuthTokenLogic logic; + + private GoogleMfaAuthTokenServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(GoogleMfaAuthTokenLogic.class); + service = new GoogleMfaAuthTokenServiceImpl(logic); + } + + @Test + void deleteAllTokens() { + service.delete((LocalDateTime) null); + verify(logic).deleteAll(); + } + + @Test + void deleteExpiredTokens() { + LocalDateTime expirationDate = LocalDateTime.now(); + service.delete(expirationDate); + verify(logic).delete(expirationDate); + } + + @Test + void deleteByOwnerAndOtp() { + service.delete("owner", 1); + verify(logic).delete("owner", 1); + } + + @Test + void deleteByOwner() { + service.delete("owner"); + verify(logic).delete("owner"); + } + + @Test + void deleteByOtp() { + service.delete(1); + verify(logic).delete(1); + } + + @Test + void store() { + GoogleMfaAuthToken token = new GoogleMfaAuthToken(); + service.store("owner", token); + verify(logic).store("owner", token); + } + + @Test + void readByOwnerAndOtp() { + GoogleMfaAuthToken token = new GoogleMfaAuthToken(); + when(logic.read("owner", 1)).thenReturn(token); + assertSame(token, service.read("owner", 1)); + } + + @Test + void readByOwner() { + GoogleMfaAuthToken token = new GoogleMfaAuthToken(); + when(logic.read("owner")).thenReturn(List.of(token)); + PagedResult result = service.read("owner"); + assertEquals(1, result.getPage()); + assertEquals(1, result.getSize()); + assertEquals(1L, result.getTotalCount()); + assertEquals(List.of(token), result.getResult()); + } + + @Test + void list() { + GoogleMfaAuthToken token = new GoogleMfaAuthToken(); + when(logic.list()).thenReturn(List.of(token)); + PagedResult result = service.list(); + assertEquals(1, result.getPage()); + assertEquals(1, result.getSize()); + assertEquals(1L, result.getTotalCount()); + assertEquals(List.of(token), result.getResult()); + } +} diff --git a/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/ImpersonationServiceTest.java b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/ImpersonationServiceTest.java new file mode 100644 index 00000000000..fc29d88601b --- /dev/null +++ b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/ImpersonationServiceTest.java @@ -0,0 +1,63 @@ +/* + * 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.core.rest.cxf.service.wa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import org.apache.syncope.common.lib.wa.ImpersonationAccount; +import org.apache.syncope.core.logic.wa.ImpersonationLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class ImpersonationServiceTest { + + private ImpersonationLogic logic; + + private ImpersonationServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(ImpersonationLogic.class); + service = new ImpersonationServiceImpl(logic); + } + + @Test + void read() { + ImpersonationAccount account = new ImpersonationAccount(); + when(logic.read("owner")).thenReturn(List.of(account)); + assertEquals(List.of(account), service.read("owner")); + } + + @Test + void create() { + ImpersonationAccount account = new ImpersonationAccount(); + service.create("owner", account); + verify(logic).create("owner", account); + } + + @Test + void delete() { + service.delete("owner", "impersonated"); + verify(logic).delete("owner", "impersonated"); + } +} diff --git a/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/MfaTrustStorageServiceTest.java b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/MfaTrustStorageServiceTest.java new file mode 100644 index 00000000000..ebcc203a2da --- /dev/null +++ b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/MfaTrustStorageServiceTest.java @@ -0,0 +1,103 @@ +/* + * 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.core.rest.cxf.service.wa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.UriInfo; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.stream.Stream; +import org.apache.syncope.common.lib.to.PagedResult; +import org.apache.syncope.common.lib.wa.MfaTrustedDevice; +import org.apache.syncope.common.rest.api.beans.MfaTrustedDeviceQuery; +import org.apache.syncope.core.logic.wa.MfaTrusStorageLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Page; +import org.springframework.test.util.ReflectionTestUtils; + +class MfaTrustStorageServiceTest { + + private MfaTrusStorageLogic logic; + + private MfaTrustStorageServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(MfaTrusStorageLogic.class); + service = new MfaTrustStorageServiceImpl(logic); + + UriInfo uriInfo = mock(UriInfo.class); + when(uriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<>()); + ReflectionTestUtils.setField(service, "uriInfo", uriInfo); + } + + @SuppressWarnings("unchecked") + @Test + void search() { + MfaTrustedDeviceQuery query = new MfaTrustedDeviceQuery(); + query.setPage(1); + query.setSize(10); + query.setPrincipal("user"); + query.setId(1L); + OffsetDateTime recordDate = OffsetDateTime.now(); + query.setRecordDate(recordDate); + + MfaTrustedDevice device = new MfaTrustedDevice(); + + Page page = mock(Page.class); + when(page.get()).thenReturn(Stream.of(device)); + when(page.getNumber()).thenReturn(0); + when(page.getSize()).thenReturn(10); + when(page.getTotalElements()).thenReturn(1L); + when(logic.search(eq("user"), eq(1L), eq(recordDate), any())).thenReturn(page); + + PagedResult result = service.search(query); + + assertEquals(1, result.getPage()); + assertEquals(1, result.getSize()); + assertEquals(1L, result.getTotalCount()); + assertEquals(List.of(device), result.getResult()); + verify(logic).search(eq("user"), eq(1L), eq(recordDate), any()); + } + + @Test + void create() { + MfaTrustedDevice device = new MfaTrustedDevice(); + service.create("owner", device); + verify(logic).create("owner", device); + } + + @Test + void delete() { + MfaTrustedDeviceQuery query = new MfaTrustedDeviceQuery(); + OffsetDateTime expirationDate = OffsetDateTime.now(); + query.setExpirationDate(expirationDate); + query.setRecordKey("recordKey"); + service.delete(query); + verify(logic).delete(expirationDate, "recordKey"); + } +} diff --git a/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/WAClientAppServiceTest.java b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/WAClientAppServiceTest.java new file mode 100644 index 00000000000..c701d38c023 --- /dev/null +++ b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/WAClientAppServiceTest.java @@ -0,0 +1,65 @@ +/* + * 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.core.rest.cxf.service.wa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; +import org.apache.syncope.common.lib.types.ClientAppType; +import org.apache.syncope.common.lib.wa.WAClientApp; +import org.apache.syncope.core.logic.wa.WAClientAppLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class WAClientAppServiceTest { + + private WAClientAppLogic logic; + + private WAClientAppServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(WAClientAppLogic.class); + service = new WAClientAppServiceImpl(logic); + } + + @Test + void list() { + WAClientApp app = new WAClientApp(); + when(logic.list()).thenReturn(List.of(app)); + assertEquals(List.of(app), service.list()); + } + + @Test + void readById() { + WAClientApp app = new WAClientApp(); + when(logic.read(1L, ClientAppType.SAML2SP)).thenReturn(app); + assertSame(app, service.read(1L, ClientAppType.SAML2SP)); + } + + @Test + void readByName() { + WAClientApp app = new WAClientApp(); + when(logic.read("name", ClientAppType.SAML2SP)).thenReturn(app); + assertSame(app, service.read("name", ClientAppType.SAML2SP)); + } +} diff --git a/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/WAConfigServiceTest.java b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/WAConfigServiceTest.java new file mode 100644 index 00000000000..39367fbd309 --- /dev/null +++ b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/WAConfigServiceTest.java @@ -0,0 +1,78 @@ +/* + * 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.core.rest.cxf.service.wa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import org.apache.syncope.common.lib.Attr; +import org.apache.syncope.common.rest.api.service.wa.WAConfigService.PushSubject; +import org.apache.syncope.core.logic.wa.WAConfigLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class WAConfigServiceTest { + + private WAConfigLogic logic; + + private WAConfigServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(WAConfigLogic.class); + service = new WAConfigServiceImpl(logic); + } + + @Test + void list() { + Attr attr = new Attr.Builder("schema").value("value").build(); + when(logic.list()).thenReturn(List.of(attr)); + assertEquals(List.of(attr), service.list()); + } + + @Test + void delete() { + service.delete("schema"); + verify(logic).delete("schema"); + } + + @Test + void get() { + Attr attr = new Attr.Builder("schema").value("value").build(); + when(logic.get("schema")).thenReturn(attr); + assertSame(attr, service.get("schema")); + } + + @Test + void set() { + Attr attr = new Attr.Builder("schema").value("value").build(); + service.set(attr); + verify(logic).set(attr); + } + + @Test + void pushToWA() { + service.pushToWA(PushSubject.conf, List.of("service")); + verify(logic).pushToWA(PushSubject.conf, List.of("service")); + } +} diff --git a/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/WASAML2SPServiceTest.java b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/WASAML2SPServiceTest.java new file mode 100644 index 00000000000..5a144e8228d --- /dev/null +++ b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/WASAML2SPServiceTest.java @@ -0,0 +1,103 @@ +/* + * 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.core.rest.cxf.service.wa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.Response; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import org.apache.syncope.common.lib.auth.SAML2IdPAuthModuleConf; +import org.apache.syncope.core.logic.AuthModuleLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class WASAML2SPServiceTest { + + private AuthModuleLogic logic; + + private WASAML2SPServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(AuthModuleLogic.class); + service = new WASAML2SPServiceImpl(logic); + } + + @Test + void getSAML2SPKeystore() { + SAML2IdPAuthModuleConf conf = new SAML2IdPAuthModuleConf(); + conf.setKeystore("keystore"); + when(logic.readSAML2SPConf("client")).thenReturn(conf); + + Response response = service.getSAML2SPKeystore("client"); + + assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + assertEquals("keystore", response.getEntity()); + } + + @Test + void getSAML2SPKeystoreNotFound() { + SAML2IdPAuthModuleConf conf = new SAML2IdPAuthModuleConf(); + when(logic.readSAML2SPConf("client")).thenReturn(conf); + + Response response = service.getSAML2SPKeystore("client"); + + assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus()); + } + + @Test + void setSAML2SPKeystore() { + InputStream in = new ByteArrayInputStream(new byte[0]); + service.setSAML2SPKeystore("client", in); + verify(logic).setSAML2SPKeystore("client", in); + } + + @Test + void getSAML2SPMetadata() { + SAML2IdPAuthModuleConf conf = new SAML2IdPAuthModuleConf(); + conf.setServiceProviderMetadata("metadata"); + when(logic.readSAML2SPConf("client")).thenReturn(conf); + + Response response = service.getSAML2SPMetadata("client"); + + assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + assertEquals("metadata", response.getEntity()); + } + + @Test + void getSAML2SPMetadataNotFound() { + SAML2IdPAuthModuleConf conf = new SAML2IdPAuthModuleConf(); + when(logic.readSAML2SPConf("client")).thenReturn(conf); + + Response response = service.getSAML2SPMetadata("client"); + + assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus()); + } + + @Test + void setSAML2SPMetadata() { + InputStream in = new ByteArrayInputStream(new byte[0]); + service.setSAML2SPMetadata("client", in); + verify(logic).setSAML2SPMetadata("client", in); + } +} diff --git a/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/WebAuthnRegistrationServiceTest.java b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/WebAuthnRegistrationServiceTest.java new file mode 100644 index 00000000000..d335705b12b --- /dev/null +++ b/core/am/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/wa/WebAuthnRegistrationServiceTest.java @@ -0,0 +1,84 @@ +/* + * 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.core.rest.cxf.service.wa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import org.apache.syncope.common.lib.wa.WebAuthnAccount; +import org.apache.syncope.core.logic.wa.WebAuthnRegistrationLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class WebAuthnRegistrationServiceTest { + + private WebAuthnRegistrationLogic logic; + + private WebAuthnRegistrationServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(WebAuthnRegistrationLogic.class); + service = new WebAuthnRegistrationServiceImpl(logic); + } + + @Test + void list() { + WebAuthnAccount account = new WebAuthnAccount(); + when(logic.list()).thenReturn(List.of(account)); + assertEquals(List.of(account), service.list()); + } + + @Test + void read() { + WebAuthnAccount account = new WebAuthnAccount(); + when(logic.read("owner")).thenReturn(account); + assertSame(account, service.read("owner")); + } + + @Test + void deleteByOwner() { + service.delete("owner"); + verify(logic).delete("owner"); + } + + @Test + void deleteByOwnerAndCredential() { + service.delete("owner", "credential"); + verify(logic).delete("owner", "credential"); + } + + @Test + void create() { + WebAuthnAccount account = new WebAuthnAccount(); + service.create("owner", account); + verify(logic).create("owner", account); + } + + @Test + void update() { + WebAuthnAccount account = new WebAuthnAccount(); + service.update("owner", account); + verify(logic).update("owner", account); + } +} diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java index e248beae34b..02883413eae 100644 --- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java +++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java @@ -18,8 +18,6 @@ */ package org.apache.syncope.core.logic; -import static org.apache.syncope.core.logic.AbstractLogic.LOG; - import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Method; diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ConnectorLogicTest.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ConnectorLogicTest.java new file mode 100644 index 00000000000..3e3ea8fc14f --- /dev/null +++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ConnectorLogicTest.java @@ -0,0 +1,129 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.to.ConnInstanceTO; +import org.apache.syncope.common.lib.types.IdMEntitlement; +import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.entity.ConnInstance; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.provisioning.api.ConnIdBundleManager; +import org.apache.syncope.core.provisioning.api.ConnectorManager; +import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class ConnectorLogicTest { + + private ConnIdBundleManager connIdBundleManager; + + private ConnectorManager connectorManager; + + private ExternalResourceDAO resourceDAO; + + private ConnInstanceDAO connInstanceDAO; + + private ConnInstanceDataBinder binder; + + private ConnectorLogic logic; + + @BeforeEach + void setUp() { + connIdBundleManager = mock(ConnIdBundleManager.class); + connectorManager = mock(ConnectorManager.class); + resourceDAO = mock(ExternalResourceDAO.class); + connInstanceDAO = mock(ConnInstanceDAO.class); + binder = mock(ConnInstanceDataBinder.class); + + logic = new ConnectorLogic( + connIdBundleManager, connectorManager, resourceDAO, connInstanceDAO, binder); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void readMissing() { + when(connInstanceDAO.authFind("missing")).thenReturn(null); + + assertThrows(NotFoundException.class, () -> logic.read("missing", null)); + } + + @Test + void deleteMissing() { + when(connInstanceDAO.authFind("missing")).thenReturn(null); + + assertThrows(NotFoundException.class, () -> logic.delete("missing")); + } + + @Test + void deleteAssociatedResources() { + LogicTestAuth.authenticate("admin", "Master", + Map.of(IdMEntitlement.CONNECTOR_DELETE, Set.of(SyncopeConstants.ROOT_REALM))); + + ConnInstance connInstance = mock(ConnInstance.class); + ExternalResource resource = mock(ExternalResource.class); + when(resource.getKey()).thenReturn("resource-1"); + + var adminRealm = mock(Realm.class); + when(adminRealm.getFullPath()).thenReturn(SyncopeConstants.ROOT_REALM); + + when(connInstanceDAO.authFind("key")).thenReturn(connInstance); + when(connInstance.getAdminRealm()).thenReturn(adminRealm); + when(connInstance.getKey()).thenReturn("key"); + when(resourceDAO.findByConnInstance("key")).thenReturn(List.of(resource)); + + assertThrows(SyncopeClientException.class, () -> logic.delete("key")); + } + + @Test + void list() { + ConnInstance connInstance = mock(ConnInstance.class); + ConnInstanceTO to = new ConnInstanceTO(); + doReturn(List.of(connInstance)).when(connInstanceDAO).findAll(); + when(binder.getConnInstanceTO(connInstance)).thenReturn(to); + + assertEquals(List.of(to), logic.list(null)); + } + + @Test + void readByResourceMissing() { + when(resourceDAO.findById("missing")).thenReturn(Optional.empty()); + + assertThrows(NotFoundException.class, () -> logic.readByResource("missing", null)); + } +} diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/LogicTestAuth.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/LogicTestAuth.java new file mode 100644 index 00000000000..9e6fb4a9ac8 --- /dev/null +++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/LogicTestAuth.java @@ -0,0 +1,71 @@ +/* + * 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.core.logic; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.core.spring.security.SyncopeAuthenticationDetails; +import org.apache.syncope.core.spring.security.SyncopeGrantedAuthority; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; + +/** + * Small helper to set / clear the Spring Security context in unit tests, without bootstrapping a full application + * context. + */ +public final class LogicTestAuth { + + private static final String PASSWORD = "FAKE_PASSWORD"; + + public static void authenticate(final String username) { + authenticate(username, SyncopeConstants.MASTER_DOMAIN, Map.of()); + } + + public static void authenticate(final String username, final String domain) { + authenticate(username, domain, Map.of()); + } + + public static void authenticate( + final String username, + final String domain, + final Map> entitlements) { + + List authorities = entitlements.entrySet().stream(). + map(entry -> new SyncopeGrantedAuthority(entry.getKey(), entry.getValue().iterator().next())). + map(GrantedAuthority.class::cast). + toList(); + + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( + new org.springframework.security.core.userdetails.User(username, PASSWORD, authorities), + PASSWORD, + authorities); + authentication.setDetails(new SyncopeAuthenticationDetails(domain, null)); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + + public static void clear() { + SecurityContextHolder.clearContext(); + } + + private LogicTestAuth() { + } +} diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ReconciliationLogicTest.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ReconciliationLogicTest.java index 54e45b9c012..ba0d9aa5c63 100644 --- a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ReconciliationLogicTest.java +++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ReconciliationLogicTest.java @@ -47,7 +47,7 @@ import tools.jackson.dataformat.csv.CsvSchema; @Transactional -public class ReconciliationLogicTest extends AbstractTest { +class ReconciliationLogicTest extends AbstractTest { @Autowired private ReconciliationLogic reconciliationLogic; @@ -56,7 +56,7 @@ public class ReconciliationLogicTest extends AbstractTest { private UserLogicOp userLogic; @Test - public void pullFromCSV() { + void pullFromCSV() { CSVPullSpec spec = new CSVPullSpec.Builder(AnyTypeKind.USER.name(), "username").build(); InputStream csv = getClass().getResourceAsStream("/test1.csv"); @@ -85,7 +85,7 @@ public void pullFromCSV() { } @Test - public void pushToCSV() throws IOException { + void pushToCSV() throws IOException { Page search = AuthContextUtils.callAsAdmin(SyncopeConstants.MASTER_DOMAIN, () -> userLogic.search(null, PageRequest.of(0, 100), SyncopeConstants.ROOT_REALM, true, false)); assertNotNull(search); diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/RemediationLogicTest.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/RemediationLogicTest.java new file mode 100644 index 00000000000..e9c80d26c9d --- /dev/null +++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/RemediationLogicTest.java @@ -0,0 +1,129 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.request.AnyCR; +import org.apache.syncope.common.lib.request.UserCR; +import org.apache.syncope.common.lib.to.ProvisioningResult; +import org.apache.syncope.common.lib.to.RemediationTO; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.IdMEntitlement; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.RemediationDAO; +import org.apache.syncope.core.persistence.api.entity.Remediation; +import org.apache.syncope.core.provisioning.api.data.RemediationDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Pageable; + +class RemediationLogicTest { + + private UserLogicOp userLogic; + + private GroupLogicOp groupLogic; + + private AnyObjectLogicOp anyObjectLogic; + + private RemediationDataBinder binder; + + private RemediationDAO remediationDAO; + + private RemediationLogic logic; + + @BeforeEach + void setUp() { + userLogic = mock(UserLogicOp.class); + groupLogic = mock(GroupLogicOp.class); + anyObjectLogic = mock(AnyObjectLogicOp.class); + binder = mock(RemediationDataBinder.class); + remediationDAO = mock(RemediationDAO.class); + + logic = new RemediationLogic( + userLogic, groupLogic, anyObjectLogic, binder, remediationDAO); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void readMissing() { + when(remediationDAO.findById("missing")).thenReturn(Optional.empty()); + + assertThrows(NotFoundException.class, () -> logic.read("missing")); + } + + @Test + void deleteMissing() { + when(remediationDAO.findById("missing")).thenReturn(Optional.empty()); + + assertThrows(NotFoundException.class, () -> logic.delete("missing")); + } + + @SuppressWarnings("unchecked") + @Test + void remedyUser() { + LogicTestAuth.authenticate("admin", "Master", + Map.of(IdMEntitlement.REMEDIATION_REMEDY, Set.of(SyncopeConstants.ROOT_REALM))); + + Remediation remediation = mock(Remediation.class); + RemediationTO remediationTO = new RemediationTO(); + remediationTO.setKey("key"); + remediationTO.setAnyType(AnyTypeKind.USER.name()); + + doReturn(Optional.of(remediation)).when(remediationDAO).findById("key"); + when(binder.getRemediationTO(remediation)).thenReturn(remediationTO); + + AnyCR anyCR = new UserCR.Builder(SyncopeConstants.ROOT_REALM, "user").build(); + ProvisioningResult result = mock(ProvisioningResult.class); + + when(userLogic.create((UserCR) anyCR, false)).thenReturn(result); + + assertSame(result, logic.remedy("key", anyCR, false)); + + verify(remediationDAO).deleteById("key"); + } + + @Test + void list() { + when(remediationDAO.count(any(), any())).thenReturn(0L); + when(remediationDAO.findAll(any(), any(), any(Pageable.class))).thenReturn(List.of()); + + var result = logic.list(null, null, Pageable.unpaged()); + + assertEquals(0L, result.getTotalElements()); + } +} diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ResourceLogicTest.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ResourceLogicTest.java index 2c31400209c..2709b87a4d2 100644 --- a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ResourceLogicTest.java +++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ResourceLogicTest.java @@ -59,7 +59,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class ResourceLogicTest extends AbstractTest { +class ResourceLogicTest extends AbstractTest { @BeforeAll public static void setAuthContext() { @@ -125,7 +125,7 @@ private static ResourceTO buildResourceTO(final String resourceKey) { private AnyTypeDAO anyTypeDAO; @Test - public void updateChangePurpose() { + void updateChangePurpose() { ResourceTO ws1 = logic.read("ws-target-resource-1"); assertNotNull(ws1); @@ -141,7 +141,7 @@ public void updateChangePurpose() { } @Test - public void updateChangeOverrideCapabilities() { + void updateChangeOverrideCapabilities() { ResourceTO ldap = logic.read("resource-ldap"); assertNotNull(ldap); assertTrue(ldap.getCapabilitiesOverride().isEmpty()); @@ -157,7 +157,7 @@ public void updateChangeOverrideCapabilities() { } @Test - public void orgUnit() { + void orgUnit() { ResourceTO resourceTO = buildResourceTO("ws-orgunit"); assertNull(resourceTO.getOrgUnit()); assertNull(resourceTO.getPropagationPriority()); @@ -196,7 +196,7 @@ public void orgUnit() { } @Test - public void setLatestSyncToken() { + void setLatestSyncToken() { ConnectorManager connectorManager = mock(ConnectorManager.class); when(connectorManager.getConnector(any(ExternalResource.class))).thenAnswer(ic -> { Connector connector = mock(Connector.class); diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/init/IdMEntitlementLoaderTest.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/init/IdMEntitlementLoaderTest.java new file mode 100644 index 00000000000..657384acf09 --- /dev/null +++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/init/IdMEntitlementLoaderTest.java @@ -0,0 +1,41 @@ +/* + * 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.core.logic.init; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.syncope.common.lib.types.EntitlementsHolder; +import org.apache.syncope.common.lib.types.IdMEntitlement; +import org.junit.jupiter.api.Test; + +class IdMEntitlementLoaderTest { + + @Test + void order() { + assertEquals(900, new IdMEntitlementLoader().getOrder()); + } + + @Test + void load() { + new IdMEntitlementLoader().load(); + + assertTrue(EntitlementsHolder.getInstance().getValues().containsAll(IdMEntitlement.values())); + } +} diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/init/IdMImplementationTypeLoaderTest.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/init/IdMImplementationTypeLoaderTest.java new file mode 100644 index 00000000000..532343a7846 --- /dev/null +++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/init/IdMImplementationTypeLoaderTest.java @@ -0,0 +1,43 @@ +/* + * 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.core.logic.init; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.syncope.common.lib.types.IdMImplementationType; +import org.apache.syncope.common.lib.types.ImplementationTypesHolder; +import org.junit.jupiter.api.Test; +import org.springframework.core.Ordered; + +class IdMImplementationTypeLoaderTest { + + @Test + void order() { + assertEquals(Ordered.HIGHEST_PRECEDENCE, new IdMImplementationTypeLoader().getOrder()); + } + + @Test + void load() { + new IdMImplementationTypeLoader().load(); + + assertTrue(ImplementationTypesHolder.getInstance().getValues(). + entrySet().containsAll(IdMImplementationType.values().entrySet())); + } +} diff --git a/core/idm/rest-cxf/pom.xml b/core/idm/rest-cxf/pom.xml index 1298c6e86ff..5dd11224128 100644 --- a/core/idm/rest-cxf/pom.xml +++ b/core/idm/rest-cxf/pom.xml @@ -65,6 +65,23 @@ under the License. ${project.version} javadoc + + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + org.springframework + spring-test + test + diff --git a/core/idm/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/ConnectorServiceTest.java b/core/idm/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/ConnectorServiceTest.java new file mode 100644 index 00000000000..c21b6bbc113 --- /dev/null +++ b/core/idm/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/ConnectorServiceTest.java @@ -0,0 +1,62 @@ +/* + * 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.core.rest.cxf.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.UriInfo; +import java.util.List; +import org.apache.syncope.common.lib.to.ConnInstanceTO; +import org.apache.syncope.core.logic.ConnectorLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +class ConnectorServiceTest { + + private ConnectorLogic logic; + + private ConnectorServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(ConnectorLogic.class); + service = new ConnectorServiceImpl(logic); + ReflectionTestUtils.setField(service, "uriInfo", mock(UriInfo.class)); + } + + @Test + void list() { + when(logic.list(anyString())).thenReturn(List.of()); + assertEquals(List.of(), service.list("en")); + } + + @Test + void read() { + ConnInstanceTO to = new ConnInstanceTO(); + to.setKey("key"); + when(logic.read(eq("key"), anyString())).thenReturn(to); + assertSame(to, service.read("key", "en")); + } +} diff --git a/core/idm/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/ReconciliationServiceTest.java b/core/idm/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/ReconciliationServiceTest.java new file mode 100644 index 00000000000..e2b31865e5a --- /dev/null +++ b/core/idm/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/ReconciliationServiceTest.java @@ -0,0 +1,55 @@ +/* + * 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.core.rest.cxf.service; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.UriInfo; +import java.io.InputStream; +import java.util.List; +import org.apache.syncope.common.rest.api.beans.CSVPullSpec; +import org.apache.syncope.core.logic.ReconciliationLogic; +import org.apache.syncope.core.persistence.api.search.AnySearchCondVisitor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +class ReconciliationServiceTest { + + private ReconciliationLogic logic; + + private ReconciliationServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(ReconciliationLogic.class); + service = new ReconciliationServiceImpl(mock(AnySearchCondVisitor.class), logic); + ReflectionTestUtils.setField(service, "uriInfo", mock(UriInfo.class)); + } + + @Test + void pull() { + when(logic.pull(any(CSVPullSpec.class), any(InputStream.class))).thenReturn(List.of()); + service.pull(new CSVPullSpec(), mock(InputStream.class)); + verify(logic).pull(any(CSVPullSpec.class), any(InputStream.class)); + } +} diff --git a/core/idm/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/RemediationServiceTest.java b/core/idm/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/RemediationServiceTest.java new file mode 100644 index 00000000000..1a5a1f08a44 --- /dev/null +++ b/core/idm/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/RemediationServiceTest.java @@ -0,0 +1,62 @@ +/* + * 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.core.rest.cxf.service; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.UriInfo; +import org.apache.syncope.common.lib.to.RemediationTO; +import org.apache.syncope.core.logic.RemediationLogic; +import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +class RemediationServiceTest { + + private RemediationLogic logic; + + private RemediationServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(RemediationLogic.class); + service = new RemediationServiceImpl(logic, mock(AnyUtilsFactory.class)); + ReflectionTestUtils.setField(service, "uriInfo", mock(UriInfo.class)); + } + + @Test + void read() { + RemediationTO to = new RemediationTO(); + to.setKey("key"); + when(logic.read("key")).thenReturn(to); + assertSame(to, service.read("key")); + } + + @Test + void delete() { + doNothing().when(logic).delete("key"); + service.delete("key"); + verify(logic).delete("key"); + } +} diff --git a/core/idm/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/ResourceServiceTest.java b/core/idm/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/ResourceServiceTest.java new file mode 100644 index 00000000000..4171880dce9 --- /dev/null +++ b/core/idm/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/ResourceServiceTest.java @@ -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. + */ +package org.apache.syncope.core.rest.cxf.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.UriInfo; +import java.util.List; +import org.apache.syncope.common.lib.to.ResourceTO; +import org.apache.syncope.core.logic.ResourceLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +class ResourceServiceTest { + + private ResourceLogic logic; + + private ResourceServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(ResourceLogic.class); + service = new ResourceServiceImpl(logic); + ReflectionTestUtils.setField(service, "uriInfo", mock(UriInfo.class)); + } + + @Test + void list() { + when(logic.list()).thenReturn(List.of()); + assertEquals(List.of(), service.list()); + } + + @Test + void read() { + ResourceTO to = new ResourceTO(); + to.setKey("key"); + when(logic.read("key")).thenReturn(to); + assertSame(to, service.read("key")); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AbstractAnyLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AbstractAnyLogicTest.java new file mode 100644 index 00000000000..2b0cb313e48 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AbstractAnyLogicTest.java @@ -0,0 +1,246 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.request.UserCR; +import org.apache.syncope.common.lib.request.UserUR; +import org.apache.syncope.common.lib.to.PropagationStatus; +import org.apache.syncope.common.lib.to.ProvisioningResult; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.core.logic.api.LogicActions; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class AbstractAnyLogicTest { + + private static class TestLogic extends AbstractAnyLogic { + + TestLogic( + final RealmSearchDAO realmSearchDAO, + final AnyTypeDAO anyTypeDAO, + final TemplateUtils templateUtils) { + + super(realmSearchDAO, anyTypeDAO, templateUtils); + } + + BeforeResult beforeCreateUser(final UserCR input) { + return beforeCreate(input); + } + + BeforeResult beforeUpdateUser(final UserUR input, final String realmPath) { + return beforeUpdate(input, realmPath); + } + + BeforeResult beforeDeleteUser(final UserTO input) { + return beforeDelete(input); + } + + ProvisioningResult afterCreateUser( + final UserTO input, final List statuses, final List actions) { + + return afterCreate(input, statuses, actions); + } + + ProvisioningResult afterUpdateUser( + final UserTO input, final List statuses, final List actions) { + + return afterUpdate(input, statuses, actions); + } + + ProvisioningResult afterDeleteUser( + final UserTO input, final List statuses, final List actions) { + + return afterDelete(input, statuses, actions); + } + + List actions(final Realm realm) { + return getActions(realm); + } + + @Override + protected UserTO resolveReference(final java.lang.reflect.Method method, final Object... args) + throws UnresolvedReferenceException { + + throw new UnresolvedReferenceException(); + } + } + + private RealmSearchDAO realmSearchDAO; + + private AnyTypeDAO anyTypeDAO; + + private TemplateUtils templateUtils; + + private TestLogic logic; + + @BeforeEach + void setUp() { + realmSearchDAO = mock(RealmSearchDAO.class); + anyTypeDAO = mock(AnyTypeDAO.class); + templateUtils = mock(TemplateUtils.class); + logic = new TestLogic(realmSearchDAO, anyTypeDAO, templateUtils); + } + + @Test + void beforeCreateMissingRealm() { + UserCR userCR = new UserCR.Builder("/missing", "user").build(); + doReturn(Optional.empty()).when(realmSearchDAO).findByFullPath("/missing"); + + assertThrows(SyncopeClientException.class, () -> logic.beforeCreateUser(userCR)); + } + + @Test + void beforeCreateMissingAnyType() { + UserCR userCR = new UserCR.Builder(SyncopeConstants.ROOT_REALM, "user").build(); + Realm realm = mock(Realm.class); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(anyTypeDAO.getUser()).thenReturn(null); + + assertThrows(SyncopeClientException.class, () -> logic.beforeCreateUser(userCR)); + } + + @Test + void beforeCreateUser() { + UserCR userCR = new UserCR.Builder(SyncopeConstants.ROOT_REALM, "user").build(); + Realm realm = mock(Realm.class); + AnyType anyType = mock(AnyType.class); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(anyTypeDAO.getUser()).thenReturn(anyType); + doReturn(Optional.empty()).when(realm).getTemplate(anyType); + when(realm.getActions()).thenReturn(List.of()); + + var result = logic.beforeCreateUser(userCR); + + assertSame(userCR, result.key()); + assertEquals(List.of(), result.actions()); + } + + @Test + void beforeUpdateMissingRealm() { + doReturn(Optional.empty()).when(realmSearchDAO).findByFullPath("/missing"); + + assertThrows(SyncopeClientException.class, + () -> logic.beforeUpdateUser(new UserUR.Builder("key").build(), "/missing")); + } + + @Test + void beforeUpdate() { + UserUR userUR = new UserUR.Builder("key").build(); + Realm realm = mock(Realm.class); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(realm.getActions()).thenReturn(List.of()); + + var result = logic.beforeUpdateUser(userUR, SyncopeConstants.ROOT_REALM); + + assertSame(userUR, result.key()); + assertEquals(List.of(), result.actions()); + } + + @Test + void beforeDeleteMissingRealm() { + UserTO userTO = new UserTO(); + userTO.setKey("key"); + userTO.setRealm("/missing"); + doReturn(Optional.empty()).when(realmSearchDAO).findByFullPath("/missing"); + + assertThrows(SyncopeClientException.class, () -> logic.beforeDeleteUser(userTO)); + } + + @Test + void beforeDelete() { + UserTO userTO = new UserTO(); + userTO.setKey("key"); + userTO.setRealm(SyncopeConstants.ROOT_REALM); + Realm realm = mock(Realm.class); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(realm.getActions()).thenReturn(List.of()); + + var result = logic.beforeDeleteUser(userTO); + + assertSame(userTO, result.key()); + } + + @Test + void afterCreate() { + UserTO input = new UserTO(); + input.setKey("key"); + UserTO output = new UserTO(); + output.setKey("output"); + + LogicActions action = mock(LogicActions.class); + doReturn(output).when(action).afterCreate(any(), any()); + PropagationStatus status = mock(PropagationStatus.class); + + ProvisioningResult result = logic.afterCreateUser(input, List.of(status), List.of(action)); + + assertSame(output, result.getEntity()); + assertEquals(List.of(status), result.getPropagationStatuses()); + } + + @Test + void afterUpdate() { + UserTO input = new UserTO(); + input.setKey("key"); + + LogicActions action = mock(LogicActions.class); + doReturn(input).when(action).afterUpdate(input, List.of()); + + ProvisioningResult result = logic.afterUpdateUser(input, List.of(), List.of(action)); + + assertSame(input, result.getEntity()); + } + + @Test + void afterDelete() { + UserTO input = new UserTO(); + input.setKey("key"); + + LogicActions action = mock(LogicActions.class); + doReturn(input).when(action).afterDelete(input, List.of()); + + ProvisioningResult result = logic.afterDeleteUser(input, List.of(), List.of(action)); + + assertSame(input, result.getEntity()); + } + + @Test + void getActions() { + Realm realm = mock(Realm.class); + when(realm.getActions()).thenReturn(List.of()); + + assertEquals(List.of(), logic.actions(realm)); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AbstractExecutableLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AbstractExecutableLogicTest.java new file mode 100644 index 00000000000..240e59c4abc --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AbstractExecutableLogicTest.java @@ -0,0 +1,151 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.mock; + +import java.time.OffsetDateTime; +import java.util.List; +import org.apache.syncope.common.lib.to.AnyTypeClassTO; +import org.apache.syncope.common.lib.to.ExecTO; +import org.apache.syncope.common.lib.to.JobTO; +import org.apache.syncope.common.lib.types.JobAction; +import org.apache.syncope.common.rest.api.batch.BatchResponseItem; +import org.apache.syncope.common.rest.api.beans.ExecSpecs; +import org.apache.syncope.core.persistence.api.dao.JobStatusDAO; +import org.apache.syncope.core.provisioning.api.job.JobManager; +import org.apache.syncope.core.provisioning.java.job.SyncopeTaskScheduler; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +class AbstractExecutableLogicTest { + + private static class TestLogic extends AbstractExecutableLogic { + + TestLogic( + final JobManager jobManager, + final SyncopeTaskScheduler scheduler, + final JobStatusDAO jobStatusDAO) { + + super(jobManager, scheduler, jobStatusDAO); + } + + @Override + protected JobReference getReference(final String jobName) { + return new JobReference(org.apache.syncope.common.lib.types.JobType.TASK, jobName, jobName); + } + + @Override + public ExecTO execute(final ExecSpecs specs) { + return null; + } + + @Override + public Page listExecutions( + final String key, + final OffsetDateTime before, + final OffsetDateTime after, + final Pageable pageable) { + + return Page.empty(pageable); + } + + @Override + public List listRecentExecutions(final int max) { + return List.of(); + } + + @Override + public ExecTO deleteExecution(final String executionKey) { + return null; + } + + @Override + public List deleteExecutions( + final String key, + final OffsetDateTime before, + final OffsetDateTime after) { + + return List.of(); + } + + @Override + public JobTO getJob(final String key) { + return null; + } + + @Override + public List listJobs() { + return List.of(); + } + + @Override + public void actionJob(final String key, final JobAction action) { + } + + @Override + protected AnyTypeClassTO resolveReference( + final java.lang.reflect.Method method, final Object... args) throws UnresolvedReferenceException { + + throw new UnresolvedReferenceException(); + } + } + + private TestLogic logic; + + @BeforeEach + void setUp() { + logic = new TestLogic( + mock(JobManager.class), mock(SyncopeTaskScheduler.class), mock(JobStatusDAO.class)); + } + + @Test + void listExecutions() { + assertEquals(0, logic.listExecutions("key", null, null, Pageable.unpaged()).getTotalElements()); + } + + @Test + void listRecentExecutions() { + assertEquals(List.of(), logic.listRecentExecutions(10)); + } + + @Test + void deleteExecutions() { + assertEquals(List.of(), logic.deleteExecutions("key", null, null)); + } + + @Test + void listJobs() { + assertEquals(List.of(), logic.listJobs()); + } + + @Test + void contract() { + ExecSpecs specs = new ExecSpecs.Builder().key("key").build(); + + assertNull(logic.execute(specs)); + assertNull(logic.deleteExecution("key")); + assertNull(logic.getJob("key")); + logic.actionJob("key", JobAction.START); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AbstractJobLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AbstractJobLogicTest.java new file mode 100644 index 00000000000..f19397cd4c1 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AbstractJobLogicTest.java @@ -0,0 +1,174 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.to.AnyTypeClassTO; +import org.apache.syncope.common.lib.to.JobTO; +import org.apache.syncope.common.lib.types.JobAction; +import org.apache.syncope.common.lib.types.JobType; +import org.apache.syncope.core.persistence.api.dao.JobStatusDAO; +import org.apache.syncope.core.provisioning.api.job.JobManager; +import org.apache.syncope.core.provisioning.java.job.SyncopeTaskScheduler; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class AbstractJobLogicTest { + + private static class TestLogic extends AbstractJobLogic { + + TestLogic( + final JobManager jobManager, + final SyncopeTaskScheduler scheduler, + final JobStatusDAO jobStatusDAO) { + + super(jobManager, scheduler, jobStatusDAO); + } + + @Override + protected JobReference getReference(final String jobName) { + return "known".equals(jobName) ? new JobReference(JobType.TASK, "key", "desc") : null; + } + + Optional jobTO(final String jobName, final boolean includeCustom) { + return getJobTO(jobName, includeCustom); + } + + List listJobs(final boolean includeCustom) { + return doListJobs(includeCustom); + } + + void actionJob(final String jobName, final JobAction action) { + doActionJob(jobName, action); + } + + @Override + protected AnyTypeClassTO resolveReference(final java.lang.reflect.Method method, final Object... args) + throws UnresolvedReferenceException { + + throw new UnresolvedReferenceException(); + } + } + + private JobManager jobManager; + + private SyncopeTaskScheduler scheduler; + + private JobStatusDAO jobStatusDAO; + + private TestLogic logic; + + @BeforeEach + void setUp() { + jobManager = mock(JobManager.class); + scheduler = mock(SyncopeTaskScheduler.class); + jobStatusDAO = mock(JobStatusDAO.class); + logic = new TestLogic(jobManager, scheduler, jobStatusDAO); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void knownJob() { + OffsetDateTime nextTrigger = OffsetDateTime.now().plusHours(1); + when(scheduler.getNextTrigger("Master", "known")).thenReturn(Optional.of(nextTrigger)); + when(jobManager.isRunning("known")).thenReturn(true); + when(jobStatusDAO.get("known")).thenReturn("status"); + + JobTO job = logic.jobTO("known", false).orElseThrow(); + + assertEquals(JobType.TASK, job.getType()); + assertEquals("key", job.getRefKey()); + assertEquals("desc", job.getRefDesc()); + assertTrue(job.isScheduled()); + assertSame(nextTrigger, job.getStart()); + assertTrue(job.isRunning()); + assertEquals("status", job.getStatus()); + } + + @Test + void knownJobNotScheduled() { + when(scheduler.getNextTrigger("Master", "known")).thenReturn(Optional.empty()); + when(jobManager.isRunning("known")).thenReturn(false); + + JobTO job = logic.jobTO("known", false).orElseThrow(); + + assertFalse(job.isScheduled()); + assertFalse(job.isRunning()); + } + + @Test + void unknownJobWithoutCustom() { + assertTrue(logic.jobTO("unknown", false).isEmpty()); + } + + @Test + void unknownCustomJob() { + when(scheduler.getJobClass("Master", "custom")).thenReturn(Optional.of(String.class)); + when(scheduler.getNextTrigger("Master", "custom")).thenReturn(Optional.empty()); + when(jobManager.isRunning("custom")).thenReturn(false); + + JobTO job = logic.jobTO("custom", true).orElseThrow(); + + assertEquals(JobType.CUSTOM, job.getType()); + assertEquals("custom", job.getRefKey()); + assertEquals(String.class.getName(), job.getRefDesc()); + } + + @Test + void unknownCustomJobWithBuiltInClass() { + when(scheduler.getJobClass("Master", "builtIn")). + thenReturn(Optional.of(org.apache.syncope.core.provisioning.java.job.TaskJob.class)); + + assertTrue(logic.jobTO("builtIn", true).isEmpty()); + } + + @Test + void listJobs() { + when(scheduler.getJobNames("Master")).thenReturn(List.of("known", "unknown")); + when(scheduler.getNextTrigger("Master", "known")).thenReturn(Optional.empty()); + + assertEquals(List.of("key"), logic.listJobs(false).stream().map(JobTO::getRefKey).toList()); + } + + @Test + void actionJob() { + logic.actionJob("known", JobAction.START); + logic.actionJob("known", JobAction.STOP); + logic.actionJob("known", JobAction.DELETE); + + verify(scheduler).start("Master", "known"); + verify(scheduler).stop("Master", "known"); + verify(scheduler).delete("Master", "known"); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AbstractLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AbstractLogicTest.java new file mode 100644 index 00000000000..3367212db3e --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AbstractLogicTest.java @@ -0,0 +1,67 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.lang.reflect.Method; +import org.apache.syncope.common.lib.to.AnyTypeClassTO; +import org.junit.jupiter.api.Test; +import org.springframework.transaction.annotation.Transactional; + +class AbstractLogicTest { + + private static class TestLogic extends AbstractLogic { + + private AnyTypeClassTO resolved; + + @Override + protected AnyTypeClassTO resolveReference(final Method method, final Object... args) { + return resolved; + } + + @Transactional(readOnly = true) + public AnyTypeClassTO readOnly() { + return null; + } + + public AnyTypeClassTO readWrite() { + return null; + } + } + + @Test + void readOnlyIsAlwaysUnresolved() throws Exception { + TestLogic logic = new TestLogic(); + + assertThrows(UnresolvedReferenceException.class, () -> logic.resolveBeanReference( + TestLogic.class.getMethod("readOnly"))); + } + + @Test + void delegatesToResolveReference() throws Exception { + TestLogic logic = new TestLogic(); + logic.resolved = new AnyTypeClassTO(); + logic.resolved.setKey("key"); + + assertSame(logic.resolved, logic.resolveBeanReference( + TestLogic.class.getMethod("readWrite"))); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AbstractTransactionalLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AbstractTransactionalLogicTest.java new file mode 100644 index 00000000000..8b262ce9fe5 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AbstractTransactionalLogicTest.java @@ -0,0 +1,46 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.reflect.Method; +import org.apache.syncope.common.lib.to.AnyTypeClassTO; +import org.junit.jupiter.api.Test; +import org.springframework.transaction.annotation.Transactional; + +class AbstractTransactionalLogicTest { + + private static class TestLogic extends AbstractTransactionalLogic { + + @Override + protected AnyTypeClassTO resolveReference(final Method method, final Object... args) { + return new AnyTypeClassTO(); + } + } + + @Test + void transactionalAnnotated() { + Transactional transactional = TestLogic.class.getAnnotation(Transactional.class); + + assertTrue(transactional != null); + assertArrayEquals(new Class[] { Throwable.class }, transactional.rollbackFor()); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AbstractUserLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AbstractUserLogicTest.java new file mode 100644 index 00000000000..26a179f89e5 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AbstractUserLogicTest.java @@ -0,0 +1,303 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.keymaster.client.api.ConfParamOps; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.request.StatusR; +import org.apache.syncope.common.lib.request.UserCR; +import org.apache.syncope.common.lib.request.UserUR; +import org.apache.syncope.common.lib.to.ProvisioningResult; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.StatusRType; +import org.apache.syncope.core.persistence.api.EncryptorManager; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.provisioning.api.ProvisioningManager; +import org.apache.syncope.core.provisioning.api.UserProvisioningManager; +import org.apache.syncope.core.provisioning.api.data.UserDataBinder; +import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class AbstractUserLogicTest { + + private static class TestLogic extends AbstractUserLogic { + + TestLogic( + final RealmSearchDAO realmSearchDAO, + final AnyTypeDAO anyTypeDAO, + final TemplateUtils templateUtils, + final UserDAO userDAO, + final UserDataBinder binder, + final UserProvisioningManager provisioningManager, + final EncryptorManager encryptorManager, + final ConfParamOps confParamOps) { + + super(realmSearchDAO, anyTypeDAO, templateUtils, userDAO, binder, provisioningManager, + encryptorManager, confParamOps); + } + + ProvisioningResult create(final UserCR userCR) { + return doCreate(userCR, false, false); + } + + ProvisioningResult update(final UserUR userUR) { + return doUpdate(userUR, false, false); + } + + ProvisioningResult status(final StatusR statusR) { + return doStatus(statusR, false); + } + + ProvisioningResult delete(final UserTO userTO) { + return doDelete(userTO, false, false); + } + + public UserTO requestPasswordReset(final String username) { + return null; + } + + public UserTO confirmPasswordReset(final String token) { + return null; + } + + public UserTO otherOperation(final Object value) { + return null; + } + } + + private RealmSearchDAO realmSearchDAO; + + private AnyTypeDAO anyTypeDAO; + + private UserDAO userDAO; + + private UserDataBinder binder; + + private UserProvisioningManager provisioningManager; + + private TestLogic logic; + + @BeforeEach + void setUp() { + realmSearchDAO = mock(RealmSearchDAO.class); + anyTypeDAO = mock(AnyTypeDAO.class); + userDAO = mock(UserDAO.class); + binder = mock(UserDataBinder.class); + provisioningManager = mock(UserProvisioningManager.class); + + logic = new TestLogic( + realmSearchDAO, + anyTypeDAO, + mock(TemplateUtils.class), + userDAO, + binder, + provisioningManager, + mock(EncryptorManager.class), + mock(ConfParamOps.class)); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void createMissingRealm() { + UserCR userCR = new UserCR.Builder("/missing", "user").build(); + doReturn(Optional.empty()).when(realmSearchDAO).findByFullPath("/missing"); + + assertThrows(SyncopeClientException.class, () -> logic.create(userCR)); + } + + @Test + void create() { + UserCR userCR = new UserCR.Builder(SyncopeConstants.ROOT_REALM, "user").build(); + Realm realm = mock(Realm.class); + AnyType anyType = mock(AnyType.class); + UserTO userTO = new UserTO(); + userTO.setKey("key"); + + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(anyTypeDAO.getUser()).thenReturn(anyType); + doReturn(Optional.empty()).when(realm).getTemplate(anyType); + when(realm.getActions()).thenReturn(List.of()); + when(provisioningManager.create(userCR, false, "unauthenticated", "REST")). + thenReturn(new ProvisioningManager.ProvisioningResult<>("key", List.of())); + when(binder.getUserTO("key")).thenReturn(userTO); + + assertSame(userTO, logic.create(userCR).getEntity()); + } + + @Test + void update() { + UserUR userUR = new UserUR.Builder("key").build(); + UserTO userTO = new UserTO(); + userTO.setKey("key"); + userTO.setRealm(SyncopeConstants.ROOT_REALM); + Realm realm = mock(Realm.class); + + when(binder.getUserTO("key")).thenReturn(userTO); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(realm.getActions()).thenReturn(List.of()); + when(provisioningManager.update(userUR, false, "unauthenticated", "REST")). + thenReturn(new ProvisioningManager.ProvisioningResult<>(userUR, List.of())); + + assertSame(userTO, logic.update(userUR).getEntity()); + } + + @Test + void deleteManager() { + UserTO userTO = new UserTO(); + userTO.setKey("key"); + userTO.setRealm(SyncopeConstants.ROOT_REALM); + Realm realm = mock(Realm.class); + + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(realm.getActions()).thenReturn(List.of()); + when(userDAO.isManager("key")).thenReturn(true); + + assertThrows(SyncopeClientException.class, () -> logic.delete(userTO)); + } + + @Test + void delete() { + UserTO userTO = new UserTO(); + userTO.setKey("key"); + userTO.setRealm(SyncopeConstants.ROOT_REALM); + Realm realm = mock(Realm.class); + + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(realm.getActions()).thenReturn(List.of()); + when(userDAO.isManager("key")).thenReturn(false); + when(provisioningManager.delete("key", false, "unauthenticated", "REST")).thenReturn(List.of()); + when(userDAO.existsById("key")).thenReturn(false); + + assertEquals("key", logic.delete(userTO).getEntity().getKey()); + } + + @Test + void statusSuspend() { + assertStatus(StatusRType.SUSPEND); + } + + @Test + void statusReactivate() { + assertStatus(StatusRType.REACTIVATE); + } + + @Test + void statusActivate() { + assertStatus(StatusRType.ACTIVATE); + } + + private void assertStatus(final StatusRType type) { + StatusR statusR = new StatusR.Builder("key", type).build(); + UserTO userTO = new UserTO(); + userTO.setKey("key"); + ProvisioningManager.ProvisioningResult result = + new ProvisioningManager.ProvisioningResult<>("key", List.of()); + + switch (type) { + case SUSPEND -> + when(provisioningManager.suspend(statusR, false, "unauthenticated", "REST")).thenReturn(result); + + case REACTIVATE -> + when(provisioningManager.reactivate(statusR, false, "unauthenticated", "REST")).thenReturn(result); + + case ACTIVATE -> + when(provisioningManager.activate(statusR, false, "unauthenticated", "REST")).thenReturn(result); + + default -> { + } + } + when(binder.getUserTO("key")).thenReturn(userTO); + + assertSame(userTO, logic.status(statusR).getEntity()); + } + + @Test + void resolvePasswordReset() throws Exception { + UserTO userTO = new UserTO(); + userTO.setKey("key"); + doReturn(Optional.of("key")).when(userDAO).findKey("user"); + when(binder.getUserTO("key")).thenReturn(userTO); + + Method method = TestLogic.class.getMethod("requestPasswordReset", String.class); + + assertSame(userTO, logic.resolveReference(method, "user")); + } + + @Test + void resolveByUuid() throws Exception { + String uuid = "11111111-1111-1111-1111-111111111111"; + UserTO userTO = new UserTO(); + userTO.setKey("key"); + when(binder.getUserTO(uuid)).thenReturn(userTO); + + Method method = TestLogic.class.getMethod("otherOperation", Object.class); + + assertSame(userTO, logic.resolveReference(method, uuid)); + } + + @Test + void unresolvedReference() throws Exception { + Method method = TestLogic.class.getMethod("otherOperation", Object.class); + + assertThrows(UnresolvedReferenceException.class, () -> logic.resolveReference(method, 42)); + } + + @Test + void securityChecksForUpdate() { + UserUR userUR = new UserUR.Builder("key").build(); + UserTO userTO = new UserTO(); + userTO.setKey("key"); + userTO.setRealm(SyncopeConstants.ROOT_REALM); + Realm realm = mock(Realm.class); + + when(binder.getUserTO("key")).thenReturn(userTO); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(realm.getActions()).thenReturn(List.of()); + when(provisioningManager.update(any(UserUR.class), eq(false), eq("unauthenticated"), eq("REST"))). + thenReturn(new ProvisioningManager.ProvisioningResult<>(userUR, List.of())); + + logic.update(userUR); + + verify(userDAO).securityChecks(any(), eq("key"), eq(SyncopeConstants.ROOT_REALM), any()); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AccessTokenLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AccessTokenLogicTest.java new file mode 100644 index 00000000000..09f547f8f4a --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AccessTokenLogicTest.java @@ -0,0 +1,172 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.OffsetDateTime; +import java.util.Optional; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.AccessTokenTO; +import org.apache.syncope.core.persistence.api.EncryptorManager; +import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.entity.AccessToken; +import org.apache.syncope.core.provisioning.api.data.AccessTokenDataBinder; +import org.apache.syncope.core.spring.security.SecurityProperties; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +class AccessTokenLogicTest { + + private SecurityProperties securityProperties; + + private EncryptorManager encryptorManager; + + private AccessTokenDataBinder binder; + + private AccessTokenDAO accessTokenDAO; + + private AccessTokenLogic logic; + + @BeforeEach + void setUp() { + securityProperties = new SecurityProperties(); + securityProperties.setAnonymousUser("anonymous"); + + encryptorManager = mock(EncryptorManager.class); + binder = mock(AccessTokenDataBinder.class); + accessTokenDAO = mock(AccessTokenDAO.class); + + logic = new AccessTokenLogic(securityProperties, encryptorManager, binder, accessTokenDAO); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void loginAsAnonymous() { + LogicTestAuth.authenticate("anonymous"); + + assertThrows(SyncopeClientException.class, () -> logic.login()); + } + + @Test + void login() { + LogicTestAuth.authenticate("user"); + + AccessTokenDataBinder.AccessTokenInfo info = + new AccessTokenDataBinder.AccessTokenInfo("jwt", OffsetDateTime.now()); + when(binder.create(any(), eq("user"), anyMap(), any(), anyBoolean())).thenReturn(info); + + assertSame(info, logic.login()); + } + + @Test + void refreshMissing() { + LogicTestAuth.authenticate("user"); + when(accessTokenDAO.findByOwner("user")).thenReturn(Optional.empty()); + + assertThrows(NotFoundException.class, () -> logic.refresh()); + } + + @Test + void refresh() { + LogicTestAuth.authenticate("user"); + AccessToken accessToken = mock(AccessToken.class); + AccessTokenDataBinder.AccessTokenInfo info = + new AccessTokenDataBinder.AccessTokenInfo("jwt", OffsetDateTime.now()); + + when(accessTokenDAO.findByOwner("user")).thenReturn(Optional.of(accessToken)); + when(binder.update(eq(accessToken), any())).thenReturn(info); + + assertSame(info, logic.refresh()); + } + + @Test + void logoutMissing() { + LogicTestAuth.authenticate("user"); + when(accessTokenDAO.findByOwner("user")).thenReturn(Optional.empty()); + + assertThrows(NotFoundException.class, () -> logic.logout()); + } + + @Test + void logout() { + LogicTestAuth.authenticate("user"); + AccessToken accessToken = mock(AccessToken.class); + when(accessToken.getKey()).thenReturn("key"); + when(accessTokenDAO.findByOwner("user")).thenReturn(Optional.of(accessToken)); + + logic.logout(); + + verify(accessTokenDAO).deleteById("key"); + } + + @Test + void list() { + Pageable pageable = Pageable.unpaged(); + when(accessTokenDAO.findAll(pageable)).thenReturn(Page.empty(pageable)); + + assertEquals(0, logic.list(pageable).getTotalElements()); + } + + @Test + void listMapped() { + Pageable pageable = Pageable.unpaged(); + AccessToken accessToken = mock(AccessToken.class); + AccessTokenTO to = new AccessTokenTO(); + to.setKey("key"); + + doReturn(new org.springframework.data.domain.PageImpl<>( + java.util.List.of(accessToken), pageable, 1)).when(accessTokenDAO).findAll(pageable); + when(binder.getAccessTokenTO(accessToken)).thenReturn(to); + + assertEquals(1, logic.list(pageable).getTotalElements()); + assertSame(to, logic.list(pageable).getContent().getFirst()); + } + + @Test + void delete() { + logic.delete("key"); + + verify(accessTokenDAO).deleteById("key"); + } + + @Test + void resolveReference() { + assertThrows(UnresolvedReferenceException.class, () -> logic.resolveBeanReference( + AccessTokenLogic.class.getMethod("delete", String.class), "key")); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AnyCRUDLogicOpTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AnyCRUDLogicOpTest.java new file mode 100644 index 00000000000..c0830344cbf --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AnyCRUDLogicOpTest.java @@ -0,0 +1,66 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Collection; +import java.util.List; +import org.apache.syncope.common.lib.request.AnyCR; +import org.apache.syncope.common.lib.request.AnyUR; +import org.apache.syncope.common.lib.to.AnyTO; +import org.apache.syncope.common.lib.to.ProvisioningResult; +import org.apache.syncope.core.persistence.api.dao.search.SearchCond; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +class AnyCRUDLogicOpTest { + + @Test + void contract() throws Exception { + assertTrue(AnyCRUDLogicOp.class.isInterface()); + + assertEquals(AnyTO.class, + AnyCRUDLogicOp.class.getMethod("read", String.class).getReturnType()); + assertEquals(Page.class, AnyCRUDLogicOp.class.getMethod( + "search", SearchCond.class, Pageable.class, String.class, boolean.class, boolean.class). + getReturnType()); + assertEquals(ProvisioningResult.class, AnyCRUDLogicOp.class.getMethod( + "create", AnyCR.class, boolean.class).getReturnType()); + assertEquals(ProvisioningResult.class, AnyCRUDLogicOp.class.getMethod( + "update", AnyUR.class, boolean.class).getReturnType()); + assertNotNull(AnyCRUDLogicOp.class.getMethod( + "delete", String.class, boolean.class)); + assertNotNull(AnyCRUDLogicOp.class.getMethod( + "unlink", String.class, Collection.class)); + assertNotNull(AnyCRUDLogicOp.class.getMethod( + "link", String.class, Collection.class)); + assertNotNull(AnyCRUDLogicOp.class.getMethod( + "unassign", String.class, Collection.class, boolean.class)); + assertNotNull(AnyCRUDLogicOp.class.getMethod( + "assign", String.class, Collection.class, boolean.class, String.class, boolean.class)); + assertNotNull(AnyCRUDLogicOp.class.getMethod( + "deprovision", String.class, List.class, boolean.class)); + assertNotNull(AnyCRUDLogicOp.class.getMethod( + "provision", String.class, List.class, boolean.class, String.class, boolean.class)); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AnyObjectLogicOpTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AnyObjectLogicOpTest.java new file mode 100644 index 00000000000..31afd97bae4 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AnyObjectLogicOpTest.java @@ -0,0 +1,36 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.syncope.common.lib.to.AnyObjectTO; +import org.junit.jupiter.api.Test; + +class AnyObjectLogicOpTest { + + @Test + void contract() throws Exception { + assertTrue(AnyObjectLogicOp.class.isInterface()); + assertTrue(AnyCRUDLogicOp.class.isAssignableFrom(AnyObjectLogicOp.class)); + assertEquals(AnyObjectTO.class, + AnyObjectLogicOp.class.getMethod("read", String.class, String.class).getReturnType()); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AnyObjectLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AnyObjectLogicTest.java new file mode 100644 index 00000000000..38062c95a8d --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AnyObjectLogicTest.java @@ -0,0 +1,204 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.request.AnyObjectCR; +import org.apache.syncope.common.lib.request.AnyObjectUR; +import org.apache.syncope.common.lib.to.AnyObjectTO; +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; +import org.apache.syncope.core.persistence.api.dao.search.SearchCond; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager; +import org.apache.syncope.core.provisioning.api.ProvisioningManager; +import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder; +import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Pageable; + +class AnyObjectLogicTest { + + private RealmSearchDAO realmSearchDAO; + + private AnyTypeDAO anyTypeDAO; + + private AnyObjectDAO anyObjectDAO; + + private AnySearchDAO searchDAO; + + private AnyObjectDataBinder binder; + + private AnyObjectProvisioningManager provisioningManager; + + private AnyObjectLogic logic; + + @BeforeEach + void setUp() { + realmSearchDAO = mock(RealmSearchDAO.class); + anyTypeDAO = mock(AnyTypeDAO.class); + anyObjectDAO = mock(AnyObjectDAO.class); + searchDAO = mock(AnySearchDAO.class); + binder = mock(AnyObjectDataBinder.class); + provisioningManager = mock(AnyObjectProvisioningManager.class); + + logic = new AnyObjectLogic( + realmSearchDAO, + anyTypeDAO, + mock(TemplateUtils.class), + anyObjectDAO, + searchDAO, + binder, + provisioningManager); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void read() { + AnyObjectTO to = new AnyObjectTO(); + to.setKey("key"); + when(binder.getAnyObjectTO("key")).thenReturn(to); + + assertSame(to, logic.read("key")); + } + + @Test + void readByTypeAndNameMissing() { + doReturn(Optional.empty()).when(anyObjectDAO).findKey("type", "missing"); + + assertThrows(NotFoundException.class, () -> logic.read("type", "missing")); + } + + @Test + void readByTypeAndName() { + AnyObjectTO to = new AnyObjectTO(); + to.setKey("key"); + + doReturn(Optional.of("key")).when(anyObjectDAO).findKey("type", "name"); + when(binder.getAnyObjectTO("key")).thenReturn(to); + + assertSame(to, logic.read("type", "name")); + } + + @Test + void searchWithoutAnyType() { + SearchCond searchCond = mock(SearchCond.class); + + assertThrows(UnsupportedOperationException.class, + () -> logic.search(searchCond, Pageable.unpaged(), SyncopeConstants.ROOT_REALM, true, false)); + } + + @Test + void searchMissingRealm() { + SearchCond searchCond = mock(SearchCond.class); + when(searchCond.hasAnyTypeCond()).thenReturn("type"); + doReturn(Optional.empty()).when(realmSearchDAO).findByFullPath("/missing"); + + assertThrows(NotFoundException.class, + () -> logic.search(searchCond, Pageable.unpaged(), "/missing", true, false)); + } + + @Test + void search() { + SearchCond searchCond = mock(SearchCond.class); + when(searchCond.hasAnyTypeCond()).thenReturn("type"); + Realm realm = mock(Realm.class); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(searchDAO.count(eq(realm), eq(true), any(), eq(searchCond), any())).thenReturn(0L); + when(searchDAO.search(eq(realm), eq(true), any(), eq(searchCond), any(Pageable.class), any())). + thenReturn(List.of()); + + assertEquals(0, logic.search(searchCond, Pageable.unpaged(), + SyncopeConstants.ROOT_REALM, true, false).getTotalElements()); + } + + @Test + void create() { + AnyObjectCR createReq = new AnyObjectCR.Builder(SyncopeConstants.ROOT_REALM, "type", "name").build(); + Realm realm = mock(Realm.class); + AnyType anyType = mock(AnyType.class); + AnyObjectTO to = new AnyObjectTO(); + to.setKey("key"); + + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + doReturn(Optional.of(anyType)).when(anyTypeDAO).findById("type"); + when(provisioningManager.create(createReq, false, "unauthenticated", "REST")). + thenReturn(new ProvisioningManager.ProvisioningResult<>("key", List.of())); + when(binder.getAnyObjectTO("key")).thenReturn(to); + + assertSame(to, logic.create(createReq, false).getEntity()); + } + + @Test + void delete() { + AnyObjectTO to = new AnyObjectTO(); + to.setKey("key"); + to.setRealm(SyncopeConstants.ROOT_REALM); + to.setType("type"); + Realm realm = mock(Realm.class); + + when(binder.getAnyObjectTO("key")).thenReturn(to); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(provisioningManager.delete("key", false, "unauthenticated", "REST")).thenReturn(List.of()); + when(anyObjectDAO.existsById("key")).thenReturn(true); + + assertSame(to, logic.delete("key", false).getEntity()); + } + + @Test + void unlink() { + AnyObject anyObject = mock(AnyObject.class); + AnyType anyType = mock(AnyType.class); + Realm realm = mock(Realm.class); + AnyObjectTO to = new AnyObjectTO(); + to.setKey("key"); + + when(anyObject.getType()).thenReturn(anyType); + when(anyType.getKey()).thenReturn("type"); + when(anyObject.getRealm()).thenReturn(realm); + when(realm.getFullPath()).thenReturn(SyncopeConstants.ROOT_REALM); + when(anyObjectDAO.authFind("key")).thenReturn(anyObject); + when(provisioningManager.unlink(any(AnyObjectUR.class), eq("unauthenticated"), eq("REST"))).thenReturn("key"); + when(binder.getAnyObjectTO("key")).thenReturn(to); + + assertSame(to, logic.unlink("key", List.of("resource"))); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AnyTypeClassLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AnyTypeClassLogicTest.java new file mode 100644 index 00000000000..bdc022b8d3a --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AnyTypeClassLogicTest.java @@ -0,0 +1,160 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.AnyTypeClassTO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; +import org.apache.syncope.core.persistence.api.dao.DuplicateException; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.provisioning.api.data.AnyTypeClassDataBinder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class AnyTypeClassLogicTest { + + private AnyTypeClassDataBinder binder; + + private AnyTypeClassDAO anyTypeClassDAO; + + private AnyTypeClassLogic logic; + + @BeforeEach + void setUp() { + binder = mock(AnyTypeClassDataBinder.class); + anyTypeClassDAO = mock(AnyTypeClassDAO.class); + logic = new AnyTypeClassLogic(binder, anyTypeClassDAO); + } + + @Test + void readMissing() { + doReturn(Optional.empty()).when(anyTypeClassDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.read("missing")); + } + + @Test + void readAndList() { + AnyTypeClass anyTypeClass = mock(AnyTypeClass.class); + AnyTypeClassTO to = new AnyTypeClassTO(); + to.setKey("key"); + + doReturn(Optional.of(anyTypeClass)).when(anyTypeClassDAO).findById("key"); + doReturn(List.of(anyTypeClass)).when(anyTypeClassDAO).findAll(); + when(binder.getAnyTypeClassTO(anyTypeClass)).thenReturn(to); + + assertSame(to, logic.read("key")); + assertEquals(List.of(to), logic.list()); + } + + @Test + void createWithoutKey() { + assertThrows(SyncopeClientException.class, () -> logic.create(new AnyTypeClassTO())); + } + + @Test + void createDuplicate() { + AnyTypeClassTO to = new AnyTypeClassTO(); + to.setKey("key"); + when(anyTypeClassDAO.existsById("key")).thenReturn(true); + + assertThrows(DuplicateException.class, () -> logic.create(to)); + } + + @Test + void create() { + AnyTypeClassTO to = new AnyTypeClassTO(); + to.setKey("key"); + + AnyTypeClass anyTypeClass = mock(AnyTypeClass.class); + when(binder.create(to)).thenReturn(anyTypeClass); + when(anyTypeClassDAO.save(anyTypeClass)).thenReturn(anyTypeClass); + when(binder.getAnyTypeClassTO(anyTypeClass)).thenReturn(to); + + assertSame(to, logic.create(to)); + } + + @Test + void updateMissing() { + AnyTypeClassTO to = new AnyTypeClassTO(); + to.setKey("missing"); + doReturn(Optional.empty()).when(anyTypeClassDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.update(to)); + } + + @Test + void update() { + AnyTypeClassTO to = new AnyTypeClassTO(); + to.setKey("key"); + + AnyTypeClass anyTypeClass = mock(AnyTypeClass.class); + AnyTypeClass updated = mock(AnyTypeClass.class); + doReturn(Optional.of(anyTypeClass)).when(anyTypeClassDAO).findById("key"); + when(binder.update(anyTypeClass, to)).thenReturn(updated); + when(anyTypeClassDAO.save(updated)).thenReturn(updated); + when(binder.getAnyTypeClassTO(updated)).thenReturn(to); + + assertSame(to, logic.update(to)); + } + + @Test + void deleteMissing() { + doReturn(Optional.empty()).when(anyTypeClassDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.delete("missing")); + } + + @Test + void delete() { + AnyTypeClass anyTypeClass = mock(AnyTypeClass.class); + AnyTypeClassTO to = new AnyTypeClassTO(); + to.setKey("key"); + + doReturn(Optional.of(anyTypeClass)).when(anyTypeClassDAO).findById("key"); + when(binder.getAnyTypeClassTO(anyTypeClass)).thenReturn(to); + + assertSame(to, logic.delete("key")); + verify(anyTypeClassDAO).deleteById("key"); + } + + @Test + void resolveBeanReference() throws Exception { + AnyTypeClass anyTypeClass = mock(AnyTypeClass.class); + AnyTypeClassTO to = new AnyTypeClassTO(); + to.setKey("key"); + + doReturn(Optional.of(anyTypeClass)).when(anyTypeClassDAO).findById("key"); + when(binder.getAnyTypeClassTO(anyTypeClass)).thenReturn(to); + + assertSame(to, logic.resolveBeanReference( + AnyTypeClassLogic.class.getMethod("create", AnyTypeClassTO.class), "key")); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AnyTypeLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AnyTypeLogicTest.java new file mode 100644 index 00000000000..d5feff282c8 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AnyTypeLogicTest.java @@ -0,0 +1,171 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.AnyTypeTO; +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.dao.DuplicateException; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.provisioning.api.data.AnyTypeDataBinder; +import org.apache.syncope.core.provisioning.api.event.EntityLifecycleEvent; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.context.ApplicationEventPublisher; + +class AnyTypeLogicTest { + + private AnyTypeDataBinder binder; + + private AnyTypeDAO anyTypeDAO; + + private AnyObjectDAO anyObjectDAO; + + private ApplicationEventPublisher publisher; + + private AnyTypeLogic logic; + + @BeforeEach + void setUp() { + binder = mock(AnyTypeDataBinder.class); + anyTypeDAO = mock(AnyTypeDAO.class); + anyObjectDAO = mock(AnyObjectDAO.class); + publisher = mock(ApplicationEventPublisher.class); + logic = new AnyTypeLogic(binder, anyTypeDAO, anyObjectDAO, publisher); + } + + @Test + void readMissing() { + doReturn(Optional.empty()).when(anyTypeDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.read("missing")); + } + + @Test + void readAndList() { + AnyType anyType = mock(AnyType.class); + AnyTypeTO to = new AnyTypeTO(); + to.setKey("key"); + + doReturn(Optional.of(anyType)).when(anyTypeDAO).findById("key"); + doReturn(List.of(anyType)).when(anyTypeDAO).findAll(); + when(binder.getAnyTypeTO(anyType)).thenReturn(to); + + assertSame(to, logic.read("key")); + assertEquals(List.of(to), logic.list()); + } + + @Test + void createWithoutKey() { + assertThrows(SyncopeClientException.class, () -> logic.create(new AnyTypeTO())); + } + + @Test + void createDuplicate() { + AnyTypeTO to = new AnyTypeTO(); + to.setKey("key"); + when(anyTypeDAO.existsById("key")).thenReturn(true); + + assertThrows(DuplicateException.class, () -> logic.create(to)); + } + + @Test + void create() { + AnyTypeTO to = new AnyTypeTO(); + to.setKey("key"); + + AnyType anyType = mock(AnyType.class); + when(binder.create(to)).thenReturn(anyType); + when(anyTypeDAO.save(anyType)).thenReturn(anyType); + when(binder.getAnyTypeTO(anyType)).thenReturn(to); + + assertSame(to, logic.create(to)); + verify(publisher).publishEvent(any(EntityLifecycleEvent.class)); + } + + @Test + void updateMissing() { + AnyTypeTO to = new AnyTypeTO(); + to.setKey("missing"); + doReturn(Optional.empty()).when(anyTypeDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.update(to)); + } + + @Test + void update() { + AnyTypeTO to = new AnyTypeTO(); + to.setKey("key"); + + AnyType anyType = mock(AnyType.class); + doReturn(Optional.of(anyType)).when(anyTypeDAO).findById("key"); + when(anyTypeDAO.save(anyType)).thenReturn(anyType); + + logic.update(to); + + verify(binder).update(anyType, to); + verify(publisher).publishEvent(any(EntityLifecycleEvent.class)); + } + + @Test + void deleteMissing() { + doReturn(Optional.empty()).when(anyTypeDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.delete("missing")); + } + + @Test + void deleteWithAnyObjects() { + AnyType anyType = mock(AnyType.class); + when(anyType.getKey()).thenReturn("key"); + doReturn(Optional.of(anyType)).when(anyTypeDAO).findById("key"); + when(anyObjectDAO.countByType()).thenReturn(Map.of("key", 2L)); + + assertThrows(SyncopeClientException.class, () -> logic.delete("key")); + } + + @Test + void delete() { + AnyType anyType = mock(AnyType.class); + when(anyType.getKey()).thenReturn("key"); + AnyTypeTO to = new AnyTypeTO(); + to.setKey("key"); + + doReturn(Optional.of(anyType)).when(anyTypeDAO).findById("key"); + when(anyObjectDAO.countByType()).thenReturn(Map.of()); + when(binder.delete(anyType)).thenReturn(to); + + assertSame(to, logic.delete("key")); + verify(publisher).publishEvent(any(EntityLifecycleEvent.class)); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AuditLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AuditLogicTest.java new file mode 100644 index 00000000000..a070532a001 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/AuditLogicTest.java @@ -0,0 +1,195 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.to.AuditConfTO; +import org.apache.syncope.common.lib.to.AuditEventTO; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.common.lib.types.OpEvent; +import org.apache.syncope.core.persistence.api.dao.AuditConfDAO; +import org.apache.syncope.core.persistence.api.dao.AuditEventDAO; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.entity.AuditConf; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.provisioning.api.AuditManager; +import org.apache.syncope.core.provisioning.api.ImplementationLookup; +import org.apache.syncope.core.provisioning.api.data.AuditDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Pageable; + +class AuditLogicTest { + + private AuditConfDAO auditConfDAO; + + private AuditEventDAO auditEventDAO; + + private EntityFactory entityFactory; + + private AuditDataBinder binder; + + private AuditManager auditManager; + + private AuditLogic logic; + + @BeforeEach + void setUp() { + auditConfDAO = mock(AuditConfDAO.class); + auditEventDAO = mock(AuditEventDAO.class); + entityFactory = mock(EntityFactory.class); + binder = mock(AuditDataBinder.class); + auditManager = mock(AuditManager.class); + + logic = new AuditLogic( + auditConfDAO, + auditEventDAO, + mock(ExternalResourceDAO.class), + entityFactory, + mock(ImplementationLookup.class), + binder, + auditManager); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void confs() { + AuditConf auditConf = mock(AuditConf.class); + AuditConfTO to = new AuditConfTO(); + to.setKey("key"); + + doReturn(List.of(auditConf)).when(auditConfDAO).findAll(); + when(binder.getAuditConfTO(auditConf)).thenReturn(to); + + assertEquals(List.of(to), logic.confs()); + } + + @Test + void getConfMissing() { + doReturn(Optional.empty()).when(auditConfDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.getConf("missing")); + } + + @Test + void getConf() { + AuditConf auditConf = mock(AuditConf.class); + AuditConfTO to = new AuditConfTO(); + to.setKey("key"); + + doReturn(Optional.of(auditConf)).when(auditConfDAO).findById("key"); + when(binder.getAuditConfTO(auditConf)).thenReturn(to); + + assertSame(to, logic.getConf("key")); + } + + @Test + void setConf() { + AuditConfTO to = new AuditConfTO(); + to.setKey("key"); + to.setActive(true); + + AuditConf auditConf = mock(AuditConf.class); + doReturn(Optional.empty()).when(auditConfDAO).findById("key"); + when(entityFactory.newEntity(AuditConf.class)).thenReturn(auditConf); + + logic.setConf(to); + + verify(auditConf).setKey("key"); + verify(auditConf).setActive(true); + verify(auditConfDAO).save(auditConf); + } + + @Test + void deleteConfMissing() { + doReturn(Optional.empty()).when(auditConfDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.deleteConf("missing")); + } + + @Test + void deleteConf() { + AuditConf auditConf = mock(AuditConf.class); + doReturn(Optional.of(auditConf)).when(auditConfDAO).findById("key"); + + logic.deleteConf("key"); + + verify(auditConfDAO).delete(auditConf); + } + + @Test + void search() { + Pageable pageable = Pageable.unpaged(); + when(auditEventDAO.count( + null, Set.of(), Set.of(), null, null, null, null, null, null, null)).thenReturn(0L); + when(auditEventDAO.search( + null, Set.of(), Set.of(), null, null, null, null, null, null, null, pageable)). + thenReturn(List.of()); + + assertEquals(0, logic.search( + null, Set.of(), Set.of(), null, null, null, null, null, null, null, pageable). + getTotalElements()); + } + + @Test + void createUnauthorized() { + AuditEventTO to = new AuditEventTO(); + OpEvent opEvent = mock(OpEvent.class); + when(opEvent.getType()).thenReturn(OpEvent.CategoryType.LOGIC); + to.setOpEvent(opEvent); + + assertThrows(SyncopeClientException.class, () -> logic.create(to)); + } + + @Test + void create() { + LogicTestAuth.authenticate("admin", "Master", + Map.of(IdRepoEntitlement.AUDIT_SET, Set.of(SyncopeConstants.ROOT_REALM))); + + AuditEventTO to = new AuditEventTO(); + OpEvent opEvent = mock(OpEvent.class); + when(opEvent.getType()).thenReturn(OpEvent.CategoryType.LOGIC); + to.setOpEvent(opEvent); + + logic.create(to); + + verify(auditManager).audit( + any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/CommandLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/CommandLogicTest.java new file mode 100644 index 00000000000..23706ae1955 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/CommandLogicTest.java @@ -0,0 +1,82 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import jakarta.validation.Validator; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.command.CommandTO; +import org.apache.syncope.common.lib.types.IdRepoImplementationType; +import org.apache.syncope.core.persistence.api.dao.ImplementationDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +class CommandLogicTest { + + private ImplementationDAO implementationDAO; + + private Validator validator; + + private CommandLogic logic; + + @BeforeEach + void setUp() { + implementationDAO = mock(ImplementationDAO.class); + validator = mock(Validator.class); + logic = new CommandLogic(implementationDAO, validator); + } + + @Test + void search() { + Pageable pageable = PageRequest.of(0, 10); + doReturn(List.of()).when(implementationDAO). + findByTypeAndKeyword(IdRepoImplementationType.COMMAND, null); + + assertEquals(0, logic.search(null, pageable).getTotalElements()); + } + + @Test + void readMissing() { + doReturn(Optional.empty()).when(implementationDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.read("missing")); + } + + @Test + void runMissing() { + CommandTO command = new CommandTO.Builder("missing").build(); + doReturn(Optional.empty()).when(implementationDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.run(command)); + } + + @Test + void resolveReference() { + assertThrows(UnsupportedOperationException.class, () -> logic.resolveBeanReference( + CommandLogic.class.getMethod("run", CommandTO.class), new CommandTO.Builder("key").build())); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/DelegationLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/DelegationLogicTest.java new file mode 100644 index 00000000000..8f7b17bda0b --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/DelegationLogicTest.java @@ -0,0 +1,190 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.to.DelegationTO; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.persistence.api.dao.DelegationDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.Delegation; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.data.DelegationDataBinder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class DelegationLogicTest { + + private static final String DELEGATING = "11111111-1111-1111-1111-111111111111"; + + private DelegationDataBinder binder; + + private DelegationDAO delegationDAO; + + private UserDAO userDAO; + + private DelegationLogic logic; + + @BeforeEach + void setUp() { + binder = mock(DelegationDataBinder.class); + delegationDAO = mock(DelegationDAO.class); + userDAO = mock(UserDAO.class); + logic = new DelegationLogic(binder, delegationDAO, userDAO); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void readMissing() { + doReturn(Optional.empty()).when(delegationDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.read("missing")); + } + + @Test + void read() { + LogicTestAuth.authenticate("admin", "Master", + Map.of(IdRepoEntitlement.DELEGATION_READ, Set.of(SyncopeConstants.ROOT_REALM))); + + Delegation delegation = mock(Delegation.class); + User delegating = mock(User.class); + DelegationTO to = new DelegationTO(); + to.setKey("key"); + + when(delegation.getDelegating()).thenReturn(delegating); + when(delegating.getKey()).thenReturn(DELEGATING); + doReturn(Optional.of(delegation)).when(delegationDAO).findById("key"); + when(binder.getDelegationTO(delegation)).thenReturn(to); + + assertSame(to, logic.read("key")); + } + + @Test + void listWithoutListEntitlement() { + doReturn(Optional.empty()).when(userDAO).findByUsername("unauthenticated"); + + assertThrows(NotFoundException.class, () -> logic.list()); + } + + @Test + void listWithListEntitlement() { + LogicTestAuth.authenticate("admin", "Master", + Map.of(IdRepoEntitlement.DELEGATION_LIST, Set.of(SyncopeConstants.ROOT_REALM))); + + Delegation delegation = mock(Delegation.class); + DelegationTO to = new DelegationTO(); + to.setKey("key"); + + doReturn(List.of(delegation)).when(delegationDAO).findAll(); + when(binder.getDelegationTO(delegation)).thenReturn(to); + + assertEquals(List.of(to), logic.list()); + } + + @Test + void create() { + LogicTestAuth.authenticate("admin", "Master", + Map.of(IdRepoEntitlement.DELEGATION_CREATE, Set.of(SyncopeConstants.ROOT_REALM))); + + DelegationTO to = new DelegationTO(); + to.setKey("key"); + to.setDelegating(DELEGATING); + + Delegation delegation = mock(Delegation.class); + when(binder.create(to)).thenReturn(delegation); + when(delegationDAO.save(delegation)).thenReturn(delegation); + when(binder.getDelegationTO(delegation)).thenReturn(to); + + assertSame(to, logic.create(to)); + } + + @Test + void updateMissing() { + DelegationTO to = new DelegationTO(); + to.setKey("missing"); + doReturn(Optional.empty()).when(delegationDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.update(to)); + } + + @Test + void update() { + LogicTestAuth.authenticate("admin", "Master", + Map.of(IdRepoEntitlement.DELEGATION_UPDATE, Set.of(SyncopeConstants.ROOT_REALM))); + + DelegationTO to = new DelegationTO(); + to.setKey("key"); + to.setDelegating(DELEGATING); + + Delegation delegation = mock(Delegation.class); + User delegating = mock(User.class); + when(delegating.getKey()).thenReturn(DELEGATING); + when(delegation.getDelegating()).thenReturn(delegating); + doReturn(Optional.of(delegation)).when(delegationDAO).findById("key"); + when(binder.update(delegation, to)).thenReturn(delegation); + when(delegationDAO.save(delegation)).thenReturn(delegation); + when(binder.getDelegationTO(delegation)).thenReturn(to); + + assertSame(to, logic.update(to)); + } + + @Test + void deleteMissing() { + doReturn(Optional.empty()).when(delegationDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.delete("missing")); + } + + @Test + void delete() { + LogicTestAuth.authenticate("admin", "Master", + Map.of(IdRepoEntitlement.DELEGATION_DELETE, Set.of(SyncopeConstants.ROOT_REALM))); + + Delegation delegation = mock(Delegation.class); + User delegating = mock(User.class); + DelegationTO to = new DelegationTO(); + to.setKey("key"); + + when(delegating.getKey()).thenReturn(DELEGATING); + when(delegation.getDelegating()).thenReturn(delegating); + doReturn(Optional.of(delegation)).when(delegationDAO).findById("key"); + when(binder.getDelegationTO(delegation)).thenReturn(to); + + assertSame(to, logic.delete("key")); + verify(delegationDAO).deleteById("key"); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/FIQLQueryLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/FIQLQueryLogicTest.java new file mode 100644 index 00000000000..ce8a54cd6fa --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/FIQLQueryLogicTest.java @@ -0,0 +1,205 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.to.FIQLQueryTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.dao.FIQLQueryDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.FIQLQuery; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.data.FIQLQueryDataBinder; +import org.apache.syncope.core.spring.security.DelegatedAdministrationException; +import org.apache.syncope.core.spring.security.SecurityProperties; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class FIQLQueryLogicTest { + + private FIQLQueryDataBinder binder; + + private FIQLQueryDAO fiqlQueryDAO; + + private UserDAO userDAO; + + private SecurityProperties securityProperties; + + private FIQLQueryLogic logic; + + @BeforeEach + void setUp() { + binder = mock(FIQLQueryDataBinder.class); + fiqlQueryDAO = mock(FIQLQueryDAO.class); + userDAO = mock(UserDAO.class); + + securityProperties = new SecurityProperties(); + securityProperties.setAdminUser("admin"); + securityProperties.setAnonymousUser("anonymous"); + + logic = new FIQLQueryLogic(binder, fiqlQueryDAO, userDAO, securityProperties); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void adminIsSkipped() { + LogicTestAuth.authenticate("admin"); + + assertThrows(NotFoundException.class, () -> logic.read("key")); + assertEquals(List.of(), logic.list(AnyTypeKind.USER.name())); + assertThrows(NotFoundException.class, () -> logic.create(new FIQLQueryTO())); + } + + @Test + void anonymousIsSkipped() { + LogicTestAuth.authenticate("anonymous"); + + assertThrows(NotFoundException.class, () -> logic.read("key")); + assertEquals(List.of(), logic.list(AnyTypeKind.USER.name())); + } + + @Test + void readMissing() { + LogicTestAuth.authenticate("user"); + doReturn(Optional.empty()).when(fiqlQueryDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.read("missing")); + } + + @Test + void readWithDifferentOwner() { + LogicTestAuth.authenticate("user"); + + FIQLQuery fiqlQuery = mock(FIQLQuery.class); + User owner = mock(User.class); + when(owner.getUsername()).thenReturn("other"); + when(fiqlQuery.getOwner()).thenReturn(owner); + doReturn(Optional.of(fiqlQuery)).when(fiqlQueryDAO).findById("key"); + + assertThrows(DelegatedAdministrationException.class, () -> logic.read("key")); + } + + @Test + void read() { + LogicTestAuth.authenticate("user"); + + FIQLQuery fiqlQuery = mock(FIQLQuery.class); + User owner = mock(User.class); + FIQLQueryTO to = new FIQLQueryTO(); + to.setKey("key"); + + when(owner.getUsername()).thenReturn("user"); + when(fiqlQuery.getOwner()).thenReturn(owner); + doReturn(Optional.of(fiqlQuery)).when(fiqlQueryDAO).findById("key"); + when(binder.getFIQLQueryTO(fiqlQuery)).thenReturn(to); + + assertSame(to, logic.read("key")); + } + + @Test + void listMissingUser() { + LogicTestAuth.authenticate("user"); + doReturn(Optional.empty()).when(userDAO).findByUsername("user"); + + assertThrows(NotFoundException.class, () -> logic.list(AnyTypeKind.USER.name())); + } + + @Test + void list() { + LogicTestAuth.authenticate("user"); + + User user = mock(User.class); + FIQLQuery fiqlQuery = mock(FIQLQuery.class); + FIQLQueryTO to = new FIQLQueryTO(); + to.setKey("key"); + + doReturn(Optional.of(user)).when(userDAO).findByUsername("user"); + doReturn(List.of(fiqlQuery)).when(fiqlQueryDAO).findByOwner(user, AnyTypeKind.USER.name()); + when(binder.getFIQLQueryTO(fiqlQuery)).thenReturn(to); + + assertEquals(List.of(to), logic.list(AnyTypeKind.USER.name())); + } + + @Test + void create() { + LogicTestAuth.authenticate("user"); + + FIQLQueryTO to = new FIQLQueryTO(); + to.setKey("key"); + FIQLQuery fiqlQuery = mock(FIQLQuery.class); + + when(binder.create(to)).thenReturn(fiqlQuery); + when(fiqlQueryDAO.save(fiqlQuery)).thenReturn(fiqlQuery); + when(binder.getFIQLQueryTO(fiqlQuery)).thenReturn(to); + + assertSame(to, logic.create(to)); + } + + @Test + void updateMissing() { + LogicTestAuth.authenticate("user"); + + FIQLQueryTO to = new FIQLQueryTO(); + to.setKey("missing"); + doReturn(Optional.empty()).when(fiqlQueryDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.update(to)); + } + + @Test + void deleteMissing() { + LogicTestAuth.authenticate("user"); + doReturn(Optional.empty()).when(fiqlQueryDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.delete("missing")); + } + + @Test + void delete() { + LogicTestAuth.authenticate("user"); + + FIQLQuery fiqlQuery = mock(FIQLQuery.class); + User owner = mock(User.class); + FIQLQueryTO to = new FIQLQueryTO(); + to.setKey("key"); + + when(owner.getUsername()).thenReturn("user"); + when(fiqlQuery.getOwner()).thenReturn(owner); + doReturn(Optional.of(fiqlQuery)).when(fiqlQueryDAO).findById("key"); + when(binder.getFIQLQueryTO(fiqlQuery)).thenReturn(to); + + assertSame(to, logic.delete("key")); + verify(fiqlQueryDAO).deleteById("key"); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/GroupLogicOpTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/GroupLogicOpTest.java new file mode 100644 index 00000000000..25dc72f90ab --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/GroupLogicOpTest.java @@ -0,0 +1,39 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.apache.syncope.common.lib.to.ExecTO; +import org.apache.syncope.common.lib.types.ProvisionAction; +import org.junit.jupiter.api.Test; + +class GroupLogicOpTest { + + @Test + void contract() throws Exception { + assertTrue(GroupLogicOp.class.isInterface()); + assertTrue(AnyCRUDLogicOp.class.isAssignableFrom(GroupLogicOp.class)); + assertEquals(List.class, GroupLogicOp.class.getMethod("own").getReturnType()); + assertEquals(ExecTO.class, GroupLogicOp.class.getMethod( + "provisionMembers", String.class, ProvisionAction.class).getReturnType()); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/GroupLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/GroupLogicTest.java new file mode 100644 index 00000000000..a250b837d3a --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/GroupLogicTest.java @@ -0,0 +1,261 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.request.GroupCR; +import org.apache.syncope.common.lib.request.GroupUR; +import org.apache.syncope.common.lib.to.GroupTO; +import org.apache.syncope.common.lib.to.PropagationStatus; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.ImplementationDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; +import org.apache.syncope.core.persistence.api.dao.TaskDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.dao.search.SearchCond; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.GroupProvisioningManager; +import org.apache.syncope.core.provisioning.api.ProvisioningManager; +import org.apache.syncope.core.provisioning.api.data.GroupDataBinder; +import org.apache.syncope.core.provisioning.api.data.TaskDataBinder; +import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils; +import org.apache.syncope.core.provisioning.api.job.JobManager; +import org.apache.syncope.core.provisioning.java.job.SyncopeTaskScheduler; +import org.apache.syncope.core.spring.security.SecurityProperties; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Pageable; + +class GroupLogicTest { + + private RealmSearchDAO realmSearchDAO; + + private AnyTypeDAO anyTypeDAO; + + private UserDAO userDAO; + + private GroupDAO groupDAO; + + private SecurityProperties securityProperties; + + private AnySearchDAO searchDAO; + + private GroupDataBinder binder; + + private GroupProvisioningManager provisioningManager; + + private GroupLogic logic; + + @BeforeEach + void setUp() { + realmSearchDAO = mock(RealmSearchDAO.class); + anyTypeDAO = mock(AnyTypeDAO.class); + userDAO = mock(UserDAO.class); + groupDAO = mock(GroupDAO.class); + searchDAO = mock(AnySearchDAO.class); + binder = mock(GroupDataBinder.class); + provisioningManager = mock(GroupProvisioningManager.class); + + securityProperties = new SecurityProperties(); + securityProperties.setAdminUser("admin"); + + logic = new GroupLogic( + realmSearchDAO, + anyTypeDAO, + mock(TemplateUtils.class), + userDAO, + groupDAO, + securityProperties, + searchDAO, + mock(ImplementationDAO.class), + mock(TaskDAO.class), + binder, + provisioningManager, + mock(TaskDataBinder.class), + mock(JobManager.class), + mock(SyncopeTaskScheduler.class), + mock(EntityFactory.class)); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void read() { + GroupTO to = new GroupTO(); + to.setKey("key"); + when(binder.getGroupTO("key")).thenReturn(to); + + assertSame(to, logic.read("key")); + } + + @Test + void ownForAdmin() { + LogicTestAuth.authenticate("admin"); + + assertEquals(List.of(), logic.own()); + } + + @Test + void ownWithMissingUser() { + LogicTestAuth.authenticate("user"); + doReturn(Optional.empty()).when(userDAO).findByUsername("user"); + + assertThrows(NotFoundException.class, () -> logic.own()); + } + + @Test + void own() { + LogicTestAuth.authenticate("user"); + + User user = mock(User.class); + Group group = mock(Group.class); + GroupTO to = new GroupTO(); + to.setKey("key"); + + doReturn(Optional.of(user)).when(userDAO).findByUsername("user"); + when(userDAO.findAllGroups(user)).thenReturn(List.of(group)); + when(binder.getGroupTO(group, true)).thenReturn(to); + + assertEquals(List.of(to), logic.own()); + } + + @Test + void searchMissingRealm() { + doReturn(Optional.empty()).when(realmSearchDAO).findByFullPath("/missing"); + + assertThrows(NotFoundException.class, + () -> logic.search(null, Pageable.unpaged(), "/missing", true, false)); + } + + @Test + void search() { + Realm realm = mock(Realm.class); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(searchDAO.getAllMatchingCond()).thenReturn(mock(SearchCond.class)); + when(searchDAO.count(eq(realm), eq(true), any(), any(SearchCond.class), any())).thenReturn(0L); + when(searchDAO.search( + eq(realm), eq(true), any(), any(SearchCond.class), any(Pageable.class), any())). + thenReturn(List.of()); + + assertEquals(0, logic.search(null, Pageable.unpaged(), + SyncopeConstants.ROOT_REALM, true, false).getTotalElements()); + } + + @Test + void create() { + GroupCR createReq = new GroupCR.Builder(SyncopeConstants.ROOT_REALM, "group").build(); + Realm realm = mock(Realm.class); + GroupTO to = new GroupTO(); + to.setKey("key"); + + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(anyTypeDAO.getGroup()).thenReturn(mock(org.apache.syncope.core.persistence.api.entity.AnyType.class)); + when(provisioningManager.create(createReq, false, "unauthenticated", "REST")). + thenReturn(new ProvisioningManager.ProvisioningResult<>("key", List.of())); + when(binder.getGroupTO("key")).thenReturn(to); + + assertSame(to, logic.create(createReq, false).getEntity()); + } + + @Test + void update() { + GroupUR updateReq = new GroupUR.Builder("key").build(); + Realm realm = mock(Realm.class); + GroupTO groupTO = new GroupTO(); + groupTO.setKey("key"); + groupTO.setRealm(SyncopeConstants.ROOT_REALM); + + when(binder.getGroupTO("key")).thenReturn(groupTO); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(provisioningManager.update(updateReq, Set.of(), false, "unauthenticated", "REST")). + thenReturn(new ProvisioningManager.ProvisioningResult<>(updateReq, List.of())); + + assertSame(groupTO, logic.update(updateReq, false).getEntity()); + } + + @Test + void deleteManager() { + GroupTO groupTO = new GroupTO(); + groupTO.setKey("key"); + groupTO.setRealm(SyncopeConstants.ROOT_REALM); + Realm realm = mock(Realm.class); + + when(binder.getGroupTO("key")).thenReturn(groupTO); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(groupDAO.isManager("key")).thenReturn(true); + + assertThrows(SyncopeClientException.class, () -> logic.delete("key", false)); + } + + @Test + void unlink() { + GroupTO groupTO = new GroupTO(); + groupTO.setKey("key"); + groupTO.setRealm(SyncopeConstants.ROOT_REALM); + GroupTO unlinked = new GroupTO(); + unlinked.setKey("key"); + Realm realm = mock(Realm.class); + + when(binder.getGroupTO("key")).thenReturn(groupTO, unlinked); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(provisioningManager.unlink(any(GroupUR.class), eq("unauthenticated"), eq("REST"))).thenReturn("key"); + + assertSame(unlinked, logic.unlink("key", List.of("resource"))); + } + + @Test + void deprovision() { + GroupTO groupTO = new GroupTO(); + groupTO.setKey("key"); + groupTO.setRealm(SyncopeConstants.ROOT_REALM); + Realm realm = mock(Realm.class); + PropagationStatus status = mock(PropagationStatus.class); + when(status.getResource()).thenReturn("resource"); + + when(binder.getGroupTO("key")).thenReturn(groupTO); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(provisioningManager.deprovision("key", List.of("resource"), false, "unauthenticated")). + thenReturn(List.of(status)); + + assertEquals(List.of(status), logic.deprovision("key", List.of("resource"), false).getPropagationStatuses()); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/ImplementationLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/ImplementationLogicTest.java new file mode 100644 index 00000000000..f230117ecd3 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/ImplementationLogicTest.java @@ -0,0 +1,262 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.ImplementationTO; +import org.apache.syncope.common.lib.types.IdRepoImplementationType; +import org.apache.syncope.common.lib.types.ImplementationTypesHolder; +import org.apache.syncope.core.persistence.api.dao.DuplicateException; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.ImplementationDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.NotificationDAO; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.PolicyDAO; +import org.apache.syncope.core.persistence.api.dao.RealmDAO; +import org.apache.syncope.core.persistence.api.dao.ReportDAO; +import org.apache.syncope.core.persistence.api.dao.TaskDAO; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.provisioning.api.data.ImplementationDataBinder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class ImplementationLogicTest { + + private ImplementationDataBinder binder; + + private ImplementationDAO implementationDAO; + + private ReportDAO reportDAO; + + private PolicyDAO policyDAO; + + private ExternalResourceDAO resourceDAO; + + private TaskDAO taskDAO; + + private RealmDAO realmDAO; + + private PlainSchemaDAO plainSchemaDAO; + + private NotificationDAO notificationDAO; + + private ImplementationLogic logic; + + @BeforeEach + void setUp() { + ImplementationTypesHolder.getInstance().putAll(IdRepoImplementationType.values()); + + binder = mock(ImplementationDataBinder.class); + implementationDAO = mock(ImplementationDAO.class); + reportDAO = mock(ReportDAO.class); + policyDAO = mock(PolicyDAO.class); + resourceDAO = mock(ExternalResourceDAO.class); + taskDAO = mock(TaskDAO.class); + realmDAO = mock(RealmDAO.class); + plainSchemaDAO = mock(PlainSchemaDAO.class); + notificationDAO = mock(NotificationDAO.class); + + logic = new ImplementationLogic( + binder, + implementationDAO, + reportDAO, + policyDAO, + resourceDAO, + taskDAO, + realmDAO, + plainSchemaDAO, + notificationDAO); + } + + @Test + void listWithInvalidType() { + assertThrows(SyncopeClientException.class, () -> logic.list("unknown")); + } + + @Test + void list() { + Implementation implementation = mock(Implementation.class); + ImplementationTO to = new ImplementationTO(); + to.setKey("key"); + + doReturn(List.of(implementation)).when(implementationDAO).findByType(IdRepoImplementationType.COMMAND); + when(binder.getImplementationTO(implementation)).thenReturn(to); + + assertEquals(List.of(to), logic.list(IdRepoImplementationType.COMMAND)); + } + + @Test + void readMissing() { + doReturn(Optional.empty()).when(implementationDAO).findById("missing"); + + assertThrows(NotFoundException.class, + () -> logic.read(IdRepoImplementationType.COMMAND, "missing")); + } + + @Test + void readWithWrongType() { + Implementation implementation = mock(Implementation.class); + doReturn(Optional.of(implementation)).when(implementationDAO).findById("key"); + when(implementation.getType()).thenReturn(IdRepoImplementationType.TASKJOB_DELEGATE); + + assertThrows(SyncopeClientException.class, + () -> logic.read(IdRepoImplementationType.COMMAND, "key")); + } + + @Test + void read() { + Implementation implementation = mock(Implementation.class); + ImplementationTO to = new ImplementationTO(); + to.setKey("key"); + + doReturn(Optional.of(implementation)).when(implementationDAO).findById("key"); + when(implementation.getType()).thenReturn(IdRepoImplementationType.COMMAND); + when(binder.getImplementationTO(implementation)).thenReturn(to); + + assertSame(to, logic.read(IdRepoImplementationType.COMMAND, "key")); + } + + @Test + void createWithoutKey() { + assertThrows(SyncopeClientException.class, () -> logic.create(new ImplementationTO())); + } + + @Test + void createDuplicate() { + ImplementationTO to = new ImplementationTO(); + to.setKey("key"); + to.setType(IdRepoImplementationType.COMMAND); + when(implementationDAO.existsById("key")).thenReturn(true); + + assertThrows(DuplicateException.class, () -> logic.create(to)); + } + + @Test + void createWithInvalidType() { + ImplementationTO to = new ImplementationTO(); + to.setKey("key"); + to.setType("unknown"); + + assertThrows(SyncopeClientException.class, () -> logic.create(to)); + } + + @Test + void create() { + ImplementationTO to = new ImplementationTO(); + to.setKey("key"); + to.setType(IdRepoImplementationType.COMMAND); + + Implementation implementation = mock(Implementation.class); + when(binder.create(to)).thenReturn(implementation); + when(implementationDAO.save(implementation)).thenReturn(implementation); + when(binder.getImplementationTO(implementation)).thenReturn(to); + + assertSame(to, logic.create(to)); + } + + @Test + void updateMissing() { + ImplementationTO to = new ImplementationTO(); + to.setKey("missing"); + to.setType(IdRepoImplementationType.COMMAND); + doReturn(Optional.empty()).when(implementationDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.update(to)); + } + + @Test + void update() { + ImplementationTO to = new ImplementationTO(); + to.setKey("key"); + to.setType(IdRepoImplementationType.COMMAND); + + Implementation implementation = mock(Implementation.class); + doReturn(Optional.of(implementation)).when(implementationDAO).findById("key"); + when(implementationDAO.save(implementation)).thenReturn(implementation); + when(binder.getImplementationTO(implementation)).thenReturn(to); + + assertSame(to, logic.update(to)); + verify(binder).update(implementation, to); + } + + @Test + void deleteMissing() { + doReturn(Optional.empty()).when(implementationDAO).findById("missing"); + + assertThrows(NotFoundException.class, + () -> logic.delete(IdRepoImplementationType.COMMAND, "missing")); + } + + @Test + void deleteWithWrongType() { + Implementation implementation = mock(Implementation.class); + doReturn(Optional.of(implementation)).when(implementationDAO).findById("key"); + when(implementation.getType()).thenReturn(IdRepoImplementationType.TASKJOB_DELEGATE); + + assertThrows(SyncopeClientException.class, + () -> logic.delete(IdRepoImplementationType.COMMAND, "key")); + } + + @Test + void deleteInUse() { + Implementation implementation = mock(Implementation.class); + doReturn(Optional.of(implementation)).when(implementationDAO).findById("key"); + when(implementation.getType()).thenReturn(IdRepoImplementationType.ACCOUNT_RULE); + doReturn(List.of(mock(org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy.class))). + when(policyDAO).findByAccountRule(implementation); + + assertThrows(SyncopeClientException.class, + () -> logic.delete(IdRepoImplementationType.ACCOUNT_RULE, "key")); + } + + @Test + void delete() { + Implementation implementation = mock(Implementation.class); + doReturn(Optional.of(implementation)).when(implementationDAO).findById("key"); + when(implementation.getType()).thenReturn("custom"); + + logic.delete("custom", "key"); + + verify(implementationDAO).deleteById("key"); + } + + @Test + void resolveBeanReference() throws Exception { + Implementation implementation = mock(Implementation.class); + ImplementationTO to = new ImplementationTO(); + to.setKey("key"); + + doReturn(Optional.of(implementation)).when(implementationDAO).findById("key"); + when(binder.getImplementationTO(implementation)).thenReturn(to); + + assertSame(to, logic.resolveBeanReference( + ImplementationLogic.class.getMethod("create", ImplementationTO.class), "key")); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/LogicInvocationHandlerTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/LogicInvocationHandlerTest.java new file mode 100644 index 00000000000..ec609f0b4f7 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/LogicInvocationHandlerTest.java @@ -0,0 +1,137 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Method; +import org.apache.syncope.common.lib.to.AnyTypeClassTO; +import org.apache.syncope.core.provisioning.api.AuditManager; +import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent; +import org.apache.syncope.core.provisioning.api.notification.NotificationManager; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.reflect.MethodSignature; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class LogicInvocationHandlerTest { + + private NotificationManager notificationManager; + + private AuditManager auditManager; + + private LogicInvocationHandler handler; + + private ProceedingJoinPoint joinPoint; + + private AbstractLogic target; + + private Method method; + + @SuppressWarnings("unchecked") + @BeforeEach + void setUp() throws Exception { + notificationManager = mock(NotificationManager.class); + auditManager = mock(AuditManager.class); + handler = new LogicInvocationHandler(notificationManager, auditManager); + + method = Object.class.getMethod("toString"); + + MethodSignature signature = mock(MethodSignature.class); + when(signature.getMethod()).thenReturn(method); + when(signature.getName()).thenReturn("operation"); + + target = mock(AbstractLogic.class); + + joinPoint = mock(ProceedingJoinPoint.class); + when(joinPoint.getTarget()).thenReturn(target); + when(joinPoint.getSignature()).thenReturn(signature); + when(joinPoint.getArgs()).thenReturn(new Object[] { "arg" }); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void noNotificationNorAudit() throws Throwable { + when(joinPoint.proceed()).thenReturn("output"); + + assertEquals("output", handler.around(joinPoint)); + + verify(notificationManager, never()).createTasks(any()); + verify(auditManager, never()).audit(any()); + } + + @Test + void notificationAndAudit() throws Throwable { + when(notificationManager.notificationsAvailable(any(), any(), any(), any(), any())).thenReturn(true); + when(auditManager.auditRequested(any(), any(), any(), any(), any(), any())).thenReturn(true); + doThrow(new UnresolvedReferenceException()).when(target).resolveBeanReference(any(), any()); + when(joinPoint.proceed()).thenReturn("output"); + + assertEquals("output", handler.around(joinPoint)); + + verify(notificationManager).createTasks(any(AfterHandlingEvent.class)); + verify(auditManager).audit(any(AfterHandlingEvent.class)); + } + + @Test + void beforeReferenceResolved() throws Throwable { + when(auditManager.auditRequested(any(), any(), any(), any(), any(), any())).thenReturn(true); + when(target.resolveBeanReference(any(), any())).thenReturn(new AnyTypeClassTO()); + when(joinPoint.proceed()).thenReturn("output"); + + assertEquals("output", handler.around(joinPoint)); + + verify(target).resolveBeanReference(any(), any()); + verify(auditManager).audit(any(AfterHandlingEvent.class)); + } + + @Test + void failureIsPropagated() throws Throwable { + when(notificationManager.notificationsAvailable( + any(), any(), any(), any(), any())).thenReturn(true); + when(joinPoint.proceed()).thenThrow(new IllegalStateException("failure")); + + assertThrows(IllegalStateException.class, () -> handler.around(joinPoint)); + + verify(notificationManager).createTasks(any(AfterHandlingEvent.class)); + verify(auditManager).audit(any(AfterHandlingEvent.class)); + } + + @Test + void notificationErrorsAreIgnored() throws Throwable { + when(notificationManager.notificationsAvailable( + any(), any(), any(), any(), any())).thenReturn(true); + doThrow(new IllegalStateException("failure")).when(notificationManager).createTasks(any()); + when(joinPoint.proceed()).thenReturn("output"); + + assertEquals("output", handler.around(joinPoint)); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/LogicTestAuth.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/LogicTestAuth.java new file mode 100644 index 00000000000..9e6fb4a9ac8 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/LogicTestAuth.java @@ -0,0 +1,71 @@ +/* + * 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.core.logic; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.core.spring.security.SyncopeAuthenticationDetails; +import org.apache.syncope.core.spring.security.SyncopeGrantedAuthority; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; + +/** + * Small helper to set / clear the Spring Security context in unit tests, without bootstrapping a full application + * context. + */ +public final class LogicTestAuth { + + private static final String PASSWORD = "FAKE_PASSWORD"; + + public static void authenticate(final String username) { + authenticate(username, SyncopeConstants.MASTER_DOMAIN, Map.of()); + } + + public static void authenticate(final String username, final String domain) { + authenticate(username, domain, Map.of()); + } + + public static void authenticate( + final String username, + final String domain, + final Map> entitlements) { + + List authorities = entitlements.entrySet().stream(). + map(entry -> new SyncopeGrantedAuthority(entry.getKey(), entry.getValue().iterator().next())). + map(GrantedAuthority.class::cast). + toList(); + + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( + new org.springframework.security.core.userdetails.User(username, PASSWORD, authorities), + PASSWORD, + authorities); + authentication.setDetails(new SyncopeAuthenticationDetails(domain, null)); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + + public static void clear() { + SecurityContextHolder.clearContext(); + } + + private LogicTestAuth() { + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/MailTemplateLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/MailTemplateLogicTest.java new file mode 100644 index 00000000000..1fa439435b8 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/MailTemplateLogicTest.java @@ -0,0 +1,180 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.MailTemplateTO; +import org.apache.syncope.common.lib.types.MailTemplateFormat; +import org.apache.syncope.core.persistence.api.dao.DuplicateException; +import org.apache.syncope.core.persistence.api.dao.MailTemplateDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.NotificationDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.MailTemplate; +import org.apache.syncope.core.persistence.api.entity.Notification; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class MailTemplateLogicTest { + + private MailTemplateDAO mailTemplateDAO; + + private NotificationDAO notificationDAO; + + private EntityFactory entityFactory; + + private MailTemplateLogic logic; + + @BeforeEach + void setUp() { + mailTemplateDAO = mock(MailTemplateDAO.class); + notificationDAO = mock(NotificationDAO.class); + entityFactory = mock(EntityFactory.class); + logic = new MailTemplateLogic(mailTemplateDAO, notificationDAO, entityFactory); + } + + @Test + void readMissing() { + doReturn(Optional.empty()).when(mailTemplateDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.read("missing")); + } + + @Test + void read() { + MailTemplate mailTemplate = mock(MailTemplate.class); + doReturn(Optional.of(mailTemplate)).when(mailTemplateDAO).findById("key"); + + assertEquals("key", logic.read("key").getKey()); + } + + @Test + void list() { + MailTemplate mailTemplate = mock(MailTemplate.class); + when(mailTemplate.getKey()).thenReturn("key"); + doReturn(List.of(mailTemplate)).when(mailTemplateDAO).findAll(); + + assertEquals(List.of("key"), logic.list().stream().map(MailTemplateTO::getKey).toList()); + } + + @Test + void createDuplicate() { + when(mailTemplateDAO.existsById("key")).thenReturn(true); + + assertThrows(DuplicateException.class, () -> logic.create("key")); + } + + @Test + void create() { + MailTemplate mailTemplate = mock(MailTemplate.class); + when(entityFactory.newEntity(MailTemplate.class)).thenReturn(mailTemplate); + + assertEquals("key", logic.create("key").getKey()); + verify(mailTemplate).setKey("key"); + verify(mailTemplateDAO).save(mailTemplate); + } + + @Test + void getFormatMissing() { + doReturn(Optional.empty()).when(mailTemplateDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.getFormat("missing", MailTemplateFormat.TEXT)); + } + + @Test + void getFormatWithoutContent() { + MailTemplate mailTemplate = mock(MailTemplate.class); + doReturn(Optional.of(mailTemplate)).when(mailTemplateDAO).findById("key"); + + assertThrows(NotFoundException.class, () -> logic.getFormat("key", MailTemplateFormat.HTML)); + } + + @Test + void getFormat() { + MailTemplate mailTemplate = mock(MailTemplate.class); + doReturn(Optional.of(mailTemplate)).when(mailTemplateDAO).findById("key"); + when(mailTemplate.getTextTemplate()).thenReturn("text"); + when(mailTemplate.getHTMLTemplate()).thenReturn("html"); + + assertEquals("text", logic.getFormat("key", MailTemplateFormat.TEXT)); + assertEquals("html", logic.getFormat("key", MailTemplateFormat.HTML)); + } + + @Test + void setFormat() { + MailTemplate mailTemplate = mock(MailTemplate.class); + doReturn(Optional.of(mailTemplate)).when(mailTemplateDAO).findById("key"); + + logic.setFormat("key", MailTemplateFormat.TEXT, "text"); + logic.setFormat("key", MailTemplateFormat.HTML, "html"); + + verify(mailTemplate).setTextTemplate("text"); + verify(mailTemplate).setHTMLTemplate("html"); + verify(mailTemplateDAO, org.mockito.Mockito.times(2)).save(mailTemplate); + } + + @Test + void deleteMissing() { + doReturn(Optional.empty()).when(mailTemplateDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.delete("missing")); + } + + @Test + void deleteInUse() { + MailTemplate mailTemplate = mock(MailTemplate.class); + Notification notification = mock(Notification.class); + when(notification.getKey()).thenReturn("notification"); + doReturn(Optional.of(mailTemplate)).when(mailTemplateDAO).findById("key"); + when(notificationDAO.findByTemplate(mailTemplate)).thenReturn(List.of(notification)); + + assertThrows(SyncopeClientException.class, () -> logic.delete("key")); + } + + @Test + void delete() { + MailTemplate mailTemplate = mock(MailTemplate.class); + doReturn(Optional.of(mailTemplate)).when(mailTemplateDAO).findById("key"); + when(notificationDAO.findByTemplate(mailTemplate)).thenReturn(List.of()); + + assertEquals("key", logic.delete("key").getKey()); + verify(mailTemplateDAO).deleteById("key"); + } + + @Test + void resolveBeanReference() throws Exception { + MailTemplateTO to = new MailTemplateTO(); + to.setKey("key"); + + assertSame( + to.getKey(), + logic.resolveBeanReference( + MailTemplateLogic.class.getMethod("create", String.class), "key").getKey()); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/MfaLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/MfaLogicTest.java new file mode 100644 index 00000000000..0d5b908f8a6 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/MfaLogicTest.java @@ -0,0 +1,170 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import dev.samstevens.totp.code.CodeVerifier; +import dev.samstevens.totp.code.HashingAlgorithm; +import dev.samstevens.totp.qr.QrData; +import dev.samstevens.totp.qr.QrGenerator; +import dev.samstevens.totp.recovery.RecoveryCodeGenerator; +import dev.samstevens.totp.secret.SecretGenerator; +import java.util.List; +import org.apache.syncope.common.keymaster.client.api.DomainOps; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.types.Mfa; +import org.apache.syncope.common.lib.types.MfaCheck; +import org.apache.syncope.core.persistence.api.Encryptor; +import org.apache.syncope.core.persistence.api.EncryptorManager; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.provisioning.api.data.UserDataBinder; +import org.apache.syncope.core.spring.security.SecurityProperties; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class MfaLogicTest { + + private EncryptorManager encryptorManager; + + private Encryptor encryptor; + + private SecretGenerator secretGenerator; + + private QrGenerator qrGenerator; + + private RecoveryCodeGenerator recoveryCodeGenerator; + + private CodeVerifier codeVerifier; + + private SecurityProperties securityProperties; + + private MfaLogic logic; + + @BeforeEach + void setUp() { + encryptorManager = mock(EncryptorManager.class); + encryptor = mock(Encryptor.class); + secretGenerator = mock(SecretGenerator.class); + qrGenerator = mock(QrGenerator.class); + recoveryCodeGenerator = mock(RecoveryCodeGenerator.class); + codeVerifier = mock(CodeVerifier.class); + + securityProperties = new SecurityProperties(); + securityProperties.setAdminUser("admin"); + + when(encryptorManager.getInstance()).thenReturn(encryptor); + + logic = new MfaLogic( + mock(UserDataBinder.class), + mock(UserDAO.class), + encryptorManager, + mock(DomainOps.class), + secretGenerator, + qrGenerator, + HashingAlgorithm.SHA1, + recoveryCodeGenerator, + codeVerifier, + securityProperties); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void generate() throws Exception { + when(secretGenerator.generate()).thenReturn("secret"); + when(qrGenerator.generate(any(QrData.class))).thenReturn(new byte[] { 1 }); + when(qrGenerator.getImageMimeType()).thenReturn("image/png"); + when(recoveryCodeGenerator.generateCodes(10)).thenReturn(new String[] { "code" }); + + Mfa mfa = logic.generate("user"); + + assertEquals("secret", mfa.secret()); + assertEquals(List.of("code"), mfa.recoveryCodes()); + assertTrue(mfa.dataUri().startsWith("data:image/png;base64,")); + } + + @Test + void generateForAdmin() throws Exception { + when(secretGenerator.generate()).thenReturn("secret"); + when(qrGenerator.generate(any(QrData.class))).thenReturn(new byte[] { 1 }); + when(qrGenerator.getImageMimeType()).thenReturn("image/png"); + + assertEquals(List.of(), logic.generate("admin").recoveryCodes()); + } + + @Test + void checkWithoutValues() { + assertThrows(SyncopeClientException.class, () -> logic.check(new MfaCheck(null, "otp"))); + assertThrows(SyncopeClientException.class, () -> logic.check(new MfaCheck("secret", null))); + } + + @Test + void check() { + when(codeVerifier.isValidCode("secret", "otp")).thenReturn(true); + + assertTrue(logic.check(new MfaCheck("secret", "otp"))); + } + + @Test + void enrollAdminWithExistingSecret() throws Exception { + LogicTestAuth.authenticate("admin"); + securityProperties.setAdminMfaSecret("existing"); + when(encryptor.encode(any(), any())).thenReturn("encrypted"); + + assertThrows(SyncopeClientException.class, () -> logic.enroll(new Mfa("secret", "uri", List.of()))); + } + + @Test + void enrollAdmin() throws Exception { + LogicTestAuth.authenticate("admin"); + when(encryptor.encode(any(), any())).thenReturn("encrypted"); + + logic.enroll(new Mfa("secret", "uri", List.of())); + + assertEquals("encrypted", securityProperties.getAdminMfaSecret()); + } + + @Test + void enrolledAdmin() { + LogicTestAuth.authenticate("admin"); + + assertFalse(logic.enrolled("admin")); + + securityProperties.setAdminMfaSecret("encrypted"); + assertTrue(logic.enrolled("admin")); + } + + @Test + void dismissAdminOnMaster() { + LogicTestAuth.authenticate("admin"); + + assertThrows(SyncopeClientException.class, () -> logic.dismiss()); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/NotificationLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/NotificationLogicTest.java new file mode 100644 index 00000000000..5e4d53ba46c --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/NotificationLogicTest.java @@ -0,0 +1,179 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.to.JobTO; +import org.apache.syncope.common.lib.to.NotificationTO; +import org.apache.syncope.common.lib.types.JobAction; +import org.apache.syncope.common.lib.types.JobType; +import org.apache.syncope.core.persistence.api.dao.JobStatusDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.NotificationDAO; +import org.apache.syncope.core.persistence.api.entity.Notification; +import org.apache.syncope.core.provisioning.api.data.NotificationDataBinder; +import org.apache.syncope.core.provisioning.api.job.JobManager; +import org.apache.syncope.core.provisioning.java.job.SyncopeTaskScheduler; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class NotificationLogicTest { + + private JobManager jobManager; + + private SyncopeTaskScheduler scheduler; + + private JobStatusDAO jobStatusDAO; + + private NotificationDAO notificationDAO; + + private NotificationDataBinder binder; + + private NotificationLogic logic; + + @BeforeEach + void setUp() { + jobManager = mock(JobManager.class); + scheduler = mock(SyncopeTaskScheduler.class); + jobStatusDAO = mock(JobStatusDAO.class); + notificationDAO = mock(NotificationDAO.class); + binder = mock(NotificationDataBinder.class); + + logic = new NotificationLogic(jobManager, scheduler, jobStatusDAO, notificationDAO, binder); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void readMissing() { + doReturn(Optional.empty()).when(notificationDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.read("missing")); + } + + @Test + void readAndList() { + Notification notification = mock(Notification.class); + NotificationTO to = new NotificationTO(); + to.setKey("key"); + + doReturn(Optional.of(notification)).when(notificationDAO).findById("key"); + doReturn(List.of(notification)).when(notificationDAO).findAll(); + when(binder.getNotificationTO(notification)).thenReturn(to); + + assertSame(to, logic.read("key")); + assertEquals(List.of(to), logic.list()); + } + + @Test + void create() { + NotificationTO to = new NotificationTO(); + to.setKey("key"); + Notification notification = mock(Notification.class); + + when(binder.create(to)).thenReturn(notification); + when(notificationDAO.save(notification)).thenReturn(notification); + when(binder.getNotificationTO(notification)).thenReturn(to); + + assertSame(to, logic.create(to)); + } + + @Test + void updateMissing() { + NotificationTO to = new NotificationTO(); + to.setKey("missing"); + doReturn(Optional.empty()).when(notificationDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.update(to)); + } + + @Test + void update() { + NotificationTO to = new NotificationTO(); + to.setKey("key"); + Notification notification = mock(Notification.class); + + doReturn(Optional.of(notification)).when(notificationDAO).findById("key"); + when(notificationDAO.save(notification)).thenReturn(notification); + when(binder.getNotificationTO(notification)).thenReturn(to); + + assertSame(to, logic.update(to)); + verify(binder).update(notification, to); + } + + @Test + void deleteMissing() { + doReturn(Optional.empty()).when(notificationDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.delete("missing")); + } + + @Test + void delete() { + Notification notification = mock(Notification.class); + NotificationTO to = new NotificationTO(); + to.setKey("key"); + + doReturn(Optional.of(notification)).when(notificationDAO).findById("key"); + when(binder.getNotificationTO(notification)).thenReturn(to); + + assertSame(to, logic.delete("key")); + verify(notificationDAO).deleteById("key"); + } + + @Test + void getJob() { + LogicTestAuth.authenticate("admin"); + + when(scheduler.getJobNames("Master")).thenReturn(List.of(JobManager.NOTIFICATION_JOB)); + when(scheduler.getNextTrigger("Master", JobManager.NOTIFICATION_JOB)).thenReturn(Optional.empty()); + when(jobManager.isRunning(JobManager.NOTIFICATION_JOB)).thenReturn(false); + when(jobStatusDAO.get(JobManager.NOTIFICATION_JOB)).thenReturn("status"); + + JobTO job = logic.getJob(); + + assertNotNull(job); + assertEquals(JobType.NOTIFICATION, job.getType()); + assertFalse(job.isScheduled()); + assertFalse(job.isRunning()); + assertEquals("status", job.getStatus()); + } + + @Test + void actionJob() { + logic.actionJob(JobAction.START); + + verify(scheduler).start("Master", JobManager.NOTIFICATION_JOB); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/PolicyLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/PolicyLogicTest.java new file mode 100644 index 00000000000..83800344b64 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/PolicyLogicTest.java @@ -0,0 +1,179 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.policy.AccountPolicyTO; +import org.apache.syncope.common.lib.policy.PasswordPolicyTO; +import org.apache.syncope.common.lib.policy.PolicyTO; +import org.apache.syncope.common.lib.types.PolicyType; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.PolicyDAO; +import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.PolicyUtilsFactory; +import org.apache.syncope.core.provisioning.api.data.PolicyDataBinder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class PolicyLogicTest { + + private PolicyDAO policyDAO; + + private PolicyDataBinder binder; + + private PolicyUtilsFactory policyUtilsFactory; + + private PolicyLogic logic; + + @BeforeEach + void setUp() { + policyDAO = mock(PolicyDAO.class); + binder = mock(PolicyDataBinder.class); + policyUtilsFactory = new PolicyUtilsFactory(); + logic = new PolicyLogic(policyDAO, binder, policyUtilsFactory); + } + + @Test + void createWithWrongType() { + assertThrows(SyncopeClientException.class, + () -> logic.create(PolicyType.ACCOUNT, new PasswordPolicyTO())); + } + + @Test + void create() { + AccountPolicyTO to = new AccountPolicyTO(); + to.setKey("key"); + AccountPolicy policy = mock(AccountPolicy.class); + + when(binder.create(to)).thenReturn(policy); + when(policyDAO.save(policy)).thenReturn(policy); + when(binder.getPolicyTO(policy)).thenReturn(to); + + assertSame(to, logic.create(PolicyType.ACCOUNT, to)); + } + + @Test + void updateMissing() { + AccountPolicyTO to = new AccountPolicyTO(); + to.setKey("missing"); + + doReturn(Optional.empty()).when(policyDAO).findById("missing", AccountPolicy.class); + + assertThrows(NotFoundException.class, () -> logic.update(PolicyType.ACCOUNT, to)); + } + + @Test + void updateWithWrongType() { + PasswordPolicyTO to = new PasswordPolicyTO(); + to.setKey("key"); + AccountPolicy policy = mock(AccountPolicy.class); + doReturn(Optional.of(policy)).when(policyDAO).findById("key", AccountPolicy.class); + + assertThrows(SyncopeClientException.class, () -> logic.update(PolicyType.ACCOUNT, to)); + } + + @Test + void update() { + AccountPolicyTO to = new AccountPolicyTO(); + to.setKey("key"); + AccountPolicy policy = mock(AccountPolicy.class); + AccountPolicy updated = mock(AccountPolicy.class); + + doReturn(Optional.of(policy)).when(policyDAO).findById("key", AccountPolicy.class); + when(binder.update(policy, to)).thenReturn(updated); + when(policyDAO.save(updated)).thenReturn(updated); + when(binder.getPolicyTO(updated)).thenReturn(to); + + assertSame(to, logic.update(PolicyType.ACCOUNT, to)); + } + + @Test + void list() { + AccountPolicy policy = mock(AccountPolicy.class); + AccountPolicyTO to = new AccountPolicyTO(); + to.setKey("key"); + + doReturn(List.of(policy)).when(policyDAO).findAll(AccountPolicy.class); + when(binder.getPolicyTO(policy)).thenReturn(to); + + assertEquals(List.of(to), logic.list(PolicyType.ACCOUNT)); + } + + @Test + void readMissing() { + doReturn(Optional.empty()).when(policyDAO).findById("missing", AccountPolicy.class); + + assertThrows(NotFoundException.class, () -> logic.read(PolicyType.ACCOUNT, "missing")); + } + + @Test + void read() { + AccountPolicy policy = mock(AccountPolicy.class); + AccountPolicyTO to = new AccountPolicyTO(); + to.setKey("key"); + + doReturn(Optional.of(policy)).when(policyDAO).findById("key", AccountPolicy.class); + when(binder.getPolicyTO(policy)).thenReturn(to); + + assertSame(to, logic.read(PolicyType.ACCOUNT, "key")); + } + + @Test + void deleteMissing() { + doReturn(Optional.empty()).when(policyDAO).findById("missing", AccountPolicy.class); + + assertThrows(NotFoundException.class, () -> logic.delete(PolicyType.ACCOUNT, "missing")); + } + + @Test + void delete() { + AccountPolicy policy = mock(AccountPolicy.class); + AccountPolicyTO to = new AccountPolicyTO(); + to.setKey("key"); + + doReturn(Optional.of(policy)).when(policyDAO).findById("key", AccountPolicy.class); + when(binder.getPolicyTO(policy)).thenReturn(to); + + assertSame(to, logic.delete(PolicyType.ACCOUNT, "key")); + verify(policyDAO).delete(policy); + } + + @Test + void resolveReference() throws Exception { + PolicyTO to = new AccountPolicyTO(); + to.setKey("key"); + AccountPolicy policy = mock(AccountPolicy.class); + + doReturn(Optional.of(policy)).when(policyDAO).findById("key", AccountPolicy.class); + when(binder.getPolicyTO(policy)).thenReturn(to); + + assertSame(to, logic.resolveBeanReference( + PolicyLogic.class.getMethod("create", PolicyType.class, PolicyTO.class), PolicyType.ACCOUNT, "key")); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/RealmLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/RealmLogicTest.java new file mode 100644 index 00000000000..a7942f2c8b3 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/RealmLogicTest.java @@ -0,0 +1,173 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.to.RealmTO; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.persistence.api.dao.CASSPClientAppDAO; +import org.apache.syncope.core.persistence.api.dao.DuplicateException; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.OIDCRPClientAppDAO; +import org.apache.syncope.core.persistence.api.dao.RealmDAO; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; +import org.apache.syncope.core.persistence.api.dao.SAML2SPClientAppDAO; +import org.apache.syncope.core.persistence.api.dao.TaskDAO; +import org.apache.syncope.core.persistence.api.dao.search.SearchCond; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.provisioning.api.data.RealmDataBinder; +import org.apache.syncope.core.provisioning.api.propagation.PropagationManager; +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Pageable; + +class RealmLogicTest { + + private RealmDAO realmDAO; + + private RealmSearchDAO realmSearchDAO; + + private AnySearchDAO searchDAO; + + private RealmDataBinder binder; + + private RealmLogic logic; + + @BeforeEach + void setUp() { + realmDAO = mock(RealmDAO.class); + realmSearchDAO = mock(RealmSearchDAO.class); + searchDAO = mock(AnySearchDAO.class); + binder = mock(RealmDataBinder.class); + + logic = new RealmLogic( + realmDAO, + realmSearchDAO, + searchDAO, + mock(TaskDAO.class), + mock(CASSPClientAppDAO.class), + mock(OIDCRPClientAppDAO.class), + mock(SAML2SPClientAppDAO.class), + binder, + mock(PropagationManager.class), + mock(PropagationTaskExecutor.class)); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void search() { + when(realmSearchDAO.getAllMatchingCond()).thenReturn(mock(SearchCond.class)); + when(realmSearchDAO.count(any(), any(SearchCond.class))).thenReturn(0L); + when(realmSearchDAO.search(any(), any(SearchCond.class), any(Pageable.class))).thenReturn(List.of()); + + assertEquals(0, logic.search(Set.of(), null, Pageable.unpaged()).getTotalElements()); + } + + @Test + void searchWithMissingBase() { + doReturn(Optional.empty()).when(realmSearchDAO).findByFullPath("/missing"); + + assertThrows(NotFoundException.class, + () -> logic.search(Set.of("/missing"), mock(SearchCond.class), Pageable.unpaged())); + } + + @Test + void createWithMissingParent() { + RealmTO to = new RealmTO(); + to.setName("child"); + doReturn(Optional.empty()).when(realmSearchDAO).findByFullPath("/missing"); + + assertThrows(NotFoundException.class, () -> logic.create("/missing", to)); + } + + @Test + void createWithMismatchingParent() { + RealmTO to = new RealmTO(); + to.setName("child"); + to.setParent("parent-key"); + + Realm parent = mock(Realm.class); + when(parent.getFullPath()).thenReturn("/parent"); + doReturn(Optional.of(parent)).when(realmDAO).findById("parent-key"); + + assertThrows(SyncopeClientException.class, () -> logic.create("/other", to)); + } + + @Test + void createDuplicate() { + LogicTestAuth.authenticate("admin", "Master", + Map.of(IdRepoEntitlement.REALM_CREATE, Set.of(SyncopeConstants.ROOT_REALM))); + + RealmTO to = new RealmTO(); + to.setName("child"); + + Realm parent = mock(Realm.class); + when(parent.getFullPath()).thenReturn(SyncopeConstants.ROOT_REALM); + doReturn(Optional.of(parent)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + doReturn(Optional.of(mock(Realm.class))).when(realmSearchDAO).findByFullPath("/child"); + + assertThrows(DuplicateException.class, () -> logic.create(SyncopeConstants.ROOT_REALM, to)); + } + + @Test + void deleteWithChildren() { + LogicTestAuth.authenticate("admin", "Master", + Map.of(IdRepoEntitlement.REALM_DELETE, Set.of(SyncopeConstants.ROOT_REALM))); + + Realm realm = mock(Realm.class); + when(realm.getFullPath()).thenReturn("/realm"); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath("/realm"); + when(realmSearchDAO.findChildren(realm)).thenReturn(List.of(mock(Realm.class))); + + assertThrows(SyncopeClientException.class, () -> logic.delete("/realm")); + } + + @Test + void deleteWithUsers() { + LogicTestAuth.authenticate("admin", "Master", + Map.of(IdRepoEntitlement.REALM_DELETE, Set.of(SyncopeConstants.ROOT_REALM))); + + Realm realm = mock(Realm.class); + when(realm.getFullPath()).thenReturn("/realm"); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath("/realm"); + when(realmSearchDAO.findChildren(realm)).thenReturn(List.of()); + when(searchDAO.count(any(), any(Boolean.class), any(), any(SearchCond.class), any())).thenReturn(1L); + + assertThrows(SyncopeClientException.class, () -> logic.delete("/realm")); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/RelationshipTypeLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/RelationshipTypeLogicTest.java new file mode 100644 index 00000000000..b7750e617f6 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/RelationshipTypeLogicTest.java @@ -0,0 +1,140 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.to.RelationshipTypeTO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; +import org.apache.syncope.core.persistence.api.entity.RelationshipType; +import org.apache.syncope.core.provisioning.api.data.RelationshipTypeDataBinder; +import org.apache.syncope.core.provisioning.api.event.EntityLifecycleEvent; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.context.ApplicationEventPublisher; + +class RelationshipTypeLogicTest { + + private RelationshipTypeDataBinder binder; + + private RelationshipTypeDAO relationshipTypeDAO; + + private ApplicationEventPublisher publisher; + + private RelationshipTypeLogic logic; + + @BeforeEach + void setUp() { + binder = mock(RelationshipTypeDataBinder.class); + relationshipTypeDAO = mock(RelationshipTypeDAO.class); + publisher = mock(ApplicationEventPublisher.class); + logic = new RelationshipTypeLogic(binder, relationshipTypeDAO, publisher); + } + + @Test + void readMissing() { + doReturn(Optional.empty()).when(relationshipTypeDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.read("missing")); + } + + @Test + void readAndList() { + RelationshipType relationshipType = mock(RelationshipType.class); + RelationshipTypeTO to = new RelationshipTypeTO(); + to.setKey("key"); + + doReturn(Optional.of(relationshipType)).when(relationshipTypeDAO).findById("key"); + doReturn(List.of(relationshipType)).when(relationshipTypeDAO).findAll(); + when(binder.getRelationshipTypeTO(relationshipType)).thenReturn(to); + + assertSame(to, logic.read("key")); + assertEquals(List.of(to), logic.list()); + } + + @Test + void create() { + RelationshipTypeTO to = new RelationshipTypeTO(); + to.setKey("key"); + + RelationshipType relationshipType = mock(RelationshipType.class); + when(binder.create(to)).thenReturn(relationshipType); + when(relationshipTypeDAO.save(relationshipType)).thenReturn(relationshipType); + when(binder.getRelationshipTypeTO(relationshipType)).thenReturn(to); + + assertSame(to, logic.create(to)); + verify(publisher).publishEvent(any(EntityLifecycleEvent.class)); + } + + @Test + void updateMissing() { + RelationshipTypeTO to = new RelationshipTypeTO(); + to.setKey("missing"); + doReturn(Optional.empty()).when(relationshipTypeDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.update(to)); + } + + @Test + void update() { + RelationshipTypeTO to = new RelationshipTypeTO(); + to.setKey("key"); + + RelationshipType relationshipType = mock(RelationshipType.class); + RelationshipType updated = mock(RelationshipType.class); + doReturn(Optional.of(relationshipType)).when(relationshipTypeDAO).findById("key"); + when(binder.update(relationshipType, to)).thenReturn(updated); + when(relationshipTypeDAO.save(updated)).thenReturn(updated); + when(binder.getRelationshipTypeTO(updated)).thenReturn(to); + + assertSame(to, logic.update(to)); + verify(publisher).publishEvent(any(EntityLifecycleEvent.class)); + } + + @Test + void deleteMissing() { + doReturn(Optional.empty()).when(relationshipTypeDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.delete("missing")); + } + + @Test + void delete() { + RelationshipType relationshipType = mock(RelationshipType.class); + RelationshipTypeTO to = new RelationshipTypeTO(); + to.setKey("key"); + + doReturn(Optional.of(relationshipType)).when(relationshipTypeDAO).findById("key"); + when(binder.getRelationshipTypeTO(relationshipType)).thenReturn(to); + + assertSame(to, logic.delete("key")); + verify(relationshipTypeDAO).deleteById("key"); + verify(publisher).publishEvent(any(EntityLifecycleEvent.class)); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/ReportLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/ReportLogicTest.java index b11f2761162..77c3852b2a1 100644 --- a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/ReportLogicTest.java +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/ReportLogicTest.java @@ -49,7 +49,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class ReportLogicTest extends AbstractTest { +class ReportLogicTest extends AbstractTest { public static class TestReportJobDelegate extends AbstractReportJobDelegate { @@ -100,7 +100,7 @@ private void checkExport(final String execKey) throws IOException { } @Test - public void executeAndExport() throws Exception { + void executeAndExport() throws Exception { ReportTO report = logic.read("0062ea9c-924d-4ecf-9961-4492a8cc6d1b"); assertNotNull(report); assertTrue(report.isActive()); diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/RoleLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/RoleLogicTest.java new file mode 100644 index 00000000000..20015b3c479 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/RoleLogicTest.java @@ -0,0 +1,157 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.to.RoleTO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.RoleDAO; +import org.apache.syncope.core.persistence.api.entity.Role; +import org.apache.syncope.core.provisioning.api.data.RoleDataBinder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class RoleLogicTest { + + private RoleDataBinder binder; + + private RoleDAO roleDAO; + + private RoleLogic logic; + + @BeforeEach + void setUp() { + binder = mock(RoleDataBinder.class); + roleDAO = mock(RoleDAO.class); + logic = new RoleLogic(binder, roleDAO); + } + + @Test + void readMissing() { + doReturn(Optional.empty()).when(roleDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.read("missing")); + } + + @Test + void readAndList() { + Role role = mock(Role.class); + RoleTO to = new RoleTO(); + to.setKey("key"); + + doReturn(Optional.of(role)).when(roleDAO).findById("key"); + doReturn(List.of(role)).when(roleDAO).findAll(); + when(binder.getRoleTO(role)).thenReturn(to); + + assertSame(to, logic.read("key")); + assertEquals(List.of(to), logic.list()); + } + + @Test + void create() { + RoleTO to = new RoleTO(); + to.setKey("key"); + Role role = mock(Role.class); + + when(binder.create(to)).thenReturn(role); + when(binder.getRoleTO(role)).thenReturn(to); + + assertSame(to, logic.create(to)); + } + + @Test + void updateMissing() { + RoleTO to = new RoleTO(); + to.setKey("missing"); + doReturn(Optional.empty()).when(roleDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.update(to)); + } + + @Test + void update() { + RoleTO to = new RoleTO(); + to.setKey("key"); + Role role = mock(Role.class); + Role updated = mock(Role.class); + + doReturn(Optional.of(role)).when(roleDAO).findById("key"); + when(binder.update(role, to)).thenReturn(updated); + when(binder.getRoleTO(updated)).thenReturn(to); + + assertSame(to, logic.update(to)); + } + + @Test + void deleteMissing() { + doReturn(Optional.empty()).when(roleDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.delete("missing")); + } + + @Test + void delete() { + Role role = mock(Role.class); + RoleTO to = new RoleTO(); + to.setKey("key"); + + doReturn(Optional.of(role)).when(roleDAO).findById("key"); + when(binder.getRoleTO(role)).thenReturn(to); + + assertSame(to, logic.delete("key")); + verify(roleDAO).delete(role); + } + + @Test + void getAnyLayout() { + Role role = mock(Role.class); + doReturn(Optional.of(role)).when(roleDAO).findById("key"); + when(role.getAnyLayout()).thenReturn("layout"); + + assertEquals("layout", logic.getAnyLayout("key")); + } + + @Test + void getAnyLayoutMissing() { + Role role = mock(Role.class); + doReturn(Optional.of(role)).when(roleDAO).findById("key"); + + assertThrows(NotFoundException.class, () -> logic.getAnyLayout("key")); + } + + @Test + void setAnyLayout() { + Role role = mock(Role.class); + doReturn(Optional.of(role)).when(roleDAO).findById("key"); + + logic.setAnyLayout("key", "layout"); + + verify(role).setAnyLayout("layout"); + verify(roleDAO).save(role); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/SchemaLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/SchemaLogicTest.java new file mode 100644 index 00000000000..3d36bfacd1b --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/SchemaLogicTest.java @@ -0,0 +1,209 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.DerSchemaTO; +import org.apache.syncope.common.lib.to.PlainSchemaTO; +import org.apache.syncope.common.lib.to.SchemaTO; +import org.apache.syncope.common.lib.types.SchemaType; +import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; +import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.DuplicateException; +import org.apache.syncope.core.persistence.api.dao.ImplementationDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.entity.DerSchema; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.provisioning.api.data.SchemaDataBinder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class SchemaLogicTest { + + private PlainSchemaDAO plainSchemaDAO; + + private DerSchemaDAO derSchemaDAO; + + private AnyTypeClassDAO anyTypeClassDAO; + + private ImplementationDAO implementationDAO; + + private SchemaDataBinder binder; + + private SchemaLogic logic; + + @BeforeEach + void setUp() { + plainSchemaDAO = mock(PlainSchemaDAO.class); + derSchemaDAO = mock(DerSchemaDAO.class); + anyTypeClassDAO = mock(AnyTypeClassDAO.class); + implementationDAO = mock(ImplementationDAO.class); + binder = mock(SchemaDataBinder.class); + + logic = new SchemaLogic(plainSchemaDAO, derSchemaDAO, anyTypeClassDAO, implementationDAO, binder); + } + + @Test + void createWithoutKey() { + assertThrows(SyncopeClientException.class, () -> logic.create(SchemaType.PLAIN, new PlainSchemaTO())); + } + + @Test + void createDuplicate() { + PlainSchemaTO to = new PlainSchemaTO(); + to.setKey("key"); + doReturn(Optional.of(mock(PlainSchema.class))).when(plainSchemaDAO).findById("key"); + + assertThrows(DuplicateException.class, () -> logic.create(SchemaType.PLAIN, to)); + } + + @Test + void createPlain() { + PlainSchemaTO to = new PlainSchemaTO(); + to.setKey("key"); + + PlainSchema schema = mock(PlainSchema.class); + when(schema.getKey()).thenReturn("key"); + when(binder.create(to)).thenReturn(schema); + when(binder.getPlainSchemaTO("key")).thenReturn(to); + + assertSame(to, logic.create(SchemaType.PLAIN, to)); + } + + @Test + void createDerived() { + DerSchemaTO to = new DerSchemaTO(); + to.setKey("key"); + + DerSchema schema = mock(DerSchema.class); + when(schema.getKey()).thenReturn("key"); + when(binder.create(to)).thenReturn(schema); + when(binder.getDerSchemaTO("key")).thenReturn(to); + + assertSame(to, logic.create(SchemaType.DERIVED, to)); + } + + @Test + void deleteMissing() { + doReturn(Optional.empty()).when(plainSchemaDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.delete(SchemaType.PLAIN, "missing")); + } + + @Test + void deletePlain() { + doReturn(Optional.of(mock(PlainSchema.class))).when(plainSchemaDAO).findById("key"); + + logic.delete(SchemaType.PLAIN, "key"); + + verify(plainSchemaDAO).deleteById("key"); + } + + @Test + void deleteDerived() { + doReturn(Optional.of(mock(DerSchema.class))).when(derSchemaDAO).findById("key"); + + logic.delete(SchemaType.DERIVED, "key"); + + verify(derSchemaDAO).deleteById("key"); + } + + @Test + void searchPlain() { + PlainSchema schema = mock(PlainSchema.class); + PlainSchemaTO to = new PlainSchemaTO(); + to.setKey("key"); + + doReturn(List.of(schema)).when(plainSchemaDAO).findAll(); + when(schema.getKey()).thenReturn("key"); + when(binder.getPlainSchemaTO("key")).thenReturn(to); + + assertEquals(List.of(to), logic.search(SchemaType.PLAIN, new ArrayList<>(), null)); + } + + @Test + void searchDerived() { + DerSchema schema = mock(DerSchema.class); + DerSchemaTO to = new DerSchemaTO(); + to.setKey("key"); + + doReturn(List.of(schema)).when(derSchemaDAO).findAll(); + when(schema.getKey()).thenReturn("key"); + when(binder.getDerSchemaTO("key")).thenReturn(to); + + assertEquals(List.of(to), logic.search(SchemaType.DERIVED, new ArrayList<>(), null)); + } + + @Test + void read() { + PlainSchemaTO to = new PlainSchemaTO(); + to.setKey("key"); + when(binder.getPlainSchemaTO("key")).thenReturn(to); + + assertSame(to, logic.read(SchemaType.PLAIN, "key")); + } + + @Test + void updateMissing() { + PlainSchemaTO to = new PlainSchemaTO(); + to.setKey("missing"); + doReturn(Optional.empty()).when(plainSchemaDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.update(SchemaType.PLAIN, to)); + } + + @Test + void update() { + PlainSchemaTO to = new PlainSchemaTO(); + to.setKey("key"); + PlainSchema schema = mock(PlainSchema.class); + doReturn(Optional.of(schema)).when(plainSchemaDAO).findById("key"); + + logic.update(SchemaType.PLAIN, to); + + verify(binder).update(to, schema); + } + + @Test + void resolveBeanReference() throws Exception { + DerSchemaTO to = new DerSchemaTO(); + to.setKey("key"); + DerSchema schema = mock(DerSchema.class); + + doReturn(Optional.empty()).when(plainSchemaDAO).findById("key"); + doReturn(Optional.of(schema)).when(derSchemaDAO).findById("key"); + when(binder.getDerSchemaTO("key")).thenReturn(to); + + assertSame(to, logic.resolveBeanReference( + SchemaLogic.class.getMethod("create", SchemaType.class, SchemaTO.class), + to)); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/SecurityQuestionLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/SecurityQuestionLogicTest.java new file mode 100644 index 00000000000..0be707cab45 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/SecurityQuestionLogicTest.java @@ -0,0 +1,179 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.to.SecurityQuestionTO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.SecurityQuestionDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.data.SecurityQuestionDataBinder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class SecurityQuestionLogicTest { + + private SecurityQuestionDAO securityQuestionDAO; + + private UserDAO userDAO; + + private SecurityQuestionDataBinder binder; + + private SecurityQuestionLogic logic; + + @BeforeEach + void setUp() { + securityQuestionDAO = mock(SecurityQuestionDAO.class); + userDAO = mock(UserDAO.class); + binder = mock(SecurityQuestionDataBinder.class); + logic = new SecurityQuestionLogic(securityQuestionDAO, userDAO, binder); + } + + @Test + void list() { + SecurityQuestion securityQuestion = mock(SecurityQuestion.class); + SecurityQuestionTO to = new SecurityQuestionTO(); + to.setKey("key"); + + doReturn(List.of(securityQuestion)).when(securityQuestionDAO).findAll(); + when(binder.getSecurityQuestionTO(securityQuestion)).thenReturn(to); + + assertEquals(List.of(to), logic.list()); + } + + @Test + void readMissing() { + doReturn(Optional.empty()).when(securityQuestionDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.read("missing")); + } + + @Test + void read() { + SecurityQuestion securityQuestion = mock(SecurityQuestion.class); + SecurityQuestionTO to = new SecurityQuestionTO(); + to.setKey("key"); + + doReturn(Optional.of(securityQuestion)).when(securityQuestionDAO).findById("key"); + when(binder.getSecurityQuestionTO(securityQuestion)).thenReturn(to); + + assertSame(to, logic.read("key")); + } + + @Test + void create() { + SecurityQuestionTO to = new SecurityQuestionTO(); + to.setKey("key"); + SecurityQuestion securityQuestion = mock(SecurityQuestion.class); + + when(binder.create(to)).thenReturn(securityQuestion); + when(securityQuestionDAO.save(securityQuestion)).thenReturn(securityQuestion); + when(binder.getSecurityQuestionTO(securityQuestion)).thenReturn(to); + + assertSame(to, logic.create(to)); + } + + @Test + void updateMissing() { + SecurityQuestionTO to = new SecurityQuestionTO(); + to.setKey("missing"); + doReturn(Optional.empty()).when(securityQuestionDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.update(to)); + } + + @Test + void update() { + SecurityQuestionTO to = new SecurityQuestionTO(); + to.setKey("key"); + SecurityQuestion securityQuestion = mock(SecurityQuestion.class); + + doReturn(Optional.of(securityQuestion)).when(securityQuestionDAO).findById("key"); + when(securityQuestionDAO.save(securityQuestion)).thenReturn(securityQuestion); + when(binder.getSecurityQuestionTO(securityQuestion)).thenReturn(to); + + assertSame(to, logic.update(to)); + verify(binder).update(securityQuestion, to); + } + + @Test + void deleteMissing() { + doReturn(Optional.empty()).when(securityQuestionDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.delete("missing")); + } + + @Test + void delete() { + SecurityQuestion securityQuestion = mock(SecurityQuestion.class); + SecurityQuestionTO to = new SecurityQuestionTO(); + to.setKey("key"); + + doReturn(Optional.of(securityQuestion)).when(securityQuestionDAO).findById("key"); + when(binder.getSecurityQuestionTO(securityQuestion)).thenReturn(to); + + assertSame(to, logic.delete("key")); + verify(securityQuestionDAO).deleteById("key"); + } + + @Test + void readByUserWithoutUsername() { + assertThrows(NotFoundException.class, () -> logic.readByUser(null)); + } + + @Test + void readByUserMissing() { + doReturn(Optional.empty()).when(userDAO).findByUsername("missing"); + + assertThrows(NotFoundException.class, () -> logic.readByUser("missing")); + } + + @Test + void readByUserWithoutSecurityQuestion() { + User user = mock(User.class); + doReturn(Optional.of(user)).when(userDAO).findByUsername("user"); + + assertThrows(NotFoundException.class, () -> logic.readByUser("user")); + } + + @Test + void readByUser() { + User user = mock(User.class); + SecurityQuestion securityQuestion = mock(SecurityQuestion.class); + SecurityQuestionTO to = new SecurityQuestionTO(); + to.setKey("key"); + + doReturn(Optional.of(user)).when(userDAO).findByUsername("user"); + when(user.getSecurityQuestion()).thenReturn(securityQuestion); + when(binder.getSecurityQuestionTO(securityQuestion)).thenReturn(to); + + assertSame(to, logic.readByUser("user")); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/SyncopeLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/SyncopeLogicTest.java new file mode 100644 index 00000000000..8cfc7258978 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/SyncopeLogicTest.java @@ -0,0 +1,156 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.OutputStream; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.content.ContentExporter; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; +import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; +import org.apache.syncope.core.persistence.api.dao.search.SearchCond; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.provisioning.api.data.GroupDataBinder; +import org.apache.syncope.core.provisioning.api.data.RelationshipTypeDataBinder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Pageable; + +class SyncopeLogicTest { + + private RealmSearchDAO realmSearchDAO; + + private AnyTypeDAO anyTypeDAO; + + private GroupDAO groupDAO; + + private RelationshipTypeDAO relationshipTypeDAO; + + private AnySearchDAO anySearchDAO; + + private GroupDataBinder groupDataBinder; + + private RelationshipTypeDataBinder relationshipTypeDataBinder; + + private ContentExporter exporter; + + private SyncopeLogic logic; + + @BeforeEach + void setUp() { + realmSearchDAO = mock(RealmSearchDAO.class); + anyTypeDAO = mock(AnyTypeDAO.class); + groupDAO = mock(GroupDAO.class); + relationshipTypeDAO = mock(RelationshipTypeDAO.class); + anySearchDAO = mock(AnySearchDAO.class); + groupDataBinder = mock(GroupDataBinder.class); + relationshipTypeDataBinder = mock(RelationshipTypeDataBinder.class); + exporter = mock(ContentExporter.class); + + logic = new SyncopeLogic( + realmSearchDAO, + anyTypeDAO, + groupDAO, + relationshipTypeDAO, + anySearchDAO, + groupDataBinder, + relationshipTypeDataBinder, + exporter); + } + + @Test + void searchAssignableGroupsMissingRealm() { + doReturn(Optional.empty()).when(realmSearchDAO).findByFullPath("/missing"); + + assertThrows(NotFoundException.class, + () -> logic.searchAssignableGroups("/missing", null, Pageable.unpaged())); + } + + @Test + void searchAssignableGroups() { + Realm realm = mock(Realm.class); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(anySearchDAO.count(eq(realm), eq(true), any(), any(SearchCond.class), eq(AnyTypeKind.GROUP))). + thenReturn(0L); + when(anySearchDAO.search( + eq(realm), eq(true), any(), any(SearchCond.class), any(Pageable.class), eq(AnyTypeKind.GROUP))). + thenReturn(List.of()); + + assertEquals(0, logic.searchAssignableGroups( + SyncopeConstants.ROOT_REALM, "*", Pageable.unpaged()).getTotalElements()); + } + + @Test + void readUserGroupTypeExtensionMissingGroup() { + doReturn(Optional.empty()).when(groupDAO).findByName("missing"); + + assertThrows(NotFoundException.class, () -> logic.readUserGroupTypeExtension("missing")); + } + + @Test + void readUserRelationshipTypeExtensionMissing() { + doReturn(Optional.empty()).when(relationshipTypeDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.readUserRelationshipTypeExtension("missing")); + } + + @Test + void exportInternalStorageContent() throws Exception { + OutputStream os = mock(OutputStream.class); + doThrow(new IllegalStateException("test")).when(exporter). + export("Master", 1, os, new String[] { "element" }); + + logic.exportInternalStorageContent(1, os, List.of("element")); + + verify(exporter).export("Master", 1, os, new String[] { "element" }); + } + + @Test + void exportInternalStorageContentWithThreshold() throws Exception { + OutputStream os = mock(OutputStream.class); + + logic.exportInternalStorageContent(0, os, List.of()); + + verify(exporter).export(eq("Master"), anyInt(), eq(os), any(String[].class)); + } + + @Test + void resolveReference() { + assertThrows(UnresolvedReferenceException.class, () -> logic.resolveBeanReference( + SyncopeLogic.class.getMethod("searchAssignableGroups", String.class, String.class, Pageable.class), + SyncopeConstants.ROOT_REALM, null, Pageable.unpaged())); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/TaskLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/TaskLogicTest.java new file mode 100644 index 00000000000..5b264548787 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/TaskLogicTest.java @@ -0,0 +1,535 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.to.ExecTO; +import org.apache.syncope.common.lib.to.JobTO; +import org.apache.syncope.common.lib.to.MacroTaskTO; +import org.apache.syncope.common.lib.to.PropagationStatus; +import org.apache.syncope.common.lib.to.PropagationTaskTO; +import org.apache.syncope.common.lib.to.SchedTaskTO; +import org.apache.syncope.common.lib.to.TaskTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.common.lib.types.JobAction; +import org.apache.syncope.common.lib.types.JobType; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.common.lib.types.TaskType; +import org.apache.syncope.common.rest.api.beans.ExecSpecs; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.JobStatusDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.NotificationDAO; +import org.apache.syncope.core.persistence.api.dao.TaskDAO; +import org.apache.syncope.core.persistence.api.dao.TaskExecDAO; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.task.MacroTask; +import org.apache.syncope.core.persistence.api.entity.task.NotificationTask; +import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.apache.syncope.core.persistence.api.entity.task.TaskUtils; +import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory; +import org.apache.syncope.core.provisioning.api.data.TaskDataBinder; +import org.apache.syncope.core.provisioning.api.job.JobManager; +import org.apache.syncope.core.provisioning.api.notification.NotificationJobDelegate; +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor; +import org.apache.syncope.core.provisioning.java.job.SyncopeTaskScheduler; +import org.apache.syncope.core.spring.security.DelegatedAdministrationException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Pageable; + +class TaskLogicTest { + + private static final String UUID = "11111111-1111-1111-1111-111111111111"; + + private JobManager jobManager; + + private SyncopeTaskScheduler scheduler; + + private JobStatusDAO jobStatusDAO; + + private TaskDAO taskDAO; + + private TaskExecDAO taskExecDAO; + + private ExternalResourceDAO resourceDAO; + + private NotificationDAO notificationDAO; + + private TaskDataBinder binder; + + private PropagationTaskExecutor taskExecutor; + + private NotificationJobDelegate notificationJobDelegate; + + private TaskUtilsFactory taskUtilsFactory; + + private TaskLogic logic; + + @BeforeEach + void setUp() { + jobManager = mock(JobManager.class); + scheduler = mock(SyncopeTaskScheduler.class); + jobStatusDAO = mock(JobStatusDAO.class); + taskDAO = mock(TaskDAO.class); + taskExecDAO = mock(TaskExecDAO.class); + resourceDAO = mock(ExternalResourceDAO.class); + notificationDAO = mock(NotificationDAO.class); + binder = mock(TaskDataBinder.class); + taskExecutor = mock(PropagationTaskExecutor.class); + notificationJobDelegate = mock(NotificationJobDelegate.class); + taskUtilsFactory = mock(TaskUtilsFactory.class); + + logic = new TaskLogic( + jobManager, + scheduler, + jobStatusDAO, + taskDAO, + taskExecDAO, + resourceDAO, + notificationDAO, + binder, + taskExecutor, + notificationJobDelegate, + taskUtilsFactory); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void searchMissingResource() { + doReturn(Optional.empty()).when(resourceDAO).findById("missing"); + + assertThrows(SyncopeClientException.class, () -> logic.search( + TaskType.PROPAGATION, "missing", null, null, null, Pageable.unpaged(), false)); + } + + @Test + void search() { + Pageable pageable = Pageable.unpaged(); + when(taskDAO.count(TaskType.PROPAGATION, null, null, null, null)).thenReturn(0L); + when(taskDAO.findAll( + TaskType.PROPAGATION, null, null, null, null, pageable)).thenReturn(List.of()); + + assertEquals(0, logic.search( + TaskType.PROPAGATION, null, null, null, null, pageable, false).getTotalElements()); + } + + @Test + void readMissing() { + doReturn(Optional.empty()).when(taskDAO).findById(TaskType.PROPAGATION, "missing"); + + assertThrows(NotFoundException.class, () -> logic.read(TaskType.PROPAGATION, "missing", false)); + } + + @Test + void readWithWrongType() { + PropagationTask task = mock(PropagationTask.class); + TaskUtils taskUtils = mock(TaskUtils.class); + when(taskUtils.getType()).thenReturn(TaskType.NOTIFICATION); + doReturn(Optional.of(task)).when(taskDAO).findById(TaskType.PROPAGATION, "key"); + when(taskUtilsFactory.getInstance(task)).thenReturn(taskUtils); + + assertThrows(SyncopeClientException.class, () -> logic.read(TaskType.PROPAGATION, "key", false)); + } + + @Test + void readTask() { + PropagationTask task = mock(PropagationTask.class); + TaskUtils taskUtils = mock(TaskUtils.class); + PropagationTaskTO to = new PropagationTaskTO(); + to.setKey("key"); + when(taskUtils.getType()).thenReturn(TaskType.PROPAGATION); + doReturn(Optional.of(task)).when(taskDAO).findById(TaskType.PROPAGATION, "key"); + when(taskUtilsFactory.getInstance(task)).thenReturn(taskUtils); + doReturn(to).when(binder).getTaskTO(task, taskUtils, false); + + assertSame(to, logic.read(TaskType.PROPAGATION, "key", false)); + } + + @Test + void readMacroTaskWithoutAuthorization() { + MacroTask task = mock(MacroTask.class); + Realm realm = mock(Realm.class); + when(realm.getFullPath()).thenReturn("/secret"); + when(task.getRealm()).thenReturn(realm); + + TaskUtils taskUtils = mock(TaskUtils.class); + when(taskUtils.getType()).thenReturn(TaskType.MACRO); + doReturn(Optional.of(task)).when(taskDAO).findById(TaskType.MACRO, "key"); + when(taskUtilsFactory.getInstance(task)).thenReturn(taskUtils); + + assertThrows(DelegatedAdministrationException.class, + () -> logic.read(TaskType.MACRO, "key", false)); + } + + @Test + void readMacroTask() { + LogicTestAuth.authenticate("admin", "Master", + Map.of(IdRepoEntitlement.TASK_READ, Set.of(SyncopeConstants.ROOT_REALM))); + + MacroTask task = mock(MacroTask.class); + Realm realm = mock(Realm.class); + when(realm.getFullPath()).thenReturn(SyncopeConstants.ROOT_REALM); + when(task.getRealm()).thenReturn(realm); + + TaskUtils taskUtils = mock(TaskUtils.class); + when(taskUtils.getType()).thenReturn(TaskType.MACRO); + MacroTaskTO to = new MacroTaskTO(); + to.setKey("key"); + + doReturn(Optional.of(task)).when(taskDAO).findById(TaskType.MACRO, "key"); + when(taskUtilsFactory.getInstance(task)).thenReturn(taskUtils); + doReturn(to).when(binder).getTaskTO(task, taskUtils, false); + + assertSame(to, logic.read(TaskType.MACRO, "key", false)); + } + + @Test + void createSchedTaskWithWrongType() { + SchedTaskTO to = new SchedTaskTO(); + to.setKey("key"); + + TaskUtils taskUtils = mock(TaskUtils.class); + when(taskUtils.getType()).thenReturn(TaskType.PUSH); + when(taskUtilsFactory.getInstance(to)).thenReturn(taskUtils); + + assertThrows(SyncopeClientException.class, () -> logic.createSchedTask(TaskType.SCHEDULED, to)); + } + + @Test + void createSchedTask() { + SchedTaskTO to = new SchedTaskTO(); + to.setKey("key"); + + TaskUtils taskUtils = mock(TaskUtils.class); + when(taskUtils.getType()).thenReturn(TaskType.SCHEDULED); + when(taskUtilsFactory.getInstance(to)).thenReturn(taskUtils); + + SchedTask task = mock(SchedTask.class); + when(task.getKey()).thenReturn("key"); + when(binder.createSchedTask(to, taskUtils)).thenReturn(task); + when(taskDAO.save(task)).thenReturn(task); + doReturn(to).when(binder).getTaskTO(task, taskUtils, false); + + assertSame(to, logic.createSchedTask(TaskType.SCHEDULED, to)); + verify(jobManager).register(task, "unauthenticated"); + } + + @Test + void updateSchedTaskMissing() { + SchedTaskTO to = new SchedTaskTO(); + to.setKey("missing"); + doReturn(Optional.empty()).when(taskDAO).findById(TaskType.SCHEDULED, "missing"); + + assertThrows(NotFoundException.class, () -> logic.updateSchedTask(TaskType.SCHEDULED, to)); + } + + @Test + void updateSchedTask() { + SchedTaskTO to = new SchedTaskTO(); + to.setKey("key"); + + SchedTask task = mock(SchedTask.class); + when(task.isActive()).thenReturn(false); + TaskUtils taskUtils = mock(TaskUtils.class); + when(taskUtils.getType()).thenReturn(TaskType.SCHEDULED); + doReturn(Optional.of(task)).when(taskDAO).findById(TaskType.SCHEDULED, "key"); + when(taskUtilsFactory.getInstance(task)).thenReturn(taskUtils); + when(taskDAO.save(task)).thenReturn(task); + doReturn(to).when(binder).getTaskTO(task, taskUtils, false); + + assertSame(to, logic.updateSchedTask(TaskType.SCHEDULED, to)); + verify(jobManager).unregister(task); + } + + @Test + void executeMissing() { + doReturn(Optional.empty()).when(taskDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.execute(new ExecSpecs.Builder().key("missing").build())); + } + + @Test + void executeInThePast() { + SchedTask task = mock(SchedTask.class); + doReturn(Optional.of(task)).when(taskDAO).findById("key"); + + ExecSpecs specs = new ExecSpecs.Builder().key("key"). + startAt(OffsetDateTime.now().minusDays(1)).build(); + + assertThrows(SyncopeClientException.class, () -> logic.execute(specs)); + } + + @Test + void executePropagation() { + PropagationTask task = mock(PropagationTask.class); + when(task.getKey()).thenReturn("key"); + when(task.getObjectClassName()).thenReturn("__ACCOUNT__"); + when(task.getResource()).thenReturn(mock(ExternalResource.class)); + when(task.getOperation()).thenReturn(ResourceOperation.CREATE); + when(task.getAnyTypeKind()).thenReturn(AnyTypeKind.USER); + when(task.getConnObjectKey()).thenReturn("connObjectKey"); + + TaskUtils taskUtils = mock(TaskUtils.class); + when(taskUtils.getType()).thenReturn(TaskType.PROPAGATION); + doReturn(Optional.of(task)).when(taskDAO).findById("key"); + when(taskUtilsFactory.getInstance(task)).thenReturn(taskUtils); + + @SuppressWarnings("unchecked") + TaskExec exec = mock(TaskExec.class); + ExecTO execTO = new ExecTO(); + when(taskExecutor.execute(any(), any(), eq("unauthenticated"))).thenReturn(exec); + when(binder.getExecTO(exec)).thenReturn(execTO); + + assertSame(execTO, logic.execute(new ExecSpecs.Builder().key("key").build())); + } + + @Test + void executeNotification() { + NotificationTask task = mock(NotificationTask.class); + TaskUtils taskUtils = mock(TaskUtils.class); + when(taskUtils.getType()).thenReturn(TaskType.NOTIFICATION); + doReturn(Optional.of(task)).when(taskDAO).findById("key"); + when(taskUtilsFactory.getInstance(task)).thenReturn(taskUtils); + + @SuppressWarnings("unchecked") + TaskExec exec = mock(TaskExec.class); + ExecTO execTO = new ExecTO(); + when(notificationJobDelegate.executeSingle(task, "unauthenticated")).thenReturn(exec); + when(binder.getExecTO(exec)).thenReturn(execTO); + + assertSame(execTO, logic.execute(new ExecSpecs.Builder().key("key").build())); + } + + @Test + void executeInactiveScheduled() { + SchedTask task = mock(SchedTask.class); + when(task.getKey()).thenReturn("key"); + when(task.isActive()).thenReturn(false); + TaskUtils taskUtils = mock(TaskUtils.class); + when(taskUtils.getType()).thenReturn(TaskType.SCHEDULED); + doReturn(Optional.of(task)).when(taskDAO).findById("key"); + when(taskUtilsFactory.getInstance(task)).thenReturn(taskUtils); + + assertThrows(SyncopeClientException.class, () -> logic.execute(new ExecSpecs.Builder().key("key").build())); + } + + @Test + void executeScheduled() { + SchedTask task = mock(SchedTask.class); + when(task.getKey()).thenReturn("key"); + when(task.isActive()).thenReturn(true); + TaskUtils taskUtils = mock(TaskUtils.class); + when(taskUtils.getType()).thenReturn(TaskType.SCHEDULED); + doReturn(Optional.of(task)).when(taskDAO).findById("key"); + when(taskUtilsFactory.getInstance(task)).thenReturn(taskUtils); + when(binder.buildRefDesc(task)).thenReturn("desc"); + + ExecTO execTO = logic.execute(new ExecSpecs.Builder().key("key").build()); + + assertEquals(JobType.TASK, execTO.getJobType()); + assertEquals("key", execTO.getRefKey()); + assertEquals("desc", execTO.getRefDesc()); + assertEquals(JobStatusDAO.JOB_FIRED_STATUS, execTO.getStatus()); + verify(jobManager).execute(eq(task), any(), eq("unauthenticated"), eq(false), any()); + } + + @Test + void deleteMissing() { + doReturn(Optional.empty()).when(taskDAO).findById(TaskType.SCHEDULED, "missing"); + + assertThrows(NotFoundException.class, () -> logic.delete(TaskType.SCHEDULED, "missing")); + } + + @Test + void deleteScheduled() { + SchedTask task = mock(SchedTask.class); + TaskUtils taskUtils = mock(TaskUtils.class); + when(taskUtils.getType()).thenReturn(TaskType.SCHEDULED); + doReturn(Optional.of(task)).when(taskDAO).findById(TaskType.SCHEDULED, "key"); + when(taskUtilsFactory.getInstance(task)).thenReturn(taskUtils); + + TaskTO to = new SchedTaskTO(); + to.setKey("key"); + doReturn(to).when(binder).getTaskTO(task, taskUtils, true); + + assertSame(to, logic.delete(TaskType.SCHEDULED, "key")); + verify(jobManager).unregister(task); + verify(taskDAO).delete(task); + } + + @Test + void listExecutions() { + SchedTask task = mock(SchedTask.class); + doReturn(Optional.of(task)).when(taskDAO).findById("key"); + + Pageable pageable = Pageable.unpaged(); + @SuppressWarnings("unchecked") + TaskExec exec = mock(TaskExec.class); + ExecTO execTO = new ExecTO(); + when(taskExecDAO.count(task, null, null)).thenReturn(1L); + when(taskExecDAO.findAll(task, null, null, pageable)).thenReturn(List.of(exec)); + when(binder.getExecTO(exec)).thenReturn(execTO); + + assertEquals(1, logic.listExecutions("key", null, null, pageable).getTotalElements()); + assertSame(execTO, logic.listExecutions("key", null, null, pageable).getContent().getFirst()); + } + + @Test + void deleteExecutionMissing() { + doReturn(Optional.empty()).when(taskExecDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.deleteExecution("missing")); + } + + @Test + void deleteExecution() { + @SuppressWarnings("unchecked") + TaskExec exec = mock(TaskExec.class); + ExecTO execTO = new ExecTO(); + doReturn(Optional.of(exec)).when(taskExecDAO).findById("execKey"); + when(binder.getExecTO(exec)).thenReturn(execTO); + + assertSame(execTO, logic.deleteExecution("execKey")); + verify(taskExecDAO).delete(exec); + } + + @Test + void deleteExecutions() { + SchedTask task = mock(SchedTask.class); + doReturn(Optional.of(task)).when(taskDAO).findById("key"); + + @SuppressWarnings("unchecked") + TaskExec exec = mock(TaskExec.class); + when(exec.getKey()).thenReturn("execKey"); + when(taskExecDAO.findAll(task, null, null, Pageable.unpaged())).thenReturn(List.of(exec)); + + assertEquals(1, logic.deleteExecutions("key", null, null).size()); + verify(taskExecDAO).delete(exec); + } + + @Test + void getMacroTaskFormMissing() { + doReturn(Optional.empty()).when(taskDAO).findById(TaskType.MACRO, "missing"); + + assertThrows(NotFoundException.class, + () -> logic.getMacroTaskForm("missing", java.util.Locale.ENGLISH)); + } + + @Test + void getJobMissingTask() { + doReturn(Optional.empty()).when(taskDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.getJob("missing")); + } + + @Test + void getJob() { + SchedTask task = mock(SchedTask.class); + when(task.getKey()).thenReturn(UUID); + doReturn(Optional.of(task)).when(taskDAO).findById("key"); + doReturn(Optional.of(task)).when(taskDAO).findById(UUID); + when(binder.buildRefDesc(task)).thenReturn("desc"); + when(scheduler.getNextTrigger("Master", "taskJob" + UUID)).thenReturn(Optional.empty()); + when(jobManager.isRunning("taskJob" + UUID)).thenReturn(false); + when(jobStatusDAO.get("taskJob" + UUID)).thenReturn("status"); + + JobTO job = logic.getJob("key"); + + assertEquals(JobType.TASK, job.getType()); + assertEquals(UUID, job.getRefKey()); + assertEquals("desc", job.getRefDesc()); + assertEquals("status", job.getStatus()); + } + + @Test + void actionJob() { + SchedTask task = mock(SchedTask.class); + when(task.getKey()).thenReturn(UUID); + doReturn(Optional.of(task)).when(taskDAO).findById("key"); + + logic.actionJob("key", JobAction.START); + + verify(scheduler).start("Master", "taskJob" + UUID); + } + + @Test + void listJobs() { + when(scheduler.getJobNames("Master")).thenReturn(List.of()); + + assertEquals(List.of(), logic.listJobs()); + } + + @Test + void purgePropagations() { + List statuses = List.of(); + when(taskDAO.purgePropagations(null, null, null)).thenReturn(List.of()); + + assertEquals(statuses, logic.purgePropagations(null, null, null)); + } + + @Test + void resolveReference() throws Exception { + SchedTask task = mock(SchedTask.class); + TaskUtils taskUtils = mock(TaskUtils.class); + SchedTaskTO to = new SchedTaskTO(); + to.setKey("key"); + + doReturn(Optional.of(task)).when(taskDAO).findById("key"); + when(taskUtilsFactory.getInstance(task)).thenReturn(taskUtils); + doReturn(to).when(binder).getTaskTO(task, taskUtils, false); + + assertSame(to, logic.resolveReference( + TaskLogic.class.getMethod("read", TaskType.class, String.class, boolean.class), + TaskType.SCHEDULED, + "key", + false)); + } + + @Test + void resolveUnresolvedReference() throws Exception { + assertThrows(UnresolvedReferenceException.class, () -> logic.resolveReference( + TaskLogic.class.getMethod("listRecentExecutions", int.class), 10)); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UnresolvedReferenceExceptionTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UnresolvedReferenceExceptionTest.java new file mode 100644 index 00000000000..9769f46e6fe --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UnresolvedReferenceExceptionTest.java @@ -0,0 +1,43 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class UnresolvedReferenceExceptionTest { + + @Test + void noCause() { + assertNull(new UnresolvedReferenceException().getCause()); + } + + @Test + void withCause() { + IllegalStateException cause = new IllegalStateException("failure"); + + assertSame(cause, new UnresolvedReferenceException(cause).getCause()); + assertThrows(UnresolvedReferenceException.class, () -> { + throw new UnresolvedReferenceException(cause); + }); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UserLogicOpTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UserLogicOpTest.java new file mode 100644 index 00000000000..b8060edf607 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UserLogicOpTest.java @@ -0,0 +1,39 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.syncope.common.lib.to.ProvisioningResult; +import org.junit.jupiter.api.Test; + +class UserLogicOpTest { + + @Test + void contract() throws Exception { + assertTrue(UserLogicOp.class.isInterface()); + assertTrue(AnyCRUDLogicOp.class.isAssignableFrom(UserLogicOp.class)); + assertEquals(ProvisioningResult.class, UserLogicOp.class.getMethod( + "status", org.apache.syncope.common.lib.request.StatusR.class, boolean.class). + getReturnType()); + assertEquals(void.class, UserLogicOp.class.getMethod( + "verifySecurityAnswer", String.class, String.class).getReturnType()); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UserLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UserLogicTest.java new file mode 100644 index 00000000000..591a60cbcf6 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UserLogicTest.java @@ -0,0 +1,233 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.keymaster.client.api.ConfParamOps; +import org.apache.syncope.common.keymaster.client.api.StandardConfParams; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.request.UserCR; +import org.apache.syncope.common.lib.request.UserUR; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.core.persistence.api.EncryptorManager; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.dao.search.SearchCond; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.provisioning.api.ProvisioningManager; +import org.apache.syncope.core.provisioning.api.UserProvisioningManager; +import org.apache.syncope.core.provisioning.api.data.UserDataBinder; +import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Pageable; + +class UserLogicTest { + + private RealmSearchDAO realmSearchDAO; + + private AnyTypeDAO anyTypeDAO; + + private UserDAO userDAO; + + private UserDataBinder binder; + + private UserProvisioningManager provisioningManager; + + private ConfParamOps confParamOps; + + private AnySearchDAO searchDAO; + + private UserLogic logic; + + @BeforeEach + void setUp() { + realmSearchDAO = mock(RealmSearchDAO.class); + anyTypeDAO = mock(AnyTypeDAO.class); + userDAO = mock(UserDAO.class); + binder = mock(UserDataBinder.class); + provisioningManager = mock(UserProvisioningManager.class); + confParamOps = mock(ConfParamOps.class); + searchDAO = mock(AnySearchDAO.class); + + logic = new UserLogic( + realmSearchDAO, + anyTypeDAO, + mock(TemplateUtils.class), + userDAO, + binder, + provisioningManager, + mock(EncryptorManager.class), + confParamOps, + mock(GroupDAO.class), + searchDAO); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void read() { + UserTO to = new UserTO(); + to.setKey("key"); + when(binder.getUserTO("key")).thenReturn(to); + + assertSame(to, logic.read("key")); + } + + @Test + void searchMissingRealm() { + doReturn(Optional.empty()).when(realmSearchDAO).findByFullPath("/missing"); + + assertThrows(NotFoundException.class, + () -> logic.search(null, Pageable.unpaged(), "/missing", true, false)); + } + + @Test + void search() { + Realm realm = mock(Realm.class); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(searchDAO.getAllMatchingCond()).thenReturn(mock(SearchCond.class)); + when(searchDAO.count(eq(realm), eq(true), any(), any(SearchCond.class), any())). + thenReturn(0L); + when(searchDAO.search( + eq(realm), eq(true), any(), any(SearchCond.class), any(Pageable.class), any())). + thenReturn(List.of()); + + assertEquals(0, logic.search(null, Pageable.unpaged(), + SyncopeConstants.ROOT_REALM, true, false).getTotalElements()); + } + + @Test + void create() { + UserCR createReq = new UserCR.Builder(SyncopeConstants.ROOT_REALM, "user").build(); + Realm realm = mock(Realm.class); + AnyType anyType = mock(AnyType.class); + UserTO to = new UserTO(); + to.setKey("key"); + + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(anyTypeDAO.getUser()).thenReturn(anyType); + when(provisioningManager.create(createReq, false, "unauthenticated", "REST")). + thenReturn(new ProvisioningManager.ProvisioningResult<>("key", List.of())); + when(binder.getUserTO("key")).thenReturn(to); + + assertSame(to, logic.create(createReq, false).getEntity()); + } + + @Test + void update() { + UserUR updateReq = new UserUR.Builder("key").build(); + Realm realm = mock(Realm.class); + UserTO userTO = new UserTO(); + userTO.setKey("key"); + userTO.setRealm(SyncopeConstants.ROOT_REALM); + UserTO updated = new UserTO(); + updated.setKey("key"); + + when(binder.getUserTO("key")).thenReturn(userTO, updated); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(provisioningManager.update(updateReq, false, "unauthenticated", "REST")). + thenReturn(new ProvisioningManager.ProvisioningResult<>(updateReq, List.of())); + + assertSame(updated, logic.update(updateReq, false).getEntity()); + } + + @Test + void delete() { + Realm realm = mock(Realm.class); + UserTO userTO = new UserTO(); + userTO.setKey("key"); + userTO.setRealm(SyncopeConstants.ROOT_REALM); + + when(binder.getUserTO("key")).thenReturn(userTO); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(userDAO.isManager("key")).thenReturn(false); + when(provisioningManager.delete("key", false, "unauthenticated", "REST")).thenReturn(List.of()); + when(userDAO.existsById("key")).thenReturn(true); + + assertSame(userTO, logic.delete("key", false).getEntity()); + } + + @Test + void deleteManager() { + Realm realm = mock(Realm.class); + UserTO userTO = new UserTO(); + userTO.setKey("key"); + userTO.setRealm(SyncopeConstants.ROOT_REALM); + + when(binder.getUserTO("key")).thenReturn(userTO); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(userDAO.isManager("key")).thenReturn(true); + + assertThrows(SyncopeClientException.class, () -> logic.delete("key", false)); + } + + @Test + void unlink() { + Realm realm = mock(Realm.class); + UserTO userTO = new UserTO(); + userTO.setKey("key"); + userTO.setRealm(SyncopeConstants.ROOT_REALM); + UserTO unlinked = new UserTO(); + unlinked.setKey("key"); + + when(binder.getUserTO("key")).thenReturn(userTO, unlinked); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(provisioningManager.unlink(any(UserUR.class), eq("unauthenticated"), eq("REST"))).thenReturn("key"); + + assertSame(unlinked, logic.unlink("key", List.of("resource"))); + } + + @Test + void verifySecurityAnswerForbidden() { + when(confParamOps.get( + "Master", StandardConfParams.PASSWORD_RESET_ALLOWED, false, boolean.class)).thenReturn(false); + + assertThrows(SyncopeClientException.class, () -> logic.verifySecurityAnswer("user", "answer")); + } + + @Test + void verifySecurityAnswerForMissingUser() { + when(confParamOps.get( + "Master", StandardConfParams.PASSWORD_RESET_ALLOWED, false, boolean.class)).thenReturn(true); + doReturn(Optional.empty()).when(userDAO).findByUsername("missing"); + + assertThrows(NotFoundException.class, () -> logic.verifySecurityAnswer("missing", "answer")); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UserSelfLogicPasswordResetTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UserSelfLogicPasswordResetTest.java index 94d9d979de2..4f876bf667a 100644 --- a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UserSelfLogicPasswordResetTest.java +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UserSelfLogicPasswordResetTest.java @@ -48,7 +48,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -public class UserSelfLogicPasswordResetTest { +class UserSelfLogicPasswordResetTest { private static final Cache CACHE = Caching.getCachingProvider().getCacheManager().createCache( diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UserSelfLogicTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UserSelfLogicTest.java new file mode 100644 index 00000000000..808863d72d7 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UserSelfLogicTest.java @@ -0,0 +1,394 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import javax.cache.Cache; +import org.apache.syncope.common.keymaster.client.api.ConfParamOps; +import org.apache.syncope.common.keymaster.client.api.StandardConfParams; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.request.PasswordPatch; +import org.apache.syncope.common.lib.request.StatusR; +import org.apache.syncope.common.lib.request.UserCR; +import org.apache.syncope.common.lib.request.UserUR; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.common.lib.types.StatusRType; +import org.apache.syncope.common.rest.api.beans.ComplianceQuery; +import org.apache.syncope.core.persistence.api.EncryptorManager; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.dao.DelegationDAO; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.ProvisioningManager; +import org.apache.syncope.core.provisioning.api.UserProvisioningManager; +import org.apache.syncope.core.provisioning.api.data.UserDataBinder; +import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils; +import org.apache.syncope.core.provisioning.api.rules.RuleProvider; +import org.apache.syncope.core.spring.policy.PasswordPolicyException; +import org.apache.syncope.core.spring.security.SecurityProperties; +import org.apache.syncope.core.spring.security.throttle.ThrottlerAttempts; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class UserSelfLogicTest { + + private RealmSearchDAO realmSearchDAO; + + private AnyTypeDAO anyTypeDAO; + + private UserDAO userDAO; + + private UserDataBinder binder; + + private UserProvisioningManager provisioningManager; + + private ConfParamOps confParamOps; + + private DelegationDAO delegationDAO; + + private AccessTokenDAO accessTokenDAO; + + private ExternalResourceDAO resourceDAO; + + private RuleProvider ruleProvider; + + private SecurityProperties securityProperties; + + private UserSelfLogic logic; + + @SuppressWarnings("unchecked") + @BeforeEach + void setUp() { + realmSearchDAO = mock(RealmSearchDAO.class); + anyTypeDAO = mock(AnyTypeDAO.class); + userDAO = mock(UserDAO.class); + binder = mock(UserDataBinder.class); + provisioningManager = mock(UserProvisioningManager.class); + confParamOps = mock(ConfParamOps.class); + delegationDAO = mock(DelegationDAO.class); + accessTokenDAO = mock(AccessTokenDAO.class); + resourceDAO = mock(ExternalResourceDAO.class); + ruleProvider = mock(RuleProvider.class); + securityProperties = new SecurityProperties(); + securityProperties.setAnonymousUser("anonymous"); + securityProperties.setProductionMode(false); + + Cache cache = mock(Cache.class); + + logic = new UserSelfLogic( + realmSearchDAO, + anyTypeDAO, + mock(TemplateUtils.class), + userDAO, + binder, + provisioningManager, + mock(EncryptorManager.class), + confParamOps, + delegationDAO, + accessTokenDAO, + resourceDAO, + ruleProvider, + securityProperties, + cache); + } + + @AfterEach + void tearDown() { + LogicTestAuth.clear(); + } + + @Test + void createForbidden() { + when(confParamOps.get(any(), eq(StandardConfParams.SELF_REGISTRATION_ALLOWED), eq(false), eq(boolean.class))). + thenReturn(false); + + UserCR userCR = new UserCR.Builder(SyncopeConstants.ROOT_REALM, "user").build(); + + assertThrows(SyncopeClientException.class, () -> logic.create(userCR, false)); + } + + @Test + void updateDeletesTokenWhenStatusNotAuthenticated() { + LogicTestAuth.authenticate("user", "Master", + Map.of(IdRepoEntitlement.USER_UPDATE, Set.of(SyncopeConstants.ROOT_REALM))); + + UserTO userTO = new UserTO(); + userTO.setKey("key"); + userTO.setUsername("user"); + userTO.setStatus("ACTIVE"); + userTO.setRealm(SyncopeConstants.ROOT_REALM); + Realm realm = mock(Realm.class); + + UserUR userUR = new UserUR.Builder("key").build(); + UserTO output = new UserTO(); + output.setKey("key"); + output.setUsername("user"); + output.setStatus("SUSPENDED"); + + when(binder.getAuthenticatedUserTO()).thenReturn(userTO); + doReturn(userTO).doReturn(output).when(binder).getUserTO("key"); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(realm.getActions()).thenReturn(List.of()); + when(provisioningManager.update(eq(userUR), anyBoolean(), eq("user"), eq("REST"))). + thenReturn(new ProvisioningManager.ProvisioningResult<>(userUR, List.of())); + when(confParamOps.get(any(), eq(StandardConfParams.AUTHENTICATION_STATUSES), any(), eq(String[].class))). + thenReturn(new String[] { "ACTIVE" }); + + logic.update(userUR, false); + + verify(accessTokenDAO).deleteByOwner("user"); + } + + @Test + void updateKeepsTokenWhenStatusAuthenticated() { + LogicTestAuth.authenticate("user", "Master", + Map.of(IdRepoEntitlement.USER_UPDATE, Set.of(SyncopeConstants.ROOT_REALM))); + + UserTO userTO = new UserTO(); + userTO.setKey("key"); + userTO.setUsername("user"); + userTO.setStatus("ACTIVE"); + userTO.setRealm(SyncopeConstants.ROOT_REALM); + Realm realm = mock(Realm.class); + + UserUR userUR = new UserUR.Builder("key").build(); + UserTO output = new UserTO(); + output.setKey("key"); + output.setUsername("user"); + output.setStatus("ACTIVE"); + + when(binder.getAuthenticatedUserTO()).thenReturn(userTO); + doReturn(userTO).doReturn(output).when(binder).getUserTO("key"); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(realm.getActions()).thenReturn(List.of()); + when(provisioningManager.update(eq(userUR), anyBoolean(), eq("user"), eq("REST"))). + thenReturn(new ProvisioningManager.ProvisioningResult<>(userUR, List.of())); + when(confParamOps.get(any(), eq(StandardConfParams.AUTHENTICATION_STATUSES), any(), eq(String[].class))). + thenReturn(new String[] { "ACTIVE" }); + + logic.update(userUR, false); + + verify(accessTokenDAO, never()).deleteByOwner(any()); + } + + @Test + void statusSetsKeyAndDelegates() { + LogicTestAuth.authenticate("user", "Master"); + + UserTO userTO = new UserTO(); + userTO.setKey("key"); + userTO.setRealm(SyncopeConstants.ROOT_REALM); + + StatusR statusR = new StatusR.Builder("old", StatusRType.SUSPEND).build(); + Realm realm = mock(Realm.class); + + when(binder.getAuthenticatedUserTO()).thenReturn(userTO); + when(binder.getUserTO("key")).thenReturn(userTO); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(realm.getActions()).thenReturn(List.of()); + when(provisioningManager.suspend(statusR, false, "user", "REST")). + thenReturn(new ProvisioningManager.ProvisioningResult<>("key", List.of())); + + logic.status(statusR, false); + + assertEquals("key", statusR.getKey()); + } + + @Test + void deleteDelegates() { + LogicTestAuth.authenticate("user", "Master"); + + UserTO userTO = new UserTO(); + userTO.setKey("key"); + userTO.setRealm(SyncopeConstants.ROOT_REALM); + Realm realm = mock(Realm.class); + + when(binder.getAuthenticatedUserTO()).thenReturn(userTO); + when(binder.getUserTO("key")).thenReturn(userTO); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(realm.getActions()).thenReturn(List.of()); + when(userDAO.isManager("key")).thenReturn(false); + when(provisioningManager.delete("key", false, "user", "REST")).thenReturn(List.of()); + when(userDAO.existsById("key")).thenReturn(false); + + logic.delete(false); + + verify(userDAO).isManager("key"); + verify(provisioningManager).delete("key", false, "user", "REST"); + } + + @Test + void mustChangePassword() { + LogicTestAuth.authenticate("user", "Master", + Map.of(IdRepoEntitlement.MUST_CHANGE_PASSWORD, Set.of(SyncopeConstants.ROOT_REALM))); + + UserTO userTO = new UserTO(); + userTO.setKey("key"); + userTO.setUsername("user"); + userTO.setRealm(SyncopeConstants.ROOT_REALM); + userTO.setStatus("ACTIVE"); + Realm realm = mock(Realm.class); + + PasswordPatch password = new PasswordPatch.Builder().build(); + when(binder.getAuthenticatedUserTO()).thenReturn(userTO); + when(userDAO.findAllResourceKeys("key")).thenReturn(List.of("res1")); + when(binder.getUserTO("key")).thenReturn(userTO); + doReturn(Optional.of(realm)).when(realmSearchDAO).findByFullPath(SyncopeConstants.ROOT_REALM); + when(realm.getActions()).thenReturn(List.of()); + when(provisioningManager.update(any(UserUR.class), anyBoolean(), eq("user"), eq("REST"))). + thenAnswer(invocation -> new ProvisioningManager.ProvisioningResult<>( + (UserUR) invocation.getArgument(0), List.of())); + when(binder.getUserTO("key")).thenReturn(userTO); + when(confParamOps.get(any(), eq(StandardConfParams.AUTHENTICATION_STATUSES), any(), eq(String[].class))). + thenReturn(new String[] { "ACTIVE" }); + + logic.mustChangePassword(password, false); + + verify(accessTokenDAO).deleteByOwner("user"); + } + + @Test + void readAuthenticated() { + LogicTestAuth.authenticate("user", "Master", + Map.of(IdRepoEntitlement.USER_READ, Set.of(SyncopeConstants.ROOT_REALM))); + + UserTO authenticated = new UserTO(); + authenticated.setKey("key"); + + when(binder.getAuthenticatedUserTO()).thenReturn(authenticated); + when(delegationDAO.findValidDelegating("key", OffsetDateTime.now())).thenReturn(List.of()); + + UserSelfLogic.Self self = logic.read(); + + assertNotNull(self); + assertEquals(authenticated, self.user()); + } + + @Test + void readAnonymous() { + LogicTestAuth.authenticate("anonymous", "Master"); + + UserTO authenticated = new UserTO(); + authenticated.setKey("key"); + + when(binder.getAuthenticatedUserTO()).thenReturn(authenticated); + when(delegationDAO.findValidDelegating("key", OffsetDateTime.now())).thenReturn(List.of()); + + UserSelfLogic.Self self = logic.read(); + + assertNotNull(self); + assertEquals(authenticated, self.user()); + } + + @Test + void complianceEmptyQuery() { + ComplianceQuery query = new ComplianceQuery.Builder().build(); + + assertThrows(SyncopeClientException.class, () -> logic.compliance(query)); + } + + @Test + void complianceTokenValid() { + ComplianceQuery query = new ComplianceQuery.Builder(). + token("token"). + username("user"). + build(); + + Realm realm = mock(Realm.class); + User user = mock(User.class); + + when(userDAO.findByToken("token")).thenReturn(Optional.of("key")); + when(user.getRealm()).thenReturn(realm); + when(user.getResources()).thenReturn(List.of()); + doReturn(Optional.of(user)).when(userDAO).findById("key"); + when(ruleProvider.getAccountPolicies(any(), any())).thenReturn(List.of()); + + logic.compliance(query); + } + + @Test + void complianceUsernameInvalid() { + ComplianceQuery query = new ComplianceQuery.Builder(). + token("token"). + username("user!"). + build(); + + Realm realm = mock(Realm.class); + User user = mock(User.class); + + when(userDAO.findByToken("token")).thenReturn(Optional.of("key")); + when(user.getRealm()).thenReturn(realm); + when(user.getResources()).thenReturn(List.of()); + doReturn(Optional.of(user)).when(userDAO).findById("key"); + when(ruleProvider.getAccountPolicies(any(), any())).thenReturn(List.of()); + + assertThrows(InvalidEntityException.class, () -> logic.compliance(query)); + } + + @Test + void compliancePasswordInvalid() { + ComplianceQuery query = new ComplianceQuery.Builder(). + token("token"). + password("bad"). + build(); + + Realm realm = mock(Realm.class); + User user = mock(User.class); + AccountPolicy accountPolicy = mock(AccountPolicy.class); + PasswordPolicy passwordPolicy = mock(PasswordPolicy.class); + + when(userDAO.findByToken("token")).thenReturn(Optional.of("key")); + when(user.getRealm()).thenReturn(realm); + when(user.getResources()).thenReturn(List.of()); + doReturn(Optional.of(user)).when(userDAO).findById("key"); + when(ruleProvider.getAccountPolicies(any(), any())).thenReturn(List.of(accountPolicy)); + when(ruleProvider.getAccountRules(accountPolicy)).thenReturn(List.of()); + when(ruleProvider.getPasswordPolicies(any(), any())).thenReturn(List.of(passwordPolicy)); + when(ruleProvider.getPasswordRules(passwordPolicy)). + thenThrow(new PasswordPolicyException("bad")); + + assertThrows(InvalidEntityException.class, () -> logic.compliance(query)); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/api/LogicActionsTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/api/LogicActionsTest.java new file mode 100644 index 00000000000..82bef12d0a4 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/api/LogicActionsTest.java @@ -0,0 +1,80 @@ +/* + * 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.core.logic.api; + +import static org.junit.jupiter.api.Assertions.assertSame; + +import java.util.List; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.request.UserCR; +import org.apache.syncope.common.lib.request.UserUR; +import org.apache.syncope.common.lib.to.UserTO; +import org.junit.jupiter.api.Test; + +class LogicActionsTest { + + private final LogicActions actions = new LogicActions() { + }; + + @Test + void beforeCreate() { + UserCR userCR = new UserCR.Builder(SyncopeConstants.ROOT_REALM, "user").build(); + + assertSame(userCR, actions.beforeCreate(userCR)); + } + + @Test + void afterCreate() { + UserTO userTO = new UserTO(); + userTO.setKey("key"); + + assertSame(userTO, actions.afterCreate(userTO, List.of())); + } + + @Test + void beforeUpdate() { + UserUR userUR = new UserUR.Builder("key").build(); + + assertSame(userUR, actions.beforeUpdate(userUR)); + } + + @Test + void afterUpdate() { + UserTO userTO = new UserTO(); + userTO.setKey("key"); + + assertSame(userTO, actions.afterUpdate(userTO, List.of())); + } + + @Test + void beforeDelete() { + UserTO userTO = new UserTO(); + userTO.setKey("key"); + + assertSame(userTO, actions.beforeDelete(userTO)); + } + + @Test + void afterDelete() { + UserTO userTO = new UserTO(); + userTO.setKey("key"); + + assertSame(userTO, actions.afterDelete(userTO, List.of())); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookupTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookupTest.java new file mode 100644 index 00000000000..ae837c52118 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookupTest.java @@ -0,0 +1,64 @@ +/* + * 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.core.logic.init; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.apache.syncope.common.lib.types.IdMImplementationType; +import org.apache.syncope.common.lib.types.IdRepoImplementationType; +import org.apache.syncope.common.lib.types.ImplementationTypesHolder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.core.Ordered; + +class ClassPathScanImplementationLookupTest { + + private ClassPathScanImplementationLookup lookup; + + @BeforeEach + void setUp() { + ImplementationTypesHolder.getInstance().putAll(IdRepoImplementationType.values()); + ImplementationTypesHolder.getInstance().putAll(IdMImplementationType.values()); + + lookup = new ClassPathScanImplementationLookup(); + lookup.load(); + } + + @Test + void order() { + assertEquals(Ordered.HIGHEST_PRECEDENCE + 1, lookup.getOrder()); + } + + @Test + void classNames() { + IdRepoImplementationType.values().keySet(). + forEach(type -> assertNotNull(lookup.getClassNames(type))); + } + + @Test + void unknownConfigurationClasses() { + assertNull(lookup.getReportClass(null)); + assertNull(lookup.getAccountRuleClass(null)); + assertNull(lookup.getPasswordRuleClass(null)); + assertNull(lookup.getInboundCorrelationRuleClass(null)); + assertNull(lookup.getPushCorrelationRuleClass(null)); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/init/EntitlementAccessorTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/init/EntitlementAccessorTest.java new file mode 100644 index 00000000000..51ec2733f0e --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/init/EntitlementAccessorTest.java @@ -0,0 +1,59 @@ +/* + * 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.core.logic.init; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; +import org.apache.syncope.common.lib.types.AnyEntitlement; +import org.apache.syncope.common.lib.types.EntitlementsHolder; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.junit.jupiter.api.Test; + +class EntitlementAccessorTest { + + @Test + void addEntitlementsForCustomAnyTypesOnly() { + AnyType user = mock(AnyType.class); + when(user.getKey()).thenReturn("builtin-user"); + AnyType group = mock(AnyType.class); + when(group.getKey()).thenReturn("builtin-group"); + AnyType custom = mock(AnyType.class); + when(custom.getKey()).thenReturn("custom"); + + AnyTypeDAO anyTypeDAO = mock(AnyTypeDAO.class); + doReturn(List.of(user, group, custom)).when(anyTypeDAO).findAll(); + when(anyTypeDAO.getUser()).thenReturn(user); + when(anyTypeDAO.getGroup()).thenReturn(group); + + new EntitlementAccessor(anyTypeDAO).addEntitlementsForAnyTypes(); + + assertTrue(EntitlementsHolder.getInstance().getValues(). + contains(AnyEntitlement.READ.getFor("custom"))); + assertFalse(EntitlementsHolder.getInstance().getValues(). + contains(AnyEntitlement.READ.getFor("builtin-user"))); + assertFalse(EntitlementsHolder.getInstance().getValues(). + contains(AnyEntitlement.READ.getFor("builtin-group"))); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/init/IdRepoEntitlementLoaderTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/init/IdRepoEntitlementLoaderTest.java new file mode 100644 index 00000000000..c8aaf16a43d --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/init/IdRepoEntitlementLoaderTest.java @@ -0,0 +1,76 @@ +/* + * 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.core.logic.init; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import org.apache.syncope.common.lib.types.EntitlementsHolder; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.persistence.api.ApplicationContextProvider; +import org.apache.syncope.core.spring.security.SecurityProperties; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.security.core.context.SecurityContextHolder; + +class IdRepoEntitlementLoaderTest { + + private DefaultListableBeanFactory previousBeanFactory; + + @BeforeEach + void setUp() { + previousBeanFactory = ApplicationContextProvider.getBeanFactory(); + } + + @AfterEach + void tearDown() { + ApplicationContextProvider.setBeanFactory(previousBeanFactory); + SecurityContextHolder.clearContext(); + } + + @Test + void order() { + assertEquals(900, new IdRepoEntitlementLoader(mock(EntitlementAccessor.class)).getOrder()); + } + + @Test + void load() { + new IdRepoEntitlementLoader(mock(EntitlementAccessor.class)).load(); + + assertTrue(EntitlementsHolder.getInstance().getValues().containsAll(IdRepoEntitlement.values())); + } + + @Test + void loadForDomain() { + DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + SecurityProperties securityProperties = new SecurityProperties(); + securityProperties.setAdminUser("admin"); + beanFactory.registerSingleton("securityProperties", securityProperties); + ApplicationContextProvider.setBeanFactory(beanFactory); + + EntitlementAccessor accessor = mock(EntitlementAccessor.class); + new IdRepoEntitlementLoader(accessor).load("Master"); + + verify(accessor).addEntitlementsForAnyTypes(); + } +} diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/init/IdRepoImplementationTypeLoaderTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/init/IdRepoImplementationTypeLoaderTest.java new file mode 100644 index 00000000000..d39227f325c --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/init/IdRepoImplementationTypeLoaderTest.java @@ -0,0 +1,43 @@ +/* + * 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.core.logic.init; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.syncope.common.lib.types.IdRepoImplementationType; +import org.apache.syncope.common.lib.types.ImplementationTypesHolder; +import org.junit.jupiter.api.Test; +import org.springframework.core.Ordered; + +class IdRepoImplementationTypeLoaderTest { + + @Test + void order() { + assertEquals(Ordered.HIGHEST_PRECEDENCE, new IdRepoImplementationTypeLoader().getOrder()); + } + + @Test + void load() { + new IdRepoImplementationTypeLoader().load(); + + assertTrue(ImplementationTypesHolder.getInstance().getValues(). + entrySet().containsAll(IdRepoImplementationType.values().entrySet())); + } +} diff --git a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapperTest.java b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapperTest.java index 5972fe69f4a..b0f82698838 100644 --- a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapperTest.java +++ b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapperTest.java @@ -29,10 +29,10 @@ import org.junit.jupiter.api.Test; import org.springframework.core.env.Environment; -public class RestServiceExceptionMapperTest { +class RestServiceExceptionMapperTest { @Test - public void passwordResetThrottleReturnsTooManyRequests() { + void passwordResetThrottleReturnsTooManyRequests() { Response response = new RestServiceExceptionMapper(mock(Environment.class)). toResponse(new PasswordResetThrottleException(42)); diff --git a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AccessTokenServiceTest.java b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AccessTokenServiceTest.java new file mode 100644 index 00000000000..8ea54392d18 --- /dev/null +++ b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AccessTokenServiceTest.java @@ -0,0 +1,121 @@ +/* + * 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.core.rest.cxf.service; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; +import java.net.URI; +import java.time.OffsetDateTime; +import org.apache.syncope.common.lib.to.AccessTokenTO; +import org.apache.syncope.common.lib.to.PagedResult; +import org.apache.syncope.common.rest.api.beans.AccessTokenQuery; +import org.apache.syncope.core.logic.AccessTokenLogic; +import org.apache.syncope.core.provisioning.api.data.AccessTokenDataBinder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Page; +import org.springframework.test.util.ReflectionTestUtils; +class AccessTokenServiceTest { + + private AccessTokenLogic logic; + private AccessTokenServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(AccessTokenLogic.class); + service = new AccessTokenServiceImpl(logic); + + UriInfo uriInfo = mock(UriInfo.class); + UriBuilder uriBuilder = mock(UriBuilder.class); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder); + when(uriBuilder.path(anyString())).thenReturn(uriBuilder); + when(uriBuilder.build()).thenReturn(URI.create("http://localhost/")); + when(uriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<>()); + ReflectionTestUtils.setField(service, "uriInfo", uriInfo); + } + + @Test + void login() { + AccessTokenDataBinder.AccessTokenInfo info = mock(AccessTokenDataBinder.AccessTokenInfo.class); + when(info.jwt()).thenReturn("token"); + when(info.expiration()).thenReturn(OffsetDateTime.now()); + when(logic.login()).thenReturn(info); + + Response response = service.login(); + + assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus()); + } + + @Test + void refresh() { + AccessTokenDataBinder.AccessTokenInfo info = mock(AccessTokenDataBinder.AccessTokenInfo.class); + when(info.jwt()).thenReturn("token"); + when(info.expiration()).thenReturn(OffsetDateTime.now()); + when(logic.refresh()).thenReturn(info); + + Response response = service.refresh(); + + assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus()); + } + + @Test + void logout() { + doNothing().when(logic).logout(); + + service.logout(); + + verify(logic).logout(); + } + + @SuppressWarnings("unchecked") + @Test + void list() { + AccessTokenQuery query = new AccessTokenQuery(); + query.setPage(1); + query.setSize(10); + + Page page = mock(Page.class); + when(logic.list(any())).thenReturn(page); + + PagedResult result = service.list(query); + + assertNotNull(result); + verify(logic).list(any()); + } + + @Test + void delete() { + doNothing().when(logic).delete("key"); + + service.delete("key"); + + verify(logic).delete("key"); + } +} diff --git a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceTest.java b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceTest.java index a3790a2b036..9e6167fefea 100644 --- a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceTest.java +++ b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceTest.java @@ -81,7 +81,7 @@ import tools.jackson.jakarta.rs.json.JacksonJsonProvider; @SpringJUnitConfig(classes = { IdRepoRESTCXFTestContext.class }) -public class AnyObjectServiceTest { +class AnyObjectServiceTest { private static final String LOCAL_ADDRESS = "local://anyObjects"; @@ -112,7 +112,7 @@ public class AnyObjectServiceTest { private AddETagFilter addETagFilter; @BeforeEach - public void setup() { + void setup() { if (SERVER == null) { AnyObjectDAO anyObjectDAO = mock(AnyObjectDAO.class); @@ -211,7 +211,7 @@ private void checkList(final PagedResult list) { } @Test - public void list() throws IOException { + void list() throws IOException { Response response = client(MediaType.APPLICATION_JSON_TYPE). query("fiql", "$type==PRINTER"). query("page", "1"). @@ -228,7 +228,7 @@ public void list() throws IOException { } @Test - public void create() { + void create() { AnyObjectCR newPrinter = new AnyObjectCR(); newPrinter.setName("newPrinter"); newPrinter.setType("PRINTER"); diff --git a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AnyTypeClassServiceTest.java b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AnyTypeClassServiceTest.java new file mode 100644 index 00000000000..a77a1be08f8 --- /dev/null +++ b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AnyTypeClassServiceTest.java @@ -0,0 +1,96 @@ +/* + * 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.core.rest.cxf.service; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; +import java.net.URI; +import java.util.List; +import org.apache.syncope.common.lib.to.AnyTypeClassTO; +import org.apache.syncope.core.logic.AnyTypeClassLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; +class AnyTypeClassServiceTest { + + private AnyTypeClassLogic logic; + private AnyTypeClassServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(AnyTypeClassLogic.class); + service = new AnyTypeClassServiceImpl(logic); + + UriInfo uriInfo = mock(UriInfo.class); + UriBuilder uriBuilder = mock(UriBuilder.class); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder); + when(uriBuilder.path(anyString())).thenReturn(uriBuilder); + when(uriBuilder.build()).thenReturn(URI.create("http://localhost/")); + when(uriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<>()); + ReflectionTestUtils.setField(service, "uriInfo", uriInfo); + } + + @Test + void list() { + when(logic.list()).thenReturn(List.of()); + assertEquals(List.of(), service.list()); + } + + @Test + void read() { + AnyTypeClassTO to = new AnyTypeClassTO(); + to.setKey("key"); + when(logic.read("key")).thenReturn(to); + assertSame(to, service.read("key")); + } + + @Test + void create() { + AnyTypeClassTO to = new AnyTypeClassTO(); + to.setKey("key"); + when(logic.create(to)).thenReturn(to); + Response response = service.create(to); + assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus()); + } + + @Test + void update() { + AnyTypeClassTO to = new AnyTypeClassTO(); + to.setKey("key"); + when(logic.update(to)).thenReturn(to); + service.update(to); + verify(logic).update(to); + } + + @Test + void delete() { + when(logic.delete("key")).thenReturn(null); + service.delete("key"); + verify(logic).delete("key"); + } +} diff --git a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AnyTypeServiceTest.java b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AnyTypeServiceTest.java new file mode 100644 index 00000000000..55913ea519e --- /dev/null +++ b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AnyTypeServiceTest.java @@ -0,0 +1,97 @@ +/* + * 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.core.rest.cxf.service; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; +import java.net.URI; +import java.util.List; +import org.apache.syncope.common.lib.to.AnyTypeTO; +import org.apache.syncope.core.logic.AnyTypeLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; +class AnyTypeServiceTest { + + private AnyTypeLogic logic; + private AnyTypeServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(AnyTypeLogic.class); + service = new AnyTypeServiceImpl(logic); + + UriInfo uriInfo = mock(UriInfo.class); + UriBuilder uriBuilder = mock(UriBuilder.class); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder); + when(uriBuilder.path(anyString())).thenReturn(uriBuilder); + when(uriBuilder.build()).thenReturn(URI.create("http://localhost/")); + when(uriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<>()); + ReflectionTestUtils.setField(service, "uriInfo", uriInfo); + } + + @Test + void list() { + when(logic.list()).thenReturn(List.of()); + assertEquals(List.of(), service.list()); + } + + @Test + void read() { + AnyTypeTO to = new AnyTypeTO(); + to.setKey("key"); + when(logic.read("key")).thenReturn(to); + assertSame(to, service.read("key")); + } + + @Test + void create() { + AnyTypeTO to = new AnyTypeTO(); + to.setKey("key"); + when(logic.create(to)).thenReturn(to); + Response response = service.create(to); + assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus()); + } + + @Test + void update() { + AnyTypeTO to = new AnyTypeTO(); + to.setKey("key"); + doNothing().when(logic).update(to); + service.update(to); + verify(logic).update(to); + } + + @Test + void delete() { + when(logic.delete("key")).thenReturn(null); + service.delete("key"); + verify(logic).delete("key"); + } +} diff --git a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/DelegationServiceTest.java b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/DelegationServiceTest.java new file mode 100644 index 00000000000..b6c993396af --- /dev/null +++ b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/DelegationServiceTest.java @@ -0,0 +1,96 @@ +/* + * 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.core.rest.cxf.service; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; +import java.net.URI; +import java.util.List; +import org.apache.syncope.common.lib.to.DelegationTO; +import org.apache.syncope.core.logic.DelegationLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; +class DelegationServiceTest { + + private DelegationLogic logic; + private DelegationServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(DelegationLogic.class); + service = new DelegationServiceImpl(logic); + + UriInfo uriInfo = mock(UriInfo.class); + UriBuilder uriBuilder = mock(UriBuilder.class); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder); + when(uriBuilder.path(anyString())).thenReturn(uriBuilder); + when(uriBuilder.build()).thenReturn(URI.create("http://localhost/")); + when(uriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<>()); + ReflectionTestUtils.setField(service, "uriInfo", uriInfo); + } + + @Test + void list() { + when(logic.list()).thenReturn(List.of()); + assertEquals(List.of(), service.list()); + } + + @Test + void read() { + DelegationTO to = new DelegationTO(); + to.setKey("key"); + when(logic.read("key")).thenReturn(to); + assertSame(to, service.read("key")); + } + + @Test + void create() { + DelegationTO to = new DelegationTO(); + to.setKey("key"); + when(logic.create(to)).thenReturn(to); + Response response = service.create(to); + assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus()); + } + + @Test + void update() { + DelegationTO to = new DelegationTO(); + to.setKey("key"); + when(logic.update(to)).thenReturn(to); + service.update(to); + verify(logic).update(to); + } + + @Test + void delete() { + when(logic.delete("key")).thenReturn(null); + service.delete("key"); + verify(logic).delete("key"); + } +} diff --git a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/NotificationServiceTest.java b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/NotificationServiceTest.java new file mode 100644 index 00000000000..e0b5fd9de2a --- /dev/null +++ b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/NotificationServiceTest.java @@ -0,0 +1,95 @@ +/* + * 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.core.rest.cxf.service; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; +import java.net.URI; +import java.util.List; +import org.apache.syncope.common.lib.to.NotificationTO; +import org.apache.syncope.core.logic.NotificationLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; +class NotificationServiceTest { + + private NotificationLogic logic; + private NotificationServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(NotificationLogic.class); + service = new NotificationServiceImpl(logic); + + UriInfo uriInfo = mock(UriInfo.class); + UriBuilder uriBuilder = mock(UriBuilder.class); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder); + when(uriBuilder.path(anyString())).thenReturn(uriBuilder); + when(uriBuilder.build()).thenReturn(URI.create("http://localhost/")); + when(uriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<>()); + ReflectionTestUtils.setField(service, "uriInfo", uriInfo); + } + + @Test + void list() { + when(logic.list()).thenReturn(List.of()); + assertEquals(List.of(), service.list()); + } + + @Test + void read() { + NotificationTO to = new NotificationTO(); + to.setKey("key"); + when(logic.read("key")).thenReturn(to); + assertEquals(to, service.read("key")); + } + + @Test + void create() { + NotificationTO to = new NotificationTO(); + to.setKey("key"); + when(logic.create(to)).thenReturn(to); + Response response = service.create(to); + assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus()); + } + + @Test + void update() { + NotificationTO to = new NotificationTO(); + to.setKey("key"); + when(logic.update(to)).thenReturn(to); + service.update(to); + verify(logic).update(to); + } + + @Test + void delete() { + when(logic.delete("key")).thenReturn(null); + service.delete("key"); + verify(logic).delete("key"); + } +} diff --git a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/RelationshipTypeServiceTest.java b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/RelationshipTypeServiceTest.java new file mode 100644 index 00000000000..5162c3871c4 --- /dev/null +++ b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/RelationshipTypeServiceTest.java @@ -0,0 +1,96 @@ +/* + * 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.core.rest.cxf.service; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; +import java.net.URI; +import java.util.List; +import org.apache.syncope.common.lib.to.RelationshipTypeTO; +import org.apache.syncope.core.logic.RelationshipTypeLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; +class RelationshipTypeServiceTest { + + private RelationshipTypeLogic logic; + private RelationshipTypeServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(RelationshipTypeLogic.class); + service = new RelationshipTypeServiceImpl(logic); + + UriInfo uriInfo = mock(UriInfo.class); + UriBuilder uriBuilder = mock(UriBuilder.class); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder); + when(uriBuilder.path(anyString())).thenReturn(uriBuilder); + when(uriBuilder.build()).thenReturn(URI.create("http://localhost/")); + when(uriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<>()); + ReflectionTestUtils.setField(service, "uriInfo", uriInfo); + } + + @Test + void list() { + when(logic.list()).thenReturn(List.of()); + assertEquals(List.of(), service.list()); + } + + @Test + void read() { + RelationshipTypeTO to = new RelationshipTypeTO(); + to.setKey("key"); + when(logic.read("key")).thenReturn(to); + assertSame(to, service.read("key")); + } + + @Test + void create() { + RelationshipTypeTO to = new RelationshipTypeTO(); + to.setKey("key"); + when(logic.create(to)).thenReturn(to); + Response response = service.create(to); + assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus()); + } + + @Test + void update() { + RelationshipTypeTO to = new RelationshipTypeTO(); + to.setKey("key"); + when(logic.update(to)).thenReturn(to); + service.update(to); + verify(logic).update(to); + } + + @Test + void delete() { + when(logic.delete("key")).thenReturn(null); + service.delete("key"); + verify(logic).delete("key"); + } +} diff --git a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/ReportServiceTest.java b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/ReportServiceTest.java new file mode 100644 index 00000000000..4a91e22d7ed --- /dev/null +++ b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/ReportServiceTest.java @@ -0,0 +1,95 @@ +/* + * 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.core.rest.cxf.service; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; +import java.net.URI; +import java.util.List; +import org.apache.syncope.common.lib.to.ReportTO; +import org.apache.syncope.core.logic.ReportLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; +class ReportServiceTest { + + private ReportLogic logic; + private ReportServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(ReportLogic.class); + service = new ReportServiceImpl(logic); + + UriInfo uriInfo = mock(UriInfo.class); + UriBuilder uriBuilder = mock(UriBuilder.class); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder); + when(uriBuilder.path(anyString())).thenReturn(uriBuilder); + when(uriBuilder.build()).thenReturn(URI.create("http://localhost/")); + when(uriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<>()); + ReflectionTestUtils.setField(service, "uriInfo", uriInfo); + } + + @Test + void list() { + when(logic.list()).thenReturn(List.of()); + assertEquals(List.of(), service.list()); + } + + @Test + void read() { + ReportTO to = new ReportTO(); + to.setKey("key"); + when(logic.read("key")).thenReturn(to); + assertEquals(to, service.read("key")); + } + + @Test + void create() { + ReportTO to = new ReportTO(); + to.setKey("key"); + when(logic.create(to)).thenReturn(to); + Response response = service.create(to); + assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus()); + } + + @Test + void update() { + ReportTO to = new ReportTO(); + to.setKey("key"); + when(logic.update(to)).thenReturn(to); + service.update(to); + verify(logic).update(to); + } + + @Test + void delete() { + when(logic.delete("key")).thenReturn(null); + service.delete("key"); + verify(logic).delete("key"); + } +} diff --git a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/RoleServiceTest.java b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/RoleServiceTest.java new file mode 100644 index 00000000000..df728599b3e --- /dev/null +++ b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/RoleServiceTest.java @@ -0,0 +1,96 @@ +/* + * 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.core.rest.cxf.service; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; +import java.net.URI; +import java.util.List; +import org.apache.syncope.common.lib.to.RoleTO; +import org.apache.syncope.core.logic.RoleLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; +class RoleServiceTest { + + private RoleLogic logic; + private RoleServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(RoleLogic.class); + service = new RoleServiceImpl(logic); + + UriInfo uriInfo = mock(UriInfo.class); + UriBuilder uriBuilder = mock(UriBuilder.class); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder); + when(uriBuilder.path(anyString())).thenReturn(uriBuilder); + when(uriBuilder.build()).thenReturn(URI.create("http://localhost/")); + when(uriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<>()); + ReflectionTestUtils.setField(service, "uriInfo", uriInfo); + } + + @Test + void list() { + when(logic.list()).thenReturn(List.of()); + assertEquals(List.of(), service.list()); + } + + @Test + void read() { + RoleTO to = new RoleTO(); + to.setKey("key"); + when(logic.read("key")).thenReturn(to); + assertSame(to, service.read("key")); + } + + @Test + void create() { + RoleTO to = new RoleTO(); + to.setKey("key"); + when(logic.create(to)).thenReturn(to); + Response response = service.create(to); + assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus()); + } + + @Test + void update() { + RoleTO to = new RoleTO(); + to.setKey("key"); + when(logic.update(to)).thenReturn(to); + service.update(to); + verify(logic).update(to); + } + + @Test + void delete() { + when(logic.delete("key")).thenReturn(null); + service.delete("key"); + verify(logic).delete("key"); + } +} diff --git a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/SecurityQuestionServiceTest.java b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/SecurityQuestionServiceTest.java new file mode 100644 index 00000000000..69539815e0f --- /dev/null +++ b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/SecurityQuestionServiceTest.java @@ -0,0 +1,96 @@ +/* + * 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.core.rest.cxf.service; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; +import java.net.URI; +import java.util.List; +import org.apache.syncope.common.lib.to.SecurityQuestionTO; +import org.apache.syncope.core.logic.SecurityQuestionLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; +class SecurityQuestionServiceTest { + + private SecurityQuestionLogic logic; + private SecurityQuestionServiceImpl service; + + @BeforeEach + void setUp() { + logic = mock(SecurityQuestionLogic.class); + service = new SecurityQuestionServiceImpl(logic); + + UriInfo uriInfo = mock(UriInfo.class); + UriBuilder uriBuilder = mock(UriBuilder.class); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder); + when(uriBuilder.path(anyString())).thenReturn(uriBuilder); + when(uriBuilder.build()).thenReturn(URI.create("http://localhost/")); + when(uriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<>()); + ReflectionTestUtils.setField(service, "uriInfo", uriInfo); + } + + @Test + void list() { + when(logic.list()).thenReturn(List.of()); + assertEquals(List.of(), service.list()); + } + + @Test + void read() { + SecurityQuestionTO to = new SecurityQuestionTO(); + to.setKey("key"); + when(logic.read("key")).thenReturn(to); + assertSame(to, service.read("key")); + } + + @Test + void create() { + SecurityQuestionTO to = new SecurityQuestionTO(); + to.setKey("key"); + when(logic.create(to)).thenReturn(to); + Response response = service.create(to); + assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus()); + } + + @Test + void update() { + SecurityQuestionTO to = new SecurityQuestionTO(); + to.setKey("key"); + when(logic.update(to)).thenReturn(to); + service.update(to); + verify(logic).update(to); + } + + @Test + void delete() { + when(logic.delete("key")).thenReturn(null); + service.delete("key"); + verify(logic).delete("key"); + } +} diff --git a/core/metrics-starter/pom.xml b/core/metrics-starter/pom.xml index e55966c8fbd..c04944c9039 100644 --- a/core/metrics-starter/pom.xml +++ b/core/metrics-starter/pom.xml @@ -53,6 +53,18 @@ under the License. io.micrometer micrometer-core + + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-junit-jupiter + test + diff --git a/core/metrics-starter/src/test/java/org/apache/syncope/core/provisioning/java/propagation/InstrumentedPriorityPropagationTaskExecutorTest.java b/core/metrics-starter/src/test/java/org/apache/syncope/core/provisioning/java/propagation/InstrumentedPriorityPropagationTaskExecutorTest.java new file mode 100644 index 00000000000..e1198347ac6 --- /dev/null +++ b/core/metrics-starter/src/test/java/org/apache/syncope/core/provisioning/java/propagation/InstrumentedPriorityPropagationTaskExecutorTest.java @@ -0,0 +1,186 @@ +/* + * 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.core.provisioning.java.propagation; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import java.util.List; +import java.util.Set; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.TaskDAO; +import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.task.PropagationData; +import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory; +import org.apache.syncope.core.provisioning.api.AuditManager; +import org.apache.syncope.core.provisioning.api.ConnectorManager; +import org.apache.syncope.core.provisioning.api.data.TaskDataBinder; +import org.apache.syncope.core.provisioning.api.notification.NotificationManager; +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo; +import org.apache.syncope.core.provisioning.java.pushpull.OutboundMatcher; +import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils; +import org.apache.syncope.core.spring.security.AuthDataAccessor; +import org.identityconnectors.framework.common.objects.ObjectClass; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.core.task.AsyncTaskExecutor; + +class InstrumentedPriorityPropagationTaskExecutorTest { + + private ConnectorManager connectorManager; + + private ConnObjectUtils connObjectUtils; + + private TaskDAO taskDAO; + + private ExternalResourceDAO resourceDAO; + + private PlainSchemaDAO plainSchemaDAO; + + private NotificationManager notificationManager; + + private AuditManager auditManager; + + private TaskDataBinder taskDataBinder; + + private AnyUtilsFactory anyUtilsFactory; + + private TaskUtilsFactory taskUtilsFactory; + + private OutboundMatcher outboundMatcher; + + private PlainAttrValidationManager validator; + + private ApplicationEventPublisher publisher; + + private AuthDataAccessor authDataAccessor; + + private AsyncTaskExecutor taskExecutor; + + private SimpleMeterRegistry meterRegistry; + + private InstrumentedPriorityPropagationTaskExecutor executor; + + @BeforeEach + void setUp() { + connectorManager = mock(ConnectorManager.class); + connObjectUtils = mock(ConnObjectUtils.class); + taskDAO = mock(TaskDAO.class); + resourceDAO = mock(ExternalResourceDAO.class); + plainSchemaDAO = mock(PlainSchemaDAO.class); + notificationManager = mock(NotificationManager.class); + auditManager = mock(AuditManager.class); + taskDataBinder = mock(TaskDataBinder.class); + anyUtilsFactory = mock(AnyUtilsFactory.class); + taskUtilsFactory = mock(TaskUtilsFactory.class); + outboundMatcher = mock(OutboundMatcher.class); + validator = mock(PlainAttrValidationManager.class); + publisher = mock(ApplicationEventPublisher.class); + authDataAccessor = mock(AuthDataAccessor.class); + taskExecutor = mock(AsyncTaskExecutor.class); + meterRegistry = new SimpleMeterRegistry(); + + when(authDataAccessor.getAuthorities(any(), isNull())).thenReturn(Set.of()); + + executor = new InstrumentedPriorityPropagationTaskExecutor( + connectorManager, + connObjectUtils, + taskDAO, + resourceDAO, + plainSchemaDAO, + notificationManager, + auditManager, + taskDataBinder, + anyUtilsFactory, + taskUtilsFactory, + outboundMatcher, + validator, + publisher, + authDataAccessor, + taskExecutor, + meterRegistry); + } + + @Test + void propagationWithCreatedStatusIncrementsCounter() { + ExternalResource resource = mock(ExternalResource.class); + when(resource.getKey()).thenReturn("resource"); + when(resource.getPropagationPriority()).thenReturn(null); + + ObjectClass objectClass = mock(ObjectClass.class); + PropagationData propagationData = mock(PropagationData.class); + PropagationTaskInfo taskInfo = new PropagationTaskInfo( + resource, + ResourceOperation.CREATE, + objectClass, + AnyTypeKind.USER, + "user", + "entityKey", + null, + propagationData); + + executor.execute(List.of(taskInfo), true, "executor"); + + assertEquals( + 1.0, + meterRegistry.counter("syncope.propagation.created.count", "resource", "resource").count()); + } + + @Test + void propagationWithMultipleStatusesIncrementsCounters() { + ExternalResource first = mock(ExternalResource.class); + when(first.getKey()).thenReturn("first"); + when(first.getPropagationPriority()).thenReturn(null); + + ExternalResource second = mock(ExternalResource.class); + when(second.getKey()).thenReturn("second"); + when(second.getPropagationPriority()).thenReturn(null); + + ObjectClass objectClass = mock(ObjectClass.class); + PropagationData propagationData = mock(PropagationData.class); + + PropagationTaskInfo firstInfo = new PropagationTaskInfo( + first, ResourceOperation.CREATE, objectClass, AnyTypeKind.USER, "user", + "entityKey1", null, propagationData); + PropagationTaskInfo secondInfo = new PropagationTaskInfo( + second, ResourceOperation.UPDATE, objectClass, AnyTypeKind.USER, "user", + "entityKey2", null, propagationData); + + // Override the reporter used by the executor: the DefaultPropagationReporter created internally + // deduplicates by resource, so each resource yields a single CREATED status. + executor.execute(List.of(firstInfo, secondInfo), true, "executor"); + + assertEquals( + 1.0, + meterRegistry.counter("syncope.propagation.created.count", "resource", "first").count()); + assertEquals( + 1.0, + meterRegistry.counter("syncope.propagation.created.count", "resource", "second").count()); + } +} diff --git a/core/metrics-starter/src/test/java/org/apache/syncope/core/spring/security/InstrumentedAuthDataAccessorTest.java b/core/metrics-starter/src/test/java/org/apache/syncope/core/spring/security/InstrumentedAuthDataAccessorTest.java new file mode 100644 index 00000000000..ca91483cc9e --- /dev/null +++ b/core/metrics-starter/src/test/java/org/apache/syncope/core/spring/security/InstrumentedAuthDataAccessorTest.java @@ -0,0 +1,379 @@ +/* + * 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.core.spring.security; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.nimbusds.jwt.JWTClaimsSet; +import dev.samstevens.totp.code.CodeVerifier; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import java.util.Date; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.common.keymaster.client.api.ConfParamOps; +import org.apache.syncope.common.keymaster.client.api.StandardConfParams; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.common.lib.types.EntitlementsHolder; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.persistence.api.Encryptor; +import org.apache.syncope.core.persistence.api.EncryptorManager; +import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.persistence.api.dao.DelegationDAO; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; +import org.apache.syncope.core.persistence.api.dao.RoleDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.Role; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.AuditManager; +import org.apache.syncope.core.provisioning.api.ConnectorManager; +import org.apache.syncope.core.provisioning.api.MappingManager; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.security.authentication.DisabledException; +import org.springframework.security.core.Authentication; + +class InstrumentedAuthDataAccessorTest { + + private static final String ADMIN = "admin"; + + private static final String BOB = "bob"; + + private SecurityProperties securityProperties; + + private EncryptorManager encryptorManager; + + private Encryptor encryptor; + + private CodeVerifier totpCodeVerifier; + + private RealmSearchDAO realmSearchDAO; + + private UserDAO userDAO; + + private GroupDAO groupDAO; + + private AnySearchDAO anySearchDAO; + + private AccessTokenDAO accessTokenDAO; + + private ConfParamOps confParamOps; + + private RoleDAO roleDAO; + + private DelegationDAO delegationDAO; + + private ExternalResourceDAO resourceDAO; + + private ConnectorManager connectorManager; + + private AuditManager auditManager; + + private MappingManager mappingManager; + + private DefaultCredentialChecker credentialChecker; + + private SimpleMeterRegistry meterRegistry; + + private InstrumentedAuthDataAccessor authDataAccessor; + + @BeforeEach + void setUp() { + securityProperties = new SecurityProperties(); + securityProperties.setJwtIssuer("ApacheSyncope"); + securityProperties.setAdminUser(ADMIN); + securityProperties.setAnonymousUser("anonymous"); + + EntitlementsHolder.getInstance().addAll(IdRepoEntitlement.values()); + + encryptorManager = mock(EncryptorManager.class); + encryptor = mock(Encryptor.class); + when(encryptorManager.getInstance()).thenReturn(encryptor); + + totpCodeVerifier = mock(CodeVerifier.class); + realmSearchDAO = mock(RealmSearchDAO.class); + userDAO = mock(UserDAO.class); + groupDAO = mock(GroupDAO.class); + anySearchDAO = mock(AnySearchDAO.class); + accessTokenDAO = mock(AccessTokenDAO.class); + confParamOps = mock(ConfParamOps.class); + roleDAO = mock(RoleDAO.class); + delegationDAO = mock(DelegationDAO.class); + resourceDAO = mock(ExternalResourceDAO.class); + connectorManager = mock(ConnectorManager.class); + auditManager = mock(AuditManager.class); + mappingManager = mock(MappingManager.class); + credentialChecker = mock(DefaultCredentialChecker.class); + + when(confParamOps.get(anyString(), eq(StandardConfParams.MFA_ENABLED), any(), eq(boolean.class))). + thenReturn(false); + when(confParamOps.get(anyString(), eq(StandardConfParams.AUTHENTICATION_ATTRIBUTES), any(), + eq(String[].class))).thenReturn(new String[] { "username" }); + when(confParamOps.get(anyString(), eq(StandardConfParams.AUTHENTICATION_STATUSES), any(), + eq(String[].class))).thenReturn(new String[] { "active" }); + when(confParamOps.get(anyString(), eq(StandardConfParams.LOG_LAST_LOGIN_DATE), any(), eq(boolean.class))). + thenReturn(true); + + meterRegistry = new SimpleMeterRegistry(); + + authDataAccessor = new InstrumentedAuthDataAccessor( + securityProperties, + encryptorManager, + totpCodeVerifier, + realmSearchDAO, + userDAO, + groupDAO, + anySearchDAO, + accessTokenDAO, + confParamOps, + roleDAO, + delegationDAO, + resourceDAO, + connectorManager, + auditManager, + mappingManager, + credentialChecker, + List.of(), + meterRegistry); + } + + private Authentication usernamePassword(final String username, final String credentials) { + Authentication authentication = mock(Authentication.class); + when(authentication.getName()).thenReturn(username); + when(authentication.getCredentials()).thenReturn(credentials); + when(authentication.getDetails()). + thenReturn(new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, null)); + return authentication; + } + + private JWTAuthentication jwt( + final String subject, final String issuer, final String jwtID) { + + JWTClaimsSet claims = new JWTClaimsSet.Builder(). + issuer(issuer). + subject(subject). + jwtID(jwtID). + issueTime(new Date()). + build(); + return new JWTAuthentication( + claims, + new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, null)); + } + + private User user(final String username) { + User user = mock(User.class); + when(user.getUsername()).thenReturn(username); + when(user.getKey()).thenReturn(username + "-key"); + when(user.getStatus()).thenReturn("active"); + when(user.isSuspended()).thenReturn(false); + when(user.isMustChangePassword()).thenReturn(false); + when(user.getFailedLogins()).thenReturn(0); + when(user.getCipherAlgorithm()).thenReturn(CipherAlgorithm.SHA512); + when(user.getPassword()).thenReturn("encoded-password"); + + Realm realm = mock(Realm.class); + when(realm.getFullPath()).thenReturn("/"); + when(user.getRealm()).thenReturn(realm); + + return user; + } + + private Role role(final String entitlement, final String realmPath) { + Role role = mock(Role.class); + when(role.getEntitlements()).thenReturn(Set.of(entitlement)); + + Realm realm = mock(Realm.class); + when(realm.getFullPath()).thenReturn(realmPath); + doReturn(List.of(realm)).when(role).getRealms(); + return role; + } + + @Test + void successfulUsernamePasswordAuthenticationIncrementsSuccessCounter() { + User user = user(BOB); + doReturn(Optional.of(user)).when(userDAO).findByUsername(BOB); + Role userRole = role(IdRepoEntitlement.USER_READ, SyncopeConstants.ROOT_REALM); + doReturn(List.of(userRole)).when(userDAO).findAllRoles(user); + when(userDAO.save(user)).thenReturn(user); + when(userDAO.findAllResources(user)).thenReturn(List.of()); + when(realmSearchDAO.findAncestors(any())).thenReturn(List.of()); + when(encryptor.verify(any(), any(), any())).thenReturn(true); + + authDataAccessor.authenticate(Optional.empty(), usernamePassword(BOB, "password")); + + assertEquals( + 1.0, + meterRegistry.counter( + "syncope.auth.username.success.count", "realm", "/").count()); + } + + @Test + void failedUsernamePasswordAuthenticationIncrementsFailureCounter() { + User user = user(BOB); + doReturn(Optional.of(user)).when(userDAO).findByUsername(BOB); + when(userDAO.save(user)).thenReturn(user); + when(userDAO.findAllResources(user)).thenReturn(List.of()); + when(realmSearchDAO.findAncestors(any())).thenReturn(List.of()); + when(encryptor.verify(any(), any(), any())).thenReturn(false); + + authDataAccessor.authenticate(Optional.empty(), usernamePassword(BOB, "wrong")); + + assertEquals( + 1.0, + meterRegistry.counter( + "syncope.auth.username.failure.count", "realm", "/").count()); + } + + @Test + void suspendedUserIncrementsDisabledCounter() { + User user = user(BOB); + when(user.isSuspended()).thenReturn(true); + doReturn(Optional.of(user)).when(userDAO).findByUsername(BOB); + + assertThrows( + DisabledException.class, + () -> authDataAccessor.authenticate(Optional.empty(), usernamePassword(BOB, "password"))); + + assertEquals( + 1.0, + meterRegistry.counter("syncope.auth.username.disabled.count").count()); + } + + @Test + void successfulJWTAuthenticationIncrementsSuccessCounter() { + User user = user(BOB); + JWTSSOProvider provider = mock(JWTSSOProvider.class); + when(provider.getIssuer()).thenReturn("issuer"); + when(provider.resolve(any())). + thenReturn(Optional.of( + new JWTSSOProvider.ResolvedClaims( + user, Set.of(new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ))))); + + InstrumentedAuthDataAccessor accessor = new InstrumentedAuthDataAccessor( + securityProperties, + encryptorManager, + totpCodeVerifier, + realmSearchDAO, + userDAO, + groupDAO, + anySearchDAO, + accessTokenDAO, + confParamOps, + roleDAO, + delegationDAO, + resourceDAO, + connectorManager, + auditManager, + mappingManager, + credentialChecker, + List.of(provider), + meterRegistry); + + accessor.authenticate(jwt(BOB, "issuer", "jti")); + + assertEquals( + 1.0, + meterRegistry.counter("syncope.auth.jwt.success.count").count()); + } + + @Test + void jwtWithUnknownIssuerIncrementsNotFoundCounter() { + JWTSSOProvider provider = mock(JWTSSOProvider.class); + when(provider.getIssuer()).thenReturn("issuer"); + when(provider.resolve(any())).thenReturn(Optional.empty()); + + InstrumentedAuthDataAccessor accessor = new InstrumentedAuthDataAccessor( + securityProperties, + encryptorManager, + totpCodeVerifier, + realmSearchDAO, + userDAO, + groupDAO, + anySearchDAO, + accessTokenDAO, + confParamOps, + roleDAO, + delegationDAO, + resourceDAO, + connectorManager, + auditManager, + mappingManager, + credentialChecker, + List.of(provider), + meterRegistry); + + assertThrows( + org.springframework.security.authentication.AuthenticationCredentialsNotFoundException.class, + () -> accessor.authenticate(jwt(BOB, "issuer", "jti"))); + + assertEquals( + 1.0, + meterRegistry.counter("syncope.auth.jwt.notfound.count").count()); + } + + @Test + void suspendedJWTUserIncrementsDisabledCounter() { + User user = user(BOB); + when(user.isSuspended()).thenReturn(true); + JWTSSOProvider provider = mock(JWTSSOProvider.class); + when(provider.getIssuer()).thenReturn("issuer"); + when(provider.resolve(any())). + thenReturn(Optional.of(new JWTSSOProvider.ResolvedClaims(user, Set.of()))); + + InstrumentedAuthDataAccessor accessor = new InstrumentedAuthDataAccessor( + securityProperties, + encryptorManager, + totpCodeVerifier, + realmSearchDAO, + userDAO, + groupDAO, + anySearchDAO, + accessTokenDAO, + confParamOps, + roleDAO, + delegationDAO, + resourceDAO, + connectorManager, + auditManager, + mappingManager, + credentialChecker, + List.of(provider), + meterRegistry); + + assertThrows( + DisabledException.class, + () -> accessor.authenticate(jwt(BOB, "issuer", "jti"))); + + assertEquals( + 1.0, + meterRegistry.counter("syncope.auth.jwt.disabled.count").count()); + } +} diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/ApplicationContextProviderTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/ApplicationContextProviderTest.java new file mode 100644 index 00000000000..e3e5054e300 --- /dev/null +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/ApplicationContextProviderTest.java @@ -0,0 +1,92 @@ +/* + * 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 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.core.persistence.api; + +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.ConfigurableApplicationContext; + +class ApplicationContextProviderTest { + + @BeforeEach + void setUp() { + ApplicationContextProvider.setApplicationContext(null); + ApplicationContextProvider.setBeanFactory(null); + } + + @AfterEach + void tearDown() { + ApplicationContextProvider.setApplicationContext(null); + ApplicationContextProvider.setBeanFactory(null); + } + + @Test + void getApplicationContextReturnsNullWhenUnset() { + assertNull(ApplicationContextProvider.getApplicationContext()); + } + + @Test + void getBeanFactoryReturnsNullWhenUnset() { + assertNull(ApplicationContextProvider.getBeanFactory()); + } + + @Test + void setApplicationContextSetsContext() { + final ConfigurableApplicationContext ctx = Mockito.mock(ConfigurableApplicationContext.class); + + ApplicationContextProvider.setApplicationContext(ctx); + + assertSame(ctx, ApplicationContextProvider.getApplicationContext()); + } + + @Test + void getBeanFactoryReturnsBeanFactoryFromContext() { + final ConfigurableApplicationContext ctx = Mockito.mock(ConfigurableApplicationContext.class); + final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + Mockito.when(ctx.getBeanFactory()).thenReturn(beanFactory); + + ApplicationContextProvider.setApplicationContext(ctx); + + assertSame(beanFactory, ApplicationContextProvider.getBeanFactory()); + } + + @Test + void setBeanFactorySetsBeanFactory() { + final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + + ApplicationContextProvider.setBeanFactory(beanFactory); + + assertSame(beanFactory, ApplicationContextProvider.getBeanFactory()); + } + + @Test + void setApplicationContextViaAware() { + final ConfigurableApplicationContext ctx = Mockito.mock(ConfigurableApplicationContext.class); + final ApplicationContextProvider provider = new ApplicationContextProvider(); + + provider.setApplicationContext(ctx); + + assertSame(ctx, ApplicationContextProvider.getApplicationContext()); + } +} diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/dao/AllowedSchemasTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/dao/AllowedSchemasTest.java new file mode 100644 index 00000000000..6d75542d0dc --- /dev/null +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/dao/AllowedSchemasTest.java @@ -0,0 +1,117 @@ +/* + * 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 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.core.persistence.api.dao; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashSet; +import java.util.Set; +import org.apache.syncope.core.persistence.api.entity.RelationshipType; +import org.apache.syncope.core.persistence.api.entity.Schema; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class AllowedSchemasTest { + + @Mock + private Schema schema; + + @Mock + private Group group; + + @Mock + private RelationshipType relationshipType; + + @Test + void selfContains() { + AllowedSchemas allowedSchemas = new AllowedSchemas<>(); + final Schema other = Mockito.mock(Schema.class); + + assertFalse(allowedSchemas.selfContains(schema)); + allowedSchemas.self().add(schema); + assertTrue(allowedSchemas.selfContains(schema)); + assertFalse(allowedSchemas.selfContains(other)); + } + + @Test + void selfContainsByName() { + AllowedSchemas allowedSchemas = new AllowedSchemas<>(); + final Schema other = Mockito.mock(Schema.class); + + allowedSchemas.self().add(schema); + Mockito.when(schema.getKey()).thenReturn("this-schema"); + + assertTrue(allowedSchemas.selfContains("this-schema")); + assertFalse(allowedSchemas.selfContains("other-schema")); + assertFalse(allowedSchemas.selfContains(other)); + } + + @Test + void membershipsContains() { + AllowedSchemas allowedSchemas = new AllowedSchemas<>(); + final Set schemas = new HashSet<>(); + schemas.add(schema); + + assertFalse(allowedSchemas.membershipsContains(group, schema)); + allowedSchemas.memberships().put(group, schemas); + assertTrue(allowedSchemas.membershipsContains(group, schema)); + } + + @Test + void membershipsContainsByName() { + AllowedSchemas allowedSchemas = new AllowedSchemas<>(); + final Set schemas = new HashSet<>(); + schemas.add(schema); + + allowedSchemas.memberships().put(group, schemas); + Mockito.when(schema.getKey()).thenReturn("this-schema"); + + assertTrue(allowedSchemas.membershipsContains(group, "this-schema")); + assertFalse(allowedSchemas.membershipsContains(group, "missing-schema")); + } + + @Test + void relationshipTypesContains() { + AllowedSchemas allowedSchemas = new AllowedSchemas<>(); + final Set schemas = new HashSet<>(); + schemas.add(schema); + + assertFalse(allowedSchemas.relationshipTypesContains(relationshipType, schema)); + allowedSchemas.relationshipTypes().put(relationshipType, schemas); + assertTrue(allowedSchemas.relationshipTypesContains(relationshipType, schema)); + } + + @Test + void relationshipTypesContainsByName() { + AllowedSchemas allowedSchemas = new AllowedSchemas<>(); + final Set schemas = new HashSet<>(); + schemas.add(schema); + + allowedSchemas.relationshipTypes().put(relationshipType, schemas); + Mockito.when(schema.getKey()).thenReturn("this-schema"); + + assertTrue(allowedSchemas.relationshipTypesContains(relationshipType, "this-schema")); + assertFalse(allowedSchemas.relationshipTypesContains(relationshipType, "missing-schema")); + } +} diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/dao/AnyCheckerTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/dao/AnyCheckerTest.java new file mode 100644 index 00000000000..b2f34068d8f --- /dev/null +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/dao/AnyCheckerTest.java @@ -0,0 +1,351 @@ +/* + * 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 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.core.persistence.api.dao; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.common.lib.types.EntityViolationType; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.AnyUtils; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.RelationshipType; +import org.apache.syncope.core.persistence.api.entity.RelationshipTypeExtension; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.group.GroupTypeExtension; +import org.apache.syncope.core.persistence.api.entity.user.UMembership; +import org.apache.syncope.core.persistence.api.entity.user.URelationship; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class AnyCheckerTest { + + private static final String ALLOWED_SCHEMA = "allowed-schema"; + private static final String NOT_ALLOWED_SCHEMA = "not-allowed-schema"; + private static final String NOT_ALLOWED_RELATIONSHIP_SCHEMA = "not-allowed-relationship-schema"; + private static final String MEMBER_NOT_ALLOWED_SCHEMA = "member-not-allowed-schema"; + + @Mock + private PlainSchemaDAO plainSchemaDAO; + + private AnyChecker anyChecker; + + @BeforeEach + void setUp() { + anyChecker = new AnyChecker(plainSchemaDAO); + } + + @Test + void findAllowedSchemasByType() { + final User user = mock(User.class); + final AnyType anyType = mock(AnyType.class); + final AnyTypeClass typeClass = mock(AnyTypeClass.class); + final PlainSchema plainSchema = mock(PlainSchema.class); + final List plainSchemas = new ArrayList<>(); + plainSchemas.add(plainSchema); + final Set anyTypeClasses = new HashSet<>(); + anyTypeClasses.add(typeClass); + final List auxClasses = new ArrayList<>(); + + when(user.getType()).thenReturn(anyType); + doReturn(anyTypeClasses).when(anyType).getClasses(); + doReturn(auxClasses).when(user).getAuxClasses(); + doReturn(plainSchemas).when(typeClass).getPlainSchemas(); + when(plainSchema.getKey()).thenReturn("plain-schema"); + + final AllowedSchemas allowedSchemas = anyChecker.findAllowedSchemas(user, PlainSchema.class); + + assertTrue(allowedSchemas.selfContains("plain-schema")); + } + + @Test + void findAllowedSchemasByMembership() { + final User user = mock(User.class); + final AnyType anyType = mock(AnyType.class); + final AnyTypeClass auxTypeClass = mock(AnyTypeClass.class); + final PlainSchema plainSchema = mock(PlainSchema.class); + final Group group = mock(Group.class); + final GroupTypeExtension typeExtension = mock(GroupTypeExtension.class); + final UMembership membership = mock(UMembership.class); + + final List plainSchemas = new ArrayList<>(); + plainSchemas.add(plainSchema); + final List auxClasses = new ArrayList<>(); + final List auxTypeClasses = new ArrayList<>(); + auxTypeClasses.add(auxTypeClass); + final List groupTypeExtensions = new ArrayList<>(); + groupTypeExtensions.add(typeExtension); + final List memberships = new ArrayList<>(); + memberships.add(membership); + + when(user.getType()).thenReturn(anyType); + doReturn(memberships).when(user).getMemberships(); + when(membership.getRightEnd()).thenReturn(group); + doReturn(groupTypeExtensions).when(group).getTypeExtensions(); + doReturn(auxTypeClasses).when(typeExtension).getAuxClasses(); + doReturn(plainSchemas).when(auxTypeClass).getPlainSchemas(); + doReturn(auxClasses).when(user).getAuxClasses(); + + final AllowedSchemas allowedSchemas = anyChecker.findAllowedSchemas(user, PlainSchema.class); + + assertTrue(allowedSchemas.memberships().get(group).contains(plainSchema)); + } + + @Test + void findAllowedSchemasByRelationshipType() { + final User user = mock(User.class); + final AnyType anyType = mock(AnyType.class); + final AnyTypeClass auxTypeClass = mock(AnyTypeClass.class); + final PlainSchema plainSchema = mock(PlainSchema.class); + final RelationshipType relationshipType = mock(RelationshipType.class); + final URelationship relationship = mock(URelationship.class); + final RelationshipTypeExtension relationshipTypeExtension = mock(RelationshipTypeExtension.class); + + final List auxClasses = new ArrayList<>(); + final List auxTypeClasses = new ArrayList<>(); + auxTypeClasses.add(auxTypeClass); + final List relationshipTypeExtensions = new ArrayList<>(); + relationshipTypeExtensions.add(relationshipTypeExtension); + final List relationships = new ArrayList<>(); + relationships.add(relationship); + final List plainSchemas = new ArrayList<>(); + plainSchemas.add(plainSchema); + + when(user.getType()).thenReturn(anyType); + doReturn(auxClasses).when(user).getAuxClasses(); + doReturn(relationships).when(user).getRelationships(); + when(relationship.getType()).thenReturn(relationshipType); + doReturn(relationshipTypeExtensions).when(relationshipType).getTypeExtensions(); + doReturn(auxTypeClasses).when(relationshipTypeExtension).getAuxClasses(); + doReturn(plainSchemas).when(auxTypeClass).getPlainSchemas(); + + final AllowedSchemas allowedSchemas = anyChecker.findAllowedSchemas(user, PlainSchema.class); + + assertTrue(allowedSchemas.relationshipTypes().get(relationshipType).contains(plainSchema)); + } + + @Test + void checkBeforeSaveWithInvalidManager() { + final User user = mock(User.class); + final User manager = mock(User.class); + final Group managerGroup = mock(Group.class); + final AnyUtils anyUtils = mock(AnyUtils.class); + + when(user.getuManager()).thenReturn(manager); + when(user.getgManager()).thenReturn(managerGroup); + doReturn(User.class).when(anyUtils).anyClass(); + + assertThrows(InvalidEntityException.class, () -> anyChecker.checkBeforeSave(user, anyUtils)); + } + + @Test + void checkBeforeSaveWithAllowedSchema() { + final User user = mock(User.class); + final AnyType anyType = mock(AnyType.class); + final AnyTypeClass typeClass = mock(AnyTypeClass.class); + final PlainSchema plainSchema = mock(PlainSchema.class); + final PlainAttr attr = mock(PlainAttr.class); + final List plainSchemas = new ArrayList<>(); + plainSchemas.add(plainSchema); + final Set anyTypeClasses = new HashSet<>(); + anyTypeClasses.add(typeClass); + final List auxClasses = new ArrayList<>(); + final AnyUtils anyUtils = mock(AnyUtils.class); + + when(user.getuManager()).thenReturn(null); + when(user.getType()).thenReturn(anyType); + doReturn(anyTypeClasses).when(anyType).getClasses(); + doReturn(auxClasses).when(user).getAuxClasses(); + doReturn(plainSchemas).when(typeClass).getPlainSchemas(); + when(user.getPlainAttrs()).thenReturn(List.of(attr)); + when(attr.getSchema()).thenReturn(ALLOWED_SCHEMA); + when(attr.getUniqueValue()).thenReturn(null); + when(plainSchema.getKey()).thenReturn(ALLOWED_SCHEMA); + + assertDoesNotThrow(() -> anyChecker.checkBeforeSave(user, anyUtils)); + verify(plainSchemaDAO, never()).existsPlainAttrUniqueValue(any(), anyString(), any(), any()); + } + + @Test + void checkBeforeSaveWithNotAllowedSchema() { + final User user = mock(User.class); + final AnyType anyType = mock(AnyType.class); + final AnyTypeClass typeClass = mock(AnyTypeClass.class); + final PlainSchema plainSchema = mock(PlainSchema.class); + final PlainAttr attr = mock(PlainAttr.class); + final List plainSchemas = new ArrayList<>(); + plainSchemas.add(plainSchema); + final Set anyTypeClasses = new HashSet<>(); + anyTypeClasses.add(typeClass); + final List auxClasses = new ArrayList<>(); + final AnyUtils anyUtils = mock(AnyUtils.class); + + when(user.getuManager()).thenReturn(null); + when(user.getType()).thenReturn(anyType); + doReturn(anyTypeClasses).when(anyType).getClasses(); + doReturn(auxClasses).when(user).getAuxClasses(); + doReturn(plainSchemas).when(typeClass).getPlainSchemas(); + when(user.getPlainAttrs()).thenReturn(List.of(attr)); + when(attr.getSchema()).thenReturn(NOT_ALLOWED_SCHEMA); + when(plainSchema.getKey()).thenReturn(ALLOWED_SCHEMA); + doReturn(User.class).when(anyUtils).anyClass(); + + final InvalidEntityException exception = assertThrows(InvalidEntityException.class, + () -> anyChecker.checkBeforeSave(user, anyUtils)); + + assertTrue(exception.hasViolation(EntityViolationType.InvalidPlainAttr)); + } + + @Test + void checkBeforeSaveWithNotAllowedMembershipSchema() { + final User user = mock(User.class); + final AnyType anyType = mock(AnyType.class); + final AnyTypeClass auxTypeClass = mock(AnyTypeClass.class); + final PlainSchema plainSchema = mock(PlainSchema.class); + final Group group = mock(Group.class); + final GroupTypeExtension typeExtension = mock(GroupTypeExtension.class); + final UMembership membership = mock(UMembership.class); + final PlainAttr attr = mock(PlainAttr.class); + final AnyUtils anyUtils = mock(AnyUtils.class); + + final List plainSchemas = new ArrayList<>(); + plainSchemas.add(plainSchema); + final List auxTypeClasses = new ArrayList<>(); + auxTypeClasses.add(auxTypeClass); + final List groupTypeExtensions = new ArrayList<>(); + groupTypeExtensions.add(typeExtension); + final List memberships = new ArrayList<>(); + memberships.add(membership); + final List userAttrs = new ArrayList<>(); + final List membershipAttrs = new ArrayList<>(); + membershipAttrs.add(attr); + + when(user.getuManager()).thenReturn(null); + when(user.getType()).thenReturn(anyType); + doReturn(memberships).when(user).getMemberships(); + when(membership.getRightEnd()).thenReturn(group); + doReturn(groupTypeExtensions).when(group).getTypeExtensions(); + doReturn(auxTypeClasses).when(typeExtension).getAuxClasses(); + doReturn(plainSchemas).when(auxTypeClass).getPlainSchemas(); + doReturn(userAttrs).when(user).getPlainAttrs(); + doReturn(membershipAttrs).when(user).getPlainAttrs(membership); + when(attr.getSchema()).thenReturn(MEMBER_NOT_ALLOWED_SCHEMA); + when(plainSchema.getKey()).thenReturn("member-plain-schema"); + doReturn(User.class).when(anyUtils).anyClass(); + + final InvalidEntityException exception = assertThrows(InvalidEntityException.class, + () -> anyChecker.checkBeforeSave(user, anyUtils)); + + assertTrue(exception.hasViolation(EntityViolationType.InvalidPlainAttr)); + } + + @Test + void checkBeforeSaveWithNotAllowedRelationshipSchema() { + final User user = mock(User.class); + final AnyType anyType = mock(AnyType.class); + final RelationshipType relationshipType = mock(RelationshipType.class); + final URelationship relationship = mock(URelationship.class); + final RelationshipTypeExtension relationshipTypeExtension = mock(RelationshipTypeExtension.class); + final AnyTypeClass auxTypeClass = mock(AnyTypeClass.class); + final PlainAttr attr = mock(PlainAttr.class); + final AnyUtils anyUtils = mock(AnyUtils.class); + + final List auxClasses = new ArrayList<>(); + final List auxTypeClasses = new ArrayList<>(); + auxTypeClasses.add(auxTypeClass); + final List relationshipTypeExtensions = new ArrayList<>(); + relationshipTypeExtensions.add(relationshipTypeExtension); + final List relationships = new ArrayList<>(); + relationships.add(relationship); + final List userAttrs = new ArrayList<>(); + final List relationshipAttrs = new ArrayList<>(); + relationshipAttrs.add(attr); + + when(user.getuManager()).thenReturn(null); + when(user.getType()).thenReturn(anyType); + doReturn(auxClasses).when(user).getAuxClasses(); + doReturn(relationships).when(user).getRelationships(); + when(relationship.getType()).thenReturn(relationshipType); + doReturn(relationshipTypeExtensions).when(relationshipType).getTypeExtensions(); + doReturn(auxTypeClasses).when(relationshipTypeExtension).getAuxClasses(); + doReturn(userAttrs).when(user).getPlainAttrs(); + doReturn(relationshipAttrs).when(user).getPlainAttrs(relationship); + when(attr.getSchema()).thenReturn(NOT_ALLOWED_RELATIONSHIP_SCHEMA); + doReturn(User.class).when(anyUtils).anyClass(); + + final InvalidEntityException exception = assertThrows(InvalidEntityException.class, + () -> anyChecker.checkBeforeSave(user, anyUtils)); + + assertTrue(exception.hasViolation(EntityViolationType.InvalidPlainAttr)); + } + + @Test + void checkBeforeSaveWithDuplicateUniqueValue() { + final User user = mock(User.class); + final AnyType anyType = mock(AnyType.class); + final AnyTypeClass typeClass = mock(AnyTypeClass.class); + final PlainSchema plainSchema = mock(PlainSchema.class); + final PlainAttr attr = mock(PlainAttr.class); + final PlainAttrValue uniqueValue = mock(PlainAttrValue.class); + final List plainSchemas = new ArrayList<>(); + plainSchemas.add(plainSchema); + final Set anyTypeClasses = new HashSet<>(); + anyTypeClasses.add(typeClass); + final List auxClasses = new ArrayList<>(); + final AnyUtils anyUtils = mock(AnyUtils.class); + + when(user.getuManager()).thenReturn(null); + when(user.getType()).thenReturn(anyType); + doReturn(anyTypeClasses).when(anyType).getClasses(); + doReturn(auxClasses).when(user).getAuxClasses(); + doReturn(plainSchemas).when(typeClass).getPlainSchemas(); + when(user.getKey()).thenReturn("user-key"); + when(user.getPlainAttrs()).thenReturn(List.of(attr)); + when(attr.getSchema()).thenReturn(ALLOWED_SCHEMA); + when(attr.getUniqueValue()).thenReturn(uniqueValue); + when(plainSchema.getKey()).thenReturn(ALLOWED_SCHEMA); + doReturn(Optional.of(plainSchema)).when(plainSchemaDAO).findById(ALLOWED_SCHEMA); + when(plainSchemaDAO.existsPlainAttrUniqueValue(any(), eq("user-key"), any(), any())).thenReturn(true); + + assertThrows(DuplicateException.class, () -> anyChecker.checkBeforeSave(user, anyUtils)); + verify(plainSchemaDAO).existsPlainAttrUniqueValue(any(), eq("user-key"), any(), any()); + } +} diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/dao/RealmCheckerTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/dao/RealmCheckerTest.java new file mode 100644 index 00000000000..31c9e2486ca --- /dev/null +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/dao/RealmCheckerTest.java @@ -0,0 +1,161 @@ +/* + * 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 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.core.persistence.api.dao; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.common.lib.types.EntityViolationType; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class RealmCheckerTest { + + private static final String ALLOWED_SCHEMA = "allowed-schema"; + private static final String NOT_ALLOWED_SCHEMA = "not-allowed-schema"; + + @Mock + private PlainSchemaDAO plainSchemaDAO; + + private RealmChecker realmChecker; + + @BeforeEach + void setUp() { + realmChecker = new RealmChecker(plainSchemaDAO); + } + + @Test + void checkBeforeSaveWithNoAttrs() { + final Realm realm = mock(Realm.class); + final Set noAnyTypeClasses = new HashSet<>(); + final List noAttrs = new ArrayList<>(); + doReturn(noAnyTypeClasses).when(realm).getAnyTypeClasses(); + doReturn(noAttrs).when(realm).getPlainAttrs(); + + assertDoesNotThrow(() -> realmChecker.checkBeforeSave(realm)); + verify(plainSchemaDAO, never()).existsPlainAttrUniqueValue(anyString(), any(), any()); + } + + @Test + void checkBeforeSaveWithNullSchema() { + final Realm realm = mock(Realm.class); + final PlainAttr attr = mock(PlainAttr.class); + final List attrs = new ArrayList<>(); + attrs.add(attr); + doReturn(new HashSet()).when(realm).getAnyTypeClasses(); + when(realm.getPlainAttrs()).thenReturn(attrs); + when(attr.getSchema()).thenReturn(null); + when(attr.getUniqueValue()).thenReturn(null); + + assertDoesNotThrow(() -> realmChecker.checkBeforeSave(realm)); + verify(plainSchemaDAO, never()).existsPlainAttrUniqueValue(anyString(), any(), any()); + } + + @Test + void checkBeforeSaveWithAllowedSchema() { + final Realm realm = mock(Realm.class); + final AnyTypeClass anyTypeClass = mock(AnyTypeClass.class); + final PlainSchema plainSchema = mock(PlainSchema.class); + final PlainAttr attr = mock(PlainAttr.class); + final PlainAttrValue uniqueValue = mock(PlainAttrValue.class); + final List plainSchemas = new ArrayList<>(); + plainSchemas.add(plainSchema); + final Set anyTypeClasses = new HashSet<>(); + anyTypeClasses.add(anyTypeClass); + + when(plainSchema.getKey()).thenReturn(ALLOWED_SCHEMA); + doReturn(plainSchemas).when(anyTypeClass).getPlainSchemas(); + doReturn(anyTypeClasses).when(realm).getAnyTypeClasses(); + when(realm.getPlainAttrs()).thenReturn(List.of(attr)); + when(realm.getKey()).thenReturn("realm-key"); + when(attr.getSchema()).thenReturn(ALLOWED_SCHEMA); + when(attr.getUniqueValue()).thenReturn(uniqueValue); + doReturn(Optional.of(plainSchema)).when(plainSchemaDAO).findById(ALLOWED_SCHEMA); + when(plainSchemaDAO.existsPlainAttrUniqueValue("realm-key", plainSchema, uniqueValue)).thenReturn(false); + + assertDoesNotThrow(() -> realmChecker.checkBeforeSave(realm)); + verify(plainSchemaDAO).existsPlainAttrUniqueValue("realm-key", plainSchema, uniqueValue); + } + + @Test + void checkBeforeSaveWithNotAllowedSchema() { + final Realm realm = mock(Realm.class); + final PlainAttr attr = mock(PlainAttr.class); + final List attrs = new ArrayList<>(); + attrs.add(attr); + doReturn(new HashSet()).when(realm).getAnyTypeClasses(); + when(realm.getPlainAttrs()).thenReturn(attrs); + when(attr.getSchema()).thenReturn(NOT_ALLOWED_SCHEMA); + + final InvalidEntityException exception = assertThrows(InvalidEntityException.class, + () -> realmChecker.checkBeforeSave(realm)); + + assertTrue(exception.hasViolation(EntityViolationType.InvalidPlainAttr)); + assertEquals(1, exception.getViolations().size()); + } + + @Test + void checkBeforeSaveWithDuplicateUniqueValue() { + final Realm realm = mock(Realm.class); + final AnyTypeClass anyTypeClass = mock(AnyTypeClass.class); + final PlainSchema plainSchema = mock(PlainSchema.class); + final PlainAttr attr = mock(PlainAttr.class); + final PlainAttrValue uniqueValue = mock(PlainAttrValue.class); + final List plainSchemas = new ArrayList<>(); + plainSchemas.add(plainSchema); + final Set anyTypeClasses = new HashSet<>(); + anyTypeClasses.add(anyTypeClass); + + when(plainSchema.getKey()).thenReturn(ALLOWED_SCHEMA); + doReturn(plainSchemas).when(anyTypeClass).getPlainSchemas(); + doReturn(anyTypeClasses).when(realm).getAnyTypeClasses(); + when(realm.getPlainAttrs()).thenReturn(List.of(attr)); + when(realm.getKey()).thenReturn("realm-key"); + when(attr.getSchema()).thenReturn(ALLOWED_SCHEMA); + when(attr.getUniqueValue()).thenReturn(uniqueValue); + doReturn(Optional.of(plainSchema)).when(plainSchemaDAO).findById(ALLOWED_SCHEMA); + when(plainSchemaDAO.existsPlainAttrUniqueValue("realm-key", plainSchema, uniqueValue)).thenReturn(true); + + assertThrows(DuplicateException.class, () -> realmChecker.checkBeforeSave(realm)); + verify(plainSchemaDAO).existsPlainAttrUniqueValue("realm-key", plainSchema, uniqueValue); + } +} diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/AnyUtilsFactoryTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/AnyUtilsFactoryTest.java new file mode 100644 index 00000000000..fbce9cae8d5 --- /dev/null +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/AnyUtilsFactoryTest.java @@ -0,0 +1,63 @@ +/* + * 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 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.core.persistence.api.entity; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; + +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.junit.jupiter.api.Test; + +class AnyUtilsFactoryTest { + + @Test + void getInstanceByAnyTypeKind() { + final AnyUtils userAnyUtils = mock(AnyUtils.class); + final AnyUtils groupAnyUtils = mock(AnyUtils.class); + final AnyUtils anyObjectAnyUtils = mock(AnyUtils.class); + final AnyUtilsFactory factory = new AnyUtilsFactory(userAnyUtils, groupAnyUtils, anyObjectAnyUtils); + + assertSame(userAnyUtils, factory.getInstance(AnyTypeKind.USER)); + assertSame(groupAnyUtils, factory.getInstance(AnyTypeKind.GROUP)); + assertSame(anyObjectAnyUtils, factory.getInstance(AnyTypeKind.ANY_OBJECT)); + } + + @Test + void getInstanceByAny() { + final AnyUtils userAnyUtils = mock(AnyUtils.class); + final AnyUtils groupAnyUtils = mock(AnyUtils.class); + final AnyUtils anyObjectAnyUtils = mock(AnyUtils.class); + final AnyUtilsFactory factory = new AnyUtilsFactory(userAnyUtils, groupAnyUtils, anyObjectAnyUtils); + + assertSame(userAnyUtils, factory.getInstance(mock(User.class))); + assertSame(groupAnyUtils, factory.getInstance(mock(Group.class))); + assertSame(anyObjectAnyUtils, factory.getInstance(mock(AnyObject.class))); + } + + @Test + void getInstanceByInvalidAny() { + final AnyUtilsFactory factory = new AnyUtilsFactory( + mock(AnyUtils.class), mock(AnyUtils.class), mock(AnyUtils.class)); + + assertThrows(IllegalArgumentException.class, () -> factory.getInstance(mock(Any.class))); + } +} diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/PlainAttrTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/PlainAttrTest.java new file mode 100644 index 00000000000..637f5f2ede2 --- /dev/null +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/PlainAttrTest.java @@ -0,0 +1,111 @@ +/* + * 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 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.core.persistence.api.entity; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class PlainAttrTest { + + private PlainAttr plainAttr; + + private PlainSchema plainSchema; + + @BeforeEach + void setUp() { + plainAttr = new PlainAttr(); + plainSchema = org.mockito.Mockito.mock(PlainSchema.class); + org.mockito.Mockito.when(plainSchema.getKey()).thenReturn("schema"); + } + + @Test + void setPlainSchemaSetsSchema() { + plainAttr.setPlainSchema(plainSchema); + + assertEquals("schema", plainAttr.getSchema()); + } + + @Test + void addAddsValueToValues() { + final PlainAttrValue value = new PlainAttrValue(); + + plainAttr.add(value); + + assertEquals(1, plainAttr.getValues().size()); + } + + @Test + void getValuesAsStringsWithoutUniqueValue() { + final PlainAttrValue value = new PlainAttrValue(); + value.setStringValue("stored"); + plainAttr.add(value); + + final List result = plainAttr.getValuesAsStrings(); + + assertEquals(List.of("stored"), result); + } + + @Test + void getValuesAsStringsWithUniqueValue() { + final PlainAttrValue value = new PlainAttrValue(); + value.setStringValue("stored"); + plainAttr.setUniqueValue(value); + + final List result = plainAttr.getValuesAsStrings(); + + assertEquals(List.of("stored"), result); + } + + @Test + void isValidReturnsFalseWhenMembershipAndRelationshipAreSet() { + plainAttr.setMembership("membership"); + plainAttr.setRelationship("relationship"); + + assertFalse(plainAttr.isValid()); + } + + @Test + void isValidReturnsFalseWhenNoValueIsSet() { + plainAttr.setPlainSchema(plainSchema); + + assertFalse(plainAttr.isValid()); + } + + @Test + void isValidReturnsTrueWhenValueIsSet() { + plainAttr.setPlainSchema(plainSchema); + final PlainAttrValue value = new PlainAttrValue(); + value.setStringValue("stored"); + plainAttr.add(value); + + assertTrue(plainAttr.isValid()); + } + + @Test + void toStringContainsDetails() { + plainAttr.setSchema("schema"); + + assertNotNull(plainAttr.toString()); + } +} diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/PlainAttrValueTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/PlainAttrValueTest.java new file mode 100644 index 00000000000..d501efe0f44 --- /dev/null +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/PlainAttrValueTest.java @@ -0,0 +1,232 @@ +/* + * 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 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.core.persistence.api.entity; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +import java.util.Base64; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.core.persistence.api.ApplicationContextProvider; +import org.apache.syncope.core.persistence.api.Encryptor; +import org.apache.syncope.core.persistence.api.EncryptorManager; +import org.apache.syncope.core.persistence.api.attrvalue.ParsingValidationException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.context.ConfigurableApplicationContext; + +@ExtendWith(MockitoExtension.class) +class PlainAttrValueTest { + + @Test + void getStringValueReturnsEmptyStringWhenNoValueIsSet() { + assertEquals("", new PlainAttrValue().getStringValue()); + } + + @Test + void setBinaryValueReplacesBinaryValue() { + final PlainAttrValue value = new PlainAttrValue(); + value.setBinaryValue(new byte[] { 1, 2, 3 }); + + assertArrayEquals(new byte[] { 1, 2, 3 }, value.getBinaryValue()); + } + + @Test + void parseValueByBooleanType() { + final PlainAttrValue value = new PlainAttrValue(); + final PlainSchema schema = mock(PlainSchema.class); + when(schema.getType()).thenReturn(AttrSchemaType.Boolean); + + value.parseValue(schema, "true"); + + assertEquals(Boolean.TRUE, value.getBooleanValue()); + } + + @Test + void parseValueByLongType() { + final PlainAttrValue value = new PlainAttrValue(); + final PlainSchema schema = mock(PlainSchema.class); + when(schema.getType()).thenReturn(AttrSchemaType.Long); + when(schema.getConversionPattern()).thenReturn(null); + + value.parseValue(schema, "42"); + + assertEquals(42L, value.getLongValue()); + } + + @Test + void parseValueByLongTypeWithInvalidValue() { + final PlainAttrValue value = new PlainAttrValue(); + final PlainSchema schema = mock(PlainSchema.class); + when(schema.getType()).thenReturn(AttrSchemaType.Long); + when(schema.getConversionPattern()).thenReturn(null); + + assertThrows(ParsingValidationException.class, () -> value.parseValue(schema, "not-a-number")); + assertNull(value.getLongValue()); + } + + @Test + void parseValueByDoubleType() { + final PlainAttrValue value = new PlainAttrValue(); + final PlainSchema schema = mock(PlainSchema.class); + when(schema.getType()).thenReturn(AttrSchemaType.Double); + when(schema.getConversionPattern()).thenReturn(null); + + value.parseValue(schema, "3.14"); + + assertEquals(3.14, value.getDoubleValue()); + } + + @Test + void parseValueByDateType() { + final PlainAttrValue value = new PlainAttrValue(); + final PlainSchema schema = mock(PlainSchema.class); + when(schema.getType()).thenReturn(AttrSchemaType.Date); + when(schema.getConversionPattern()).thenReturn(null); + + value.parseValue(schema, "2024-01-15T10:00:00Z"); + + assertNotNull(value.getDateValue()); + } + + @Test + void parseValueByBinaryType() { + final PlainAttrValue value = new PlainAttrValue(); + final PlainSchema schema = mock(PlainSchema.class); + when(schema.getType()).thenReturn(AttrSchemaType.Binary); + + value.parseValue(schema, "AQID"); + + assertArrayEquals(new byte[] { 1, 2, 3 }, value.getBinaryValue()); + } + + @Test + void parseValueByStringType() { + final PlainAttrValue value = new PlainAttrValue(); + final PlainSchema schema = mock(PlainSchema.class); + when(schema.getType()).thenReturn(AttrSchemaType.String); + + value.parseValue(schema, "hello"); + + assertEquals("hello", value.getStringValue()); + } + + @Test + void parseValueByEncryptedType() throws Exception { + final PlainAttrValue value = new PlainAttrValue(); + final PlainSchema schema = mock(PlainSchema.class); + when(schema.getType()).thenReturn(AttrSchemaType.Encrypted); + when(schema.getSecretKey()).thenReturn("secret-key"); + when(schema.getCipherAlgorithm()).thenReturn(CipherAlgorithm.AES); + + try (MockedStatic mocked = mockStatic(ApplicationContextProvider.class)) { + final ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class); + final EncryptorManager encryptorManager = mock(EncryptorManager.class); + final Encryptor encryptor = mock(Encryptor.class); + mocked.when(() -> ApplicationContextProvider.getApplicationContext()).thenReturn(context); + when(context.getBean(EncryptorManager.class)).thenReturn(encryptorManager); + when(encryptorManager.getInstance("secret-key")).thenReturn(encryptor); + doReturn("encoded").when(encryptor).encode(eq("value"), any(CipherAlgorithm.class)); + + value.parseValue(schema, "value"); + + assertEquals("encoded", value.getStringValue()); + } + } + + @Test + void getGenericValueReturnsTypedValue() { + final PlainAttrValue value = new PlainAttrValue(); + value.setBooleanValue(Boolean.TRUE); + + assertEquals(Boolean.TRUE, value.getValue()); + } + + @Test + void getValueAsStringByBooleanType() { + final PlainAttrValue value = new PlainAttrValue(); + value.setBooleanValue(Boolean.TRUE); + final PlainSchema schema = mock(PlainSchema.class); + when(schema.getType()).thenReturn(AttrSchemaType.Boolean); + + assertEquals("true", value.getValueAsString(schema)); + } + + @Test + void getValueAsStringByLongType() { + final PlainAttrValue value = new PlainAttrValue(); + value.setLongValue(42L); + final PlainSchema schema = mock(PlainSchema.class); + when(schema.getType()).thenReturn(AttrSchemaType.Long); + when(schema.getConversionPattern()).thenReturn(null); + + assertEquals("42", value.getValueAsString(schema)); + } + + @Test + void getValueAsStringByBinaryType() { + final PlainAttrValue value = new PlainAttrValue(); + value.setBinaryValue(new byte[] { 1, 2, 3 }); + final PlainSchema schema = mock(PlainSchema.class); + when(schema.getType()).thenReturn(AttrSchemaType.Binary); + + final String expected = Base64.getEncoder().encodeToString(new byte[] { 1, 2, 3 }); + assertEquals(expected, value.getValueAsString(schema)); + } + + @Test + void getValueAsStringByEncryptedType() { + final PlainAttrValue value = new PlainAttrValue(); + value.setStringValue("secret-value"); + final PlainSchema schema = mock(PlainSchema.class); + when(schema.getType()).thenReturn(AttrSchemaType.Encrypted); + + assertEquals("secret-value", value.getValueAsString(schema)); + } + + @Test + void getValueAsStringFallsBackToGetValueWhenTypeValueIsNull() { + final PlainAttrValue value = new PlainAttrValue(); + value.setStringValue("fallback"); + final PlainSchema schema = mock(PlainSchema.class); + when(schema.getType()).thenReturn(AttrSchemaType.Boolean); + + assertEquals("fallback", value.getValueAsString(schema)); + } + + @Test + void toStringContainsDetails() { + final PlainAttrValue value = new PlainAttrValue(); + value.setStringValue("hello"); + + assertTrue(value.toString().contains("PlainAttrValue")); + } +} diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/am/ClientAppUtilsFactoryTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/am/ClientAppUtilsFactoryTest.java new file mode 100644 index 00000000000..e0c837a3667 --- /dev/null +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/am/ClientAppUtilsFactoryTest.java @@ -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 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.core.persistence.api.entity.am; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; + +import org.apache.syncope.common.lib.to.ClientAppTO; +import org.apache.syncope.common.lib.to.SAML2SPClientAppTO; +import org.apache.syncope.common.lib.types.ClientAppType; +import org.junit.jupiter.api.Test; + +class ClientAppUtilsFactoryTest { + + private final ClientAppUtilsFactory factory = new ClientAppUtilsFactory(); + + @Test + void getInstanceByType() { + assertEquals(ClientAppType.SAML2SP, factory.getInstance(ClientAppType.SAML2SP).getType()); + } + + @Test + void getInstanceByClientApp() { + final SAML2SPClientApp saml2SpClientApp = mock(SAML2SPClientApp.class); + assertEquals(ClientAppType.SAML2SP, factory.getInstance(saml2SpClientApp).getType()); + } + + @Test + void getInstanceByInvalidClientApp() { + assertThrows(IllegalArgumentException.class, () -> factory.getInstance(mock(ClientApp.class))); + } + + @Test + void getInstanceByClientAppTOClass() { + final SAML2SPClientAppTO saml2SpClientAppTO = mock(SAML2SPClientAppTO.class); + final ClientAppUtils clientAppUtils = factory.getInstance(saml2SpClientAppTO); + assertEquals(ClientAppType.SAML2SP, clientAppUtils.getType()); + } + + @Test + void getInstanceByInvalidClientAppTOClass() { + assertThrows(IllegalArgumentException.class, () -> factory.getInstance(ClientAppTO.class)); + } +} diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtilsFactoryTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtilsFactoryTest.java new file mode 100644 index 00000000000..9b5e2cd3042 --- /dev/null +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtilsFactoryTest.java @@ -0,0 +1,65 @@ +/* + * 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 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.core.persistence.api.entity.policy; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; + +import org.apache.syncope.common.lib.policy.AccountPolicyTO; +import org.apache.syncope.common.lib.policy.PasswordPolicyTO; +import org.apache.syncope.common.lib.policy.PolicyTO; +import org.apache.syncope.common.lib.types.PolicyType; +import org.junit.jupiter.api.Test; + +class PolicyUtilsFactoryTest { + + private final PolicyUtilsFactory factory = new PolicyUtilsFactory(); + + @Test + void getInstanceByType() { + assertEquals(PolicyType.PROPAGATION, factory.getInstance(PolicyType.PROPAGATION).getType()); + } + + @Test + void getInstanceByPolicy() { + final AccountPolicy accountPolicy = mock(AccountPolicy.class); + assertEquals(PolicyType.ACCOUNT, factory.getInstance(accountPolicy).getType()); + } + + @Test + void getInstanceByInvalidPolicy() { + assertThrows(IllegalArgumentException.class, () -> factory.getInstance(mock(Policy.class))); + } + + @Test + void getInstanceByPolicyTOClass() { + assertEquals(PolicyType.PASSWORD, factory.getInstance(PasswordPolicyTO.class).getType()); + } + + @Test + void getInstanceByInvalidPolicyTOClass() { + assertThrows(IllegalArgumentException.class, () -> factory.getInstance(PolicyTO.class)); + } + + @Test + void getInstanceByPolicyTO() { + final AccountPolicyTO accountPolicyTO = mock(AccountPolicyTO.class); + assertEquals(PolicyType.ACCOUNT, factory.getInstance(accountPolicyTO).getType()); + } +} diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtilsTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtilsTest.java new file mode 100644 index 00000000000..45a4c56fe61 --- /dev/null +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtilsTest.java @@ -0,0 +1,71 @@ +/* + * 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 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.core.persistence.api.entity.policy; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.apache.syncope.common.lib.types.PolicyType; +import org.junit.jupiter.api.Test; + +class PolicyUtilsTest { + + @Test + void policyClassForAccount() { + assertEquals(AccountPolicy.class, new PolicyUtils(PolicyType.ACCOUNT).policyClass()); + } + + @Test + void policyClassForPassword() { + assertEquals(PasswordPolicy.class, new PolicyUtils(PolicyType.PASSWORD).policyClass()); + } + + @Test + void policyClassForAuth() { + assertEquals(AuthPolicy.class, new PolicyUtils(PolicyType.AUTH).policyClass()); + } + + @Test + void policyClassForAttrRelease() { + assertEquals(AttrReleasePolicy.class, new PolicyUtils(PolicyType.ATTR_RELEASE).policyClass()); + } + + @Test + void policyClassForAccess() { + assertEquals(AccessPolicy.class, new PolicyUtils(PolicyType.ACCESS).policyClass()); + } + + @Test + void policyClassForTicketExpiration() { + assertEquals(TicketExpirationPolicy.class, new PolicyUtils(PolicyType.TICKET_EXPIRATION).policyClass()); + } + + @Test + void policyClassForPropagation() { + assertEquals(PropagationPolicy.class, new PolicyUtils(PolicyType.PROPAGATION).policyClass()); + } + + @Test + void policyClassForInbound() { + assertEquals(InboundPolicy.class, new PolicyUtils(PolicyType.INBOUND).policyClass()); + } + + @Test + void policyClassForPush() { + assertEquals(PushPolicy.class, new PolicyUtils(PolicyType.PUSH).policyClass()); + } +} diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/task/PropagationDataTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/task/PropagationDataTest.java new file mode 100644 index 00000000000..9f8c373d381 --- /dev/null +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/entity/task/PropagationDataTest.java @@ -0,0 +1,71 @@ +/* + * 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 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.core.persistence.api.entity.task; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashSet; +import java.util.Set; +import org.identityconnectors.framework.common.objects.Attribute; +import org.identityconnectors.framework.common.objects.AttributeDelta; +import org.junit.jupiter.api.Test; + +class PropagationDataTest { + + @Test + void attributesAreSetFromConstructor() { + final Set attributes = new HashSet<>(); + final PropagationData propagationData = new PropagationData(attributes); + + assertEquals(attributes, propagationData.getAttributes()); + } + + @Test + void attributeDeltasAreInitiallyNull() { + final PropagationData propagationData = new PropagationData(new HashSet<>()); + + assertNull(propagationData.getAttributeDeltas()); + } + + @Test + void attributeDeltasAreSettable() { + final PropagationData propagationData = new PropagationData(new HashSet<>()); + final Set attributeDeltas = new HashSet<>(); + + propagationData.setAttributeDeltas(attributeDeltas); + + assertEquals(attributeDeltas, propagationData.getAttributeDeltas()); + } + + @Test + void toStringContainsAttributesAndDeltas() { + final Set attributes = new HashSet<>(); + final Set attributeDeltas = new HashSet<>(); + final PropagationData propagationData = new PropagationData(attributes); + propagationData.setAttributeDeltas(attributeDeltas); + + final String toString = propagationData.toString(); + + assertTrue(toString.contains("attributes")); + assertTrue(toString.contains("attributeDeltas")); + assertNotNull(toString); + } +} diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/FilterConverterTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/FilterConverterTest.java index 9de775ef001..11f9f33e739 100644 --- a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/FilterConverterTest.java +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/FilterConverterTest.java @@ -38,7 +38,7 @@ import org.identityconnectors.framework.common.objects.filter.OrFilter; import org.junit.jupiter.api.Test; -public class FilterConverterTest { +class FilterConverterTest { private boolean equals(final Filter filter1, final Filter filter2) { return EqualsBuilder.reflectionEquals(filter1, filter2); @@ -58,7 +58,7 @@ private boolean equals(final List filters1, final List filters2) } @Test - public void eq() { + void eq() { String fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("username").equalTo("rossini").query(); assertEquals("username==rossini", fiql); @@ -68,7 +68,7 @@ public void eq() { } @Test - public void ieq() { + void ieq() { String fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("username").equalToIgnoreCase("rossini").query(); assertEquals("username=~rossini", fiql); @@ -78,7 +78,7 @@ public void ieq() { } @Test - public void nieq() { + void nieq() { String fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("username").notEqualTolIgnoreCase("rossini"). query(); assertEquals("username!~rossini", fiql); @@ -95,7 +95,7 @@ public void nieq() { } @Test - public void like() { + void like() { String fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("username").equalTo("ros*").query(); assertEquals("username==ros*", fiql); @@ -122,7 +122,7 @@ public void like() { } @Test - public void ilike() { + void ilike() { String fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("username").equalToIgnoreCase("ros*").query(); assertEquals("username=~ros*", fiql); @@ -135,7 +135,7 @@ public void ilike() { } @Test - public void nilike() { + void nilike() { String fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("username").notEqualTolIgnoreCase("ros*").query(); assertEquals("username!~ros*", fiql); @@ -148,7 +148,7 @@ public void nilike() { } @Test - public void isNull() { + void isNull() { String fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("loginDate").nullValue().query(); assertEquals("loginDate==" + SpecialAttr.NULL, fiql); @@ -163,7 +163,7 @@ public void isNull() { } @Test - public void isNotNull() { + void isNotNull() { String fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("loginDate").notNullValue().query(); assertEquals("loginDate!=" + SpecialAttr.NULL, fiql); @@ -173,7 +173,7 @@ public void isNotNull() { } @Test - public void hasAuxClasses() { + void hasAuxClasses() { try { FilterConverter.convert(SpecialAttr.AUX_CLASSES + "==clazz1"); fail(); @@ -183,7 +183,7 @@ public void hasAuxClasses() { } @Test - public void hasNotAuxClasses() { + void hasNotAuxClasses() { try { FilterConverter.convert(SpecialAttr.AUX_CLASSES + "!=clazz1"); fail(); @@ -193,7 +193,7 @@ public void hasNotAuxClasses() { } @Test - public void hasResources() { + void hasResources() { try { FilterConverter.convert(SpecialAttr.RESOURCES + "==resource"); fail(); @@ -203,7 +203,7 @@ public void hasResources() { } @Test - public void hasNotResources() { + void hasNotResources() { try { FilterConverter.convert(SpecialAttr.RESOURCES + "!=resource"); fail(); @@ -213,7 +213,7 @@ public void hasNotResources() { } @Test - public void and() { + void and() { String fiql = new ConnObjectTOFiqlSearchConditionBuilder(). is("fullname").equalTo("ro*").and("fullname").equalTo("*i").query(); assertEquals("fullname==ro*;fullname==*i", fiql); @@ -232,7 +232,7 @@ public void and() { } @Test - public void or() { + void or() { String fiql = new ConnObjectTOFiqlSearchConditionBuilder(). is("fullname").equalTo("ro*").or("fullname").equalTo("*i").query(); assertEquals("fullname==ro*,fullname==*i", fiql); @@ -251,7 +251,7 @@ public void or() { } @Test - public void issueSYNCOPE1223() { + void issueSYNCOPE1223() { String fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("ctype").equalTo("ou=sample%252Co=isp").query(); Filter filter = FilterBuilder.equalTo(AttributeBuilder.build("ctype", "ou=sample,o=isp")); diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java index 72ce4b59b34..08b653b3d94 100644 --- a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java @@ -37,12 +37,12 @@ import org.apache.syncope.core.persistence.api.dao.search.SearchCond; import org.junit.jupiter.api.Test; -public class SearchCondConverterTest { +class SearchCondConverterTest { private static final AnySearchCondVisitor VISITOR = new AnySearchCondVisitor(); @Test - public void eq() { + void eq() { String fiql = new UserFiqlSearchConditionBuilder().is("username").equalTo("rossini").query(); assertEquals("username==rossini", fiql); @@ -55,7 +55,7 @@ public void eq() { } @Test - public void ieq() { + void ieq() { String fiql = new UserFiqlSearchConditionBuilder().is("username").equalToIgnoreCase("rossini").query(); assertEquals("username=~rossini", fiql); @@ -68,7 +68,7 @@ public void ieq() { } @Test - public void nieq() { + void nieq() { String fiql = new UserFiqlSearchConditionBuilder().is("username").notEqualTolIgnoreCase("rossini").query(); assertEquals("username!~rossini", fiql); @@ -81,7 +81,7 @@ public void nieq() { } @Test - public void like() { + void like() { String fiql = new UserFiqlSearchConditionBuilder().is("username").equalTo("ros*").query(); assertEquals("username==ros*", fiql); @@ -94,7 +94,7 @@ public void like() { } @Test - public void ilike() { + void ilike() { String fiql = new UserFiqlSearchConditionBuilder().is("username").equalToIgnoreCase("ros*").query(); assertEquals("username=~ros*", fiql); @@ -107,7 +107,7 @@ public void ilike() { } @Test - public void nilike() { + void nilike() { String fiql = new UserFiqlSearchConditionBuilder().is("username").notEqualTolIgnoreCase("ros*").query(); assertEquals("username!~ros*", fiql); @@ -120,7 +120,7 @@ public void nilike() { } @Test - public void isNull() { + void isNull() { String fiql = new UserFiqlSearchConditionBuilder().is("loginDate").nullValue().query(); assertEquals("loginDate==" + SpecialAttr.NULL, fiql); @@ -132,7 +132,7 @@ public void isNull() { } @Test - public void isNotNull() { + void isNotNull() { String fiql = new UserFiqlSearchConditionBuilder().is("loginDate").notNullValue().query(); assertEquals("loginDate!=" + SpecialAttr.NULL, fiql); @@ -144,7 +144,7 @@ public void isNotNull() { } @Test - public void relationships() { + void relationships() { String fiql = new UserFiqlSearchConditionBuilder(). inRelationships("ca20ffca-1305-442f-be9a-3723a0cd88ca").query(); assertEquals(SpecialAttr.RELATIONSHIPS + "==ca20ffca-1305-442f-be9a-3723a0cd88ca", fiql); @@ -157,7 +157,7 @@ public void relationships() { } @Test - public void relationshipTypes() { + void relationshipTypes() { String fiql = new UserFiqlSearchConditionBuilder().inRelationshipTypes("type1").query(); assertEquals(SpecialAttr.RELATIONSHIP_TYPES + "==type1", fiql); @@ -174,7 +174,7 @@ public void relationshipTypes() { } @Test - public void groups() { + void groups() { String fiql = new UserFiqlSearchConditionBuilder(). inGroups("e7ff94e8-19c9-4f0a-b8b7-28327edbf6ed").query(); assertEquals(SpecialAttr.GROUPS + "==e7ff94e8-19c9-4f0a-b8b7-28327edbf6ed", fiql); @@ -187,7 +187,7 @@ public void groups() { } @Test - public void roles() { + void roles() { String fiql = new UserFiqlSearchConditionBuilder().inRoles("User reviewer").query(); assertEquals(SpecialAttr.ROLES + "==User reviewer", fiql); @@ -199,7 +199,7 @@ public void roles() { } @Test - public void auxClasses() { + void auxClasses() { String fiql = new UserFiqlSearchConditionBuilder().hasAuxClasses("clazz1").query(); assertEquals(SpecialAttr.AUX_CLASSES + "==clazz1", fiql); @@ -211,7 +211,7 @@ public void auxClasses() { } @Test - public void resources() { + void resources() { String fiql = new UserFiqlSearchConditionBuilder().hasResources("resource-ldap").query(); assertEquals(SpecialAttr.RESOURCES + "==resource-ldap", fiql); @@ -223,7 +223,7 @@ public void resources() { } @Test - public void type() { + void type() { String fiql = new AnyObjectFiqlSearchConditionBuilder("PRINTER").query(); assertEquals(SpecialAttr.TYPE + "==PRINTER", fiql); @@ -235,7 +235,7 @@ public void type() { } @Test - public void member() { + void member() { String fiql = new GroupFiqlSearchConditionBuilder().withMembers("rossini").query(); assertEquals(SpecialAttr.MEMBER + "==rossini", fiql); @@ -247,7 +247,7 @@ public void member() { } @Test - public void and() { + void and() { String fiql = new UserFiqlSearchConditionBuilder(). is("fullname").equalTo("*o*").and("fullname").equalTo("*i*").query(); assertEquals("fullname==*o*;fullname==*i*", fiql); @@ -266,7 +266,7 @@ public void and() { } @Test - public void or() { + void or() { String fiql = new UserFiqlSearchConditionBuilder(). is("fullname").equalTo("*o*", "*i*", "*ini").query(); assertEquals("fullname==*o*,fullname==*i*,fullname==*ini", fiql); @@ -292,7 +292,7 @@ public void or() { } @Test - public void issueSYNCOPE1223() { + void issueSYNCOPE1223() { String fiql = new UserFiqlSearchConditionBuilder().is("ctype").equalTo("ou=sample%252Co=isp").query(); AttrCond cond = new AttrCond(AttrCond.Type.EQ); @@ -303,7 +303,7 @@ public void issueSYNCOPE1223() { } @Test - public void issueSYNCOPE1779() { + void issueSYNCOPE1779() { String fiql = new UserFiqlSearchConditionBuilder().is("username").equalToIgnoreCase("ros_*").query(); assertEquals("username=~ros_*", fiql); @@ -325,7 +325,7 @@ public void issueSYNCOPE1779() { } @Test - public void issueSYNCOPE1826() { + void issueSYNCOPE1826() { String fiql = new UserFiqlSearchConditionBuilder().is("username").equalToIgnoreCase("sh test app 0722").query(); assertEquals("username=~sh test app 0722", fiql); diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SyncopePageTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SyncopePageTest.java new file mode 100644 index 00000000000..40d215bbcba --- /dev/null +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SyncopePageTest.java @@ -0,0 +1,73 @@ +/* + * 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 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.core.persistence.api.search; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import java.util.List; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +class SyncopePageTest { + + @Test + void getTotalElementsReturnsTotal() { + final SyncopePage page = new SyncopePage<>(List.of("a"), PageRequest.of(0, 10), 42L); + + assertEquals(42L, page.getTotalElements()); + } + + @Test + void getTotalPagesReturnsOneWhenSizeIsZero() { + final Pageable pageable = Mockito.mock(Pageable.class); + final SyncopePage page = new SyncopePage<>(List.of(), pageable, 42L); + + assertEquals(1, page.getTotalPages()); + } + + @Test + void getTotalPagesComputesTotalPages() { + final SyncopePage page = new SyncopePage<>(List.of(), PageRequest.of(0, 20), 50L); + + assertEquals(3, page.getTotalPages()); + } + + @Test + void equalsReturnsTrueForEqualPages() { + final SyncopePage page = new SyncopePage<>(List.of("a"), PageRequest.of(0, 10), 42L); + + assertEquals(page, new SyncopePage<>(List.of("a"), PageRequest.of(0, 10), 42L)); + } + + @Test + void equalsReturnsFalseForDifferentPages() { + final SyncopePage page = new SyncopePage<>(List.of("a"), PageRequest.of(0, 10), 42L); + + assertNotEquals(page, new SyncopePage<>(List.of("b"), PageRequest.of(0, 10), 42L)); + } + + @Test + void hashCodeReturnsSameValueForEqualPages() { + final SyncopePage page = new SyncopePage<>(List.of("a"), PageRequest.of(0, 10), 42L); + + assertEquals(page.hashCode(), new SyncopePage<>(List.of("a"), PageRequest.of(0, 10), 42L).hashCode()); + } +} diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/AbstractTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/AbstractTest.java index 45b9ce254f1..862e2e431e8 100644 --- a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/AbstractTest.java +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/AbstractTest.java @@ -28,10 +28,10 @@ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.WARN) @SuppressWarnings("squid:S2187") -public class AbstractTest { +class AbstractTest { @BeforeEach - public void init() { + void init() { MockitoAnnotations.openMocks(this); } } diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/FormatUtilsTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/FormatUtilsTest.java index 78baeb9fa4c..6e27b7d0213 100644 --- a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/FormatUtilsTest.java +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/FormatUtilsTest.java @@ -32,14 +32,14 @@ import java.util.Locale; import org.junit.jupiter.api.Test; -public class FormatUtilsTest extends AbstractTest { +class FormatUtilsTest extends AbstractTest { private static final SecureRandom RANDOM = new SecureRandom(); private static final OffsetDateTime DATE = OffsetDateTime.now(); @Test - public void formatDate() { + void formatDate() { assertEquals( DATE.truncatedTo(ChronoUnit.SECONDS).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME), FormatUtils.format(DATE)); @@ -51,7 +51,7 @@ public void formatDate() { } @Test - public void formatLongNumber() { + void formatLongNumber() { long number = RANDOM.nextLong(); DecimalFormat df = new DecimalFormat(); df.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ENGLISH)); @@ -64,7 +64,7 @@ public void formatLongNumber() { } @Test - public void formatDoubleNumber() { + void formatDoubleNumber() { double number = RANDOM.nextDouble(); DecimalFormat df = new DecimalFormat(); df.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ENGLISH)); @@ -77,7 +77,7 @@ public void formatDoubleNumber() { } @Test - public void parseDate() throws ParseException { + void parseDate() throws ParseException { String source = DATE.truncatedTo(ChronoUnit.SECONDS).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); assertEquals( OffsetDateTime.parse(source, DateTimeFormatter.ISO_OFFSET_DATE_TIME), @@ -92,7 +92,7 @@ public void parseDate() throws ParseException { } @Test - public void parseNumber() throws ParseException { + void parseNumber() throws ParseException { String source = String.valueOf(RANDOM.nextLong()); String conversionPattern = "###,###"; assertEquals(Long.valueOf(source), FormatUtils.parseNumber(source, conversionPattern)); diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/RealmUtilsTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/RealmUtilsTest.java index e930384b6f9..f6066dba908 100644 --- a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/RealmUtilsTest.java +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/RealmUtilsTest.java @@ -28,10 +28,10 @@ import org.apache.syncope.common.lib.types.AnyTypeKind; import org.junit.jupiter.api.Test; -public class RealmUtilsTest extends AbstractTest { +class RealmUtilsTest extends AbstractTest { @Test - public void getManagerRealm() { + void getManagerRealm() { String realmPath = "realmPath"; String groupKey = "groupKey"; assertEquals( @@ -40,7 +40,7 @@ public void getManagerRealm() { } @Test - public void parseGroupOwnerRealm() { + void parseGroupOwnerRealm() { assertEquals( Optional.of(new RealmUtils.ManagerRealm("realmPath", AnyTypeKind.GROUP, "groupKey")), RealmUtils.ManagerRealm.of("realmPath@GROUP@groupKey")); @@ -48,7 +48,7 @@ public void parseGroupOwnerRealm() { } @Test - public void normalizingAddTo() { + void normalizingAddTo() { Set realms = new HashSet<>(); realms.add("/realm1"); realms.add("/realm2"); @@ -65,7 +65,7 @@ public void normalizingAddTo() { } @Test - public void getEffective() { + void getEffective() { Set allowedRealms = Set.of( "testRealm1@k1", "testRealm2@k2", diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/URIUtilsTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/URIUtilsTest.java index 10b8559c5d5..237624a4808 100644 --- a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/URIUtilsTest.java +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/URIUtilsTest.java @@ -29,10 +29,10 @@ import org.apache.commons.lang3.mutable.MutableObject; import org.junit.jupiter.api.Test; -public class URIUtilsTest extends AbstractTest { +class URIUtilsTest extends AbstractTest { @Test - public void buildForConnId() throws URISyntaxException, MalformedURLException { + void buildForConnId() throws URISyntaxException, MalformedURLException { Mutable location = new MutableObject<>(); location.setValue("www.tirasa.net"); IllegalArgumentException exception = diff --git a/core/persistence-common/pom.xml b/core/persistence-common/pom.xml index 345af790c92..9adc5b43519 100644 --- a/core/persistence-common/pom.xml +++ b/core/persistence-common/pom.xml @@ -53,6 +53,13 @@ under the License. org.apache.commons commons-jexl3 + + + + org.springframework.boot + spring-boot-starter-test + test + diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainSchemaValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainSchemaValidator.java index a60a29ec9b4..80ea6323987 100644 --- a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainSchemaValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainSchemaValidator.java @@ -18,13 +18,12 @@ */ package org.apache.syncope.core.persistence.common.validation; -import static org.apache.syncope.core.persistence.api.entity.PlainAttrValue.SPRING_ENV_PROPERTY; - import jakarta.validation.ConstraintValidatorContext; import java.util.Optional; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.apache.syncope.common.lib.types.EntityViolationType; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; import org.apache.syncope.core.persistence.api.entity.PlainSchema; public class PlainSchemaValidator extends AbstractValidator { @@ -62,7 +61,7 @@ public boolean isValid(final PlainSchema schema, final ConstraintValidatorContex } if (schema.getCipherAlgorithm() == CipherAlgorithm.AES && !SyncopeConstants.ENCRYPTED_DECODE_CONVERSION_PATTERN.equals(schema.getConversionPattern()) - && !SPRING_ENV_PROPERTY.matcher(schema.getSecretKey()).matches() + && !PlainAttrValue.SPRING_ENV_PROPERTY.matcher(schema.getSecretKey()).matches() && Optional.ofNullable(schema.getSecretKey()). map(key -> key.length() != 16 && key.length() != 24 && key.length() != 32). orElse(true)) { diff --git a/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/RuntimeDomainLoaderTest.java b/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/RuntimeDomainLoaderTest.java new file mode 100644 index 00000000000..6d7a868d57a --- /dev/null +++ b/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/RuntimeDomainLoaderTest.java @@ -0,0 +1,204 @@ +/* + * 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.core.persistence.common; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; +import org.apache.syncope.common.keymaster.client.api.DomainOps; +import org.apache.syncope.common.keymaster.client.api.model.Domain; +import org.apache.syncope.core.persistence.api.DomainHolder; +import org.apache.syncope.core.persistence.api.DomainRegistry; +import org.apache.syncope.core.persistence.api.SyncopeCoreLoader; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ConfigurableApplicationContext; + +@SuppressWarnings("unchecked") +@ExtendWith(MockitoExtension.class) +class RuntimeDomainLoaderTest { + + @Mock + private DomainHolder domainHolder; + + @Mock + private DomainRegistry domainRegistry; + + @Mock + private DomainOps domainOps; + + @Mock + private ConfigurableApplicationContext ctx; + + @Mock + private ConfigurableListableBeanFactory beanFactory; + + private RuntimeDomainLoader loader( + final Map domains) { + + when(domainHolder.getDomains()).thenReturn((Map) domains); + + return new RuntimeDomainLoader<>(domainHolder, domainRegistry, domainOps, ctx); + } + + @Test + void addedDomainAlreadyInitedSkipped() { + Map domains = new HashMap<>(); + domains.put("existing", mock(Domain.class)); + + RuntimeDomainLoader loader = loader(domains); + + Domain domain = mock(Domain.class); + when(domain.getKey()).thenReturn("existing"); + + loader.added(domain); + + verify(domainRegistry, never()).register(any()); + verify(domainOps, never()).deployed(anyString()); + } + + @Test + void addedNewDomainRegistersAndDeploys() { + Map domains = new HashMap<>(); + Domain domain = mock(Domain.class); + when(domain.getKey()).thenReturn("new-domain"); + + RuntimeDomainLoader loader = loader(domains); + + when(ctx.getBeanFactory()).thenReturn(beanFactory); + when(beanFactory.getBeansOfType(SyncopeCoreLoader.class)). + thenReturn(Map.of()); + + loader.added(domain); + + verify(domainRegistry).register(domain); + verify(domainOps).deployed("new-domain"); + } + + @Test + void addedNewDomainCallsLoaders() { + Map domains = new HashMap<>(); + Domain domain = mock(Domain.class); + when(domain.getKey()).thenReturn("new-domain"); + + SyncopeCoreLoader loader1 = mock(SyncopeCoreLoader.class); + when(loader1.getOrder()).thenReturn(1); + + SyncopeCoreLoader loader2 = mock(SyncopeCoreLoader.class); + when(loader2.getOrder()).thenReturn(2); + + Map loaders = new HashMap<>(); + loaders.put("loader1", loader1); + loaders.put("loader2", loader2); + + RuntimeDomainLoader runtimeLoader = loader(domains); + + when(ctx.getBeanFactory()).thenReturn(beanFactory); + when(beanFactory.getBeansOfType(SyncopeCoreLoader.class)).thenReturn(loaders); + + runtimeLoader.added(domain); + + verify(loader1).load("new-domain"); + verify(loader2).load("new-domain"); + verify(domainOps).deployed("new-domain"); + } + + @Test + void removedDomainNotInitedSkipped() { + Map domains = new HashMap<>(); + domains.put("other", mock(Domain.class)); + + RuntimeDomainLoader loader = loader(domains); + + loader.removed("missing-domain"); + + verify(domainRegistry, never()).unregister(anyString()); + assertTrue(domains.containsKey("other")); + assertFalse(domains.containsKey("missing-domain")); + } + + @Test + void removedDomainUnregistersAndUndeploys() { + Map domains = new HashMap<>(); + domains.put("existing-domain", mock(Domain.class)); + + SyncopeCoreLoader loader1 = mock(SyncopeCoreLoader.class); + + Map loaders = new HashMap<>(); + loaders.put("loader1", loader1); + + RuntimeDomainLoader runtimeLoader = loader(domains); + + when(ctx.getBeanFactory()).thenReturn(beanFactory); + when(beanFactory.getBeansOfType(SyncopeCoreLoader.class)).thenReturn(loaders); + + runtimeLoader.removed("existing-domain"); + + verify(loader1).unload("existing-domain"); + verify(domainRegistry).unregister("existing-domain"); + assertFalse(domains.containsKey("existing-domain")); + } + + @Test + void removedDomainCallsLoadersInReverseOrder() { + Map domains = new HashMap<>(); + domains.put("existing-domain", mock(Domain.class)); + + SyncopeCoreLoader loader1 = mock(SyncopeCoreLoader.class); + when(loader1.getOrder()).thenReturn(1); + + SyncopeCoreLoader loader2 = mock(SyncopeCoreLoader.class); + when(loader2.getOrder()).thenReturn(2); + + Map loaders = new HashMap<>(); + loaders.put("loader1", loader1); + loaders.put("loader2", loader2); + + RuntimeDomainLoader runtimeLoader = loader(domains); + + when(ctx.getBeanFactory()).thenReturn(beanFactory); + when(beanFactory.getBeansOfType(SyncopeCoreLoader.class)).thenReturn(loaders); + + runtimeLoader.removed("existing-domain"); + + verify(loader1, atLeastOnce()).unload("existing-domain"); + verify(loader2, atLeastOnce()).unload("existing-domain"); + verify(domainRegistry).unregister("existing-domain"); + assertFalse(domains.containsKey("existing-domain")); + } + + @Test + void constructorDoesNotThrow() { + assertDoesNotThrow(() -> new RuntimeDomainLoader<>( + domainHolder, domainRegistry, domainOps, ctx)); + } +} diff --git a/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/attrvalue/AlwaysTrueValidatorTest.java b/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/attrvalue/AlwaysTrueValidatorTest.java new file mode 100644 index 00000000000..8d04de22aa6 --- /dev/null +++ b/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/attrvalue/AlwaysTrueValidatorTest.java @@ -0,0 +1,61 @@ +/* + * 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.core.persistence.common.attrvalue; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.apache.syncope.core.persistence.api.attrvalue.InvalidPlainAttrValueException; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class AlwaysTrueValidatorTest { + + @Mock + private PlainSchema schema; + + private final AlwaysTrueValidator validator = new AlwaysTrueValidator(); + + @Test + void doValidateTrueValue() { + PlainAttrValue attrValue = new PlainAttrValue(); + attrValue.setBooleanValue(true); + + assertDoesNotThrow(() -> validator.doValidate(schema, attrValue)); + } + + @Test + void doValidateFalseValue() { + PlainAttrValue attrValue = new PlainAttrValue(); + attrValue.setBooleanValue(false); + + InvalidPlainAttrValueException ex = assertThrows( + InvalidPlainAttrValueException.class, + () -> validator.doValidate(schema, attrValue)); + + assertEquals("This attribute must be set to \"true\"", ex.getMessage()); + } + +} diff --git a/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/attrvalue/BasicValidatorTest.java b/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/attrvalue/BasicValidatorTest.java new file mode 100644 index 00000000000..18328e09ada --- /dev/null +++ b/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/attrvalue/BasicValidatorTest.java @@ -0,0 +1,45 @@ +/* + * 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.core.persistence.common.attrvalue; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class BasicValidatorTest { + + @Mock + private PlainSchema schema; + + private final BasicValidator validator = new BasicValidator(); + + @Test + void doValidateNoOp() { + PlainAttrValue attrValue = new PlainAttrValue(); + attrValue.setStringValue("anything"); + + assertDoesNotThrow(() -> validator.doValidate(schema, attrValue)); + } +} diff --git a/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/attrvalue/BinaryValidatorTest.java b/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/attrvalue/BinaryValidatorTest.java new file mode 100644 index 00000000000..6887b9e7705 --- /dev/null +++ b/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/attrvalue/BinaryValidatorTest.java @@ -0,0 +1,141 @@ +/* + * 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.core.persistence.common.attrvalue; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.MediaType; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidPlainAttrValueException; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class BinaryValidatorTest { + + private static final String JSON_MIME_TYPE = MediaType.APPLICATION_JSON; + + @Mock + private PlainSchema schema; + + private BinaryValidator validator() { + return new BinaryValidator(); + } + + @Test + void doValidateNullBinaryValue() { + PlainAttrValue attrValue = new PlainAttrValue(); + + assertDoesNotThrow(() -> validator().doValidate(schema, attrValue)); + } + + @Test + void doValidateMatchingMimeType() { + when(schema.getMimeType()).thenReturn(JSON_MIME_TYPE); + + PlainAttrValue attrValue = new PlainAttrValue(); + attrValue.setBinaryValue("{\"key\":\"value\"}".getBytes(StandardCharsets.UTF_8)); + + assertDoesNotThrow(() -> validator().doValidate(schema, attrValue)); + } + + @Test + void doValidateTextPlainSchemaJsonValidJsonObject() { + when(schema.getMimeType()).thenReturn(JSON_MIME_TYPE); + + // Tika detects text/plain for plain text; schema expects application/json. + // A JSON object { ... } is accepted (operator precedence makes the object branch + // always valid when wrapped in braces). + PlainAttrValue attrValue = new PlainAttrValue(); + attrValue.setBinaryValue("{not valid json}".getBytes(StandardCharsets.UTF_8)); + + assertDoesNotThrow(() -> validator().doValidate(schema, attrValue)); + } + + @Test + void doValidateTextPlainSchemaJsonNonJsonText() { + when(schema.getMimeType()).thenReturn(JSON_MIME_TYPE); + + PlainAttrValue attrValue = new PlainAttrValue(); + attrValue.setBinaryValue("this is plain text".getBytes(StandardCharsets.UTF_8)); + + InvalidPlainAttrValueException ex = assertThrows( + InvalidPlainAttrValueException.class, + () -> validator().doValidate(schema, attrValue)); + + assertEquals("Found MIME type: 'text/plain', expecting: 'application/json'", ex.getMessage()); + } + + @Test + void doValidateDifferentMimeType() { + when(schema.getMimeType()).thenReturn(JSON_MIME_TYPE); + + // JPEG magic bytes are detected by Tika as image/jpeg. + PlainAttrValue attrValue = new PlainAttrValue(); + attrValue.setBinaryValue(new byte[] { + (byte) 0xFF, (byte) 0xD8, (byte) 0xFF + }); + + InvalidPlainAttrValueException ex = assertThrows( + InvalidPlainAttrValueException.class, + () -> validator().doValidate(schema, attrValue)); + + assertEquals("Found MIME type: 'image/jpeg', expecting: 'application/json'", ex.getMessage()); + } + + @Test + void validateBinaryValue() { + when(schema.getType()).thenReturn(AttrSchemaType.Binary); + when(schema.getMimeType()).thenReturn(JSON_MIME_TYPE); + + String base64 = Base64.getEncoder().encodeToString( + "{\"key\":\"value\"}".getBytes(StandardCharsets.UTF_8)); + + PlainAttrValue attrValue = new PlainAttrValue(); + + assertDoesNotThrow(() -> validator().validate(schema, base64, attrValue)); + } + + @Test + void validateInvalidBinaryValue() { + when(schema.getType()).thenReturn(AttrSchemaType.Binary); + when(schema.getMimeType()).thenReturn(JSON_MIME_TYPE); + + // Base64 of "this is plain text" → Tika detects text/plain, schema expects application/json. + String base64 = Base64.getEncoder().encodeToString( + "this is plain text".getBytes(StandardCharsets.UTF_8)); + + PlainAttrValue attrValue = new PlainAttrValue(); + + InvalidPlainAttrValueException ex = assertThrows( + InvalidPlainAttrValueException.class, + () -> validator().validate(schema, base64, attrValue)); + + assertEquals("Found MIME type: 'text/plain', expecting: 'application/json'", ex.getMessage()); + } +} diff --git a/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/attrvalue/DefaultPlainAttrValidationManagerTest.java b/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/attrvalue/DefaultPlainAttrValidationManagerTest.java new file mode 100644 index 00000000000..78d6b73a002 --- /dev/null +++ b/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/attrvalue/DefaultPlainAttrValidationManagerTest.java @@ -0,0 +1,131 @@ +/* + * 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.core.persistence.common.attrvalue; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.syncope.core.persistence.api.attrvalue.InvalidPlainAttrValueException; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValueValidator; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.spring.implementation.ImplementationManager; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class DefaultPlainAttrValidationManagerTest { + + @Mock + private PlainSchema schema; + + @Mock + private PlainAttrValue attrValue; + + private final DefaultPlainAttrValidationManager manager = new DefaultPlainAttrValidationManager(); + + @Test + void validateWithoutValidatorUsesBasicValidator() { + when(schema.getValidator()).thenReturn((Implementation) null); + + assertDoesNotThrow(() -> manager.validate(schema, "value", attrValue)); + } + + @Test + void validateWithCustomValidator() { + Implementation impl = mock(Implementation.class); + when(schema.getValidator()).thenReturn(impl); + + PlainAttrValueValidator customValidator = mock(PlainAttrValueValidator.class); + + try (MockedStatic im = mockStatic(ImplementationManager.class); + MockedStatic ac = mockStatic(AuthContextUtils.class)) { + ac.when(AuthContextUtils::getDomain).thenReturn("Master"); + im.when(() -> ImplementationManager.build( + anyString(), any(Implementation.class), + any(), any())) + .thenReturn(customValidator); + + manager.validate(schema, "value", attrValue); + + verify(customValidator).validate(schema, "value", attrValue); + } + } + + @Test + void validateWithCustomValidatorBuildFailsFallsBackToBasic() { + Implementation impl = mock(Implementation.class); + when(schema.getValidator()).thenReturn(impl); + + try (MockedStatic im = mockStatic(ImplementationManager.class); + MockedStatic ac = mockStatic(AuthContextUtils.class)) { + ac.when(AuthContextUtils::getDomain).thenReturn("Master"); + im.when(() -> ImplementationManager.build( + anyString(), any(Implementation.class), + any(), any())) + .thenThrow(new RuntimeException("build failed")); + + // BASIC_VALIDATOR is a no-op, so no exception is expected + assertDoesNotThrow(() -> manager.validate(schema, "value", attrValue)); + } + } + + @Test + void validatePropagatesInvalidPlainAttrValueException() { + when(schema.getValidator()).thenReturn((Implementation) null); + + // Use a real schema with a Binary type and an invalid value to trigger parsing/validation + // But here we just verify the manager delegates to the validator's validate method. + // The BasicValidator is a no-op, so we use a custom validator that throws. + Implementation impl = mock(Implementation.class); + when(schema.getValidator()).thenReturn(impl); + + PlainAttrValueValidator throwingValidator = mock(PlainAttrValueValidator.class); + doThrow(new InvalidPlainAttrValueException("custom error")). + when(throwingValidator).validate(schema, "value", attrValue); + + try (MockedStatic im = mockStatic(ImplementationManager.class); + MockedStatic ac = mockStatic(AuthContextUtils.class)) { + ac.when(AuthContextUtils::getDomain).thenReturn("Master"); + im.when(() -> ImplementationManager.build( + anyString(), any(Implementation.class), + any(), any())) + .thenReturn(throwingValidator); + + InvalidPlainAttrValueException ex = assertThrows( + InvalidPlainAttrValueException.class, + () -> manager.validate(schema, "value", attrValue)); + + assertEquals("custom error", ex.getMessage()); + } + } +} diff --git a/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/attrvalue/EmailAddressValidatorTest.java b/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/attrvalue/EmailAddressValidatorTest.java new file mode 100644 index 00000000000..dfd670d7b30 --- /dev/null +++ b/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/attrvalue/EmailAddressValidatorTest.java @@ -0,0 +1,72 @@ +/* + * 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.core.persistence.common.attrvalue; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.apache.syncope.core.persistence.api.attrvalue.InvalidPlainAttrValueException; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class EmailAddressValidatorTest { + + @Mock + private PlainSchema schema; + + private final EmailAddressValidator validator = new EmailAddressValidator(); + + @Test + void doValidateValidEmail() { + PlainAttrValue attrValue = new PlainAttrValue(); + attrValue.setStringValue("user@example.com"); + + assertDoesNotThrow(() -> validator.doValidate(schema, attrValue)); + } + + @Test + void doValidateInvalidEmail() { + PlainAttrValue attrValue = new PlainAttrValue(); + attrValue.setStringValue("not-an-email"); + + InvalidPlainAttrValueException ex = assertThrows( + InvalidPlainAttrValueException.class, + () -> validator.doValidate(schema, attrValue)); + + assertEquals("\"not-an-email\" is not a valid email address", ex.getMessage()); + } + + @Test + void doValidateEmptyEmail() { + PlainAttrValue attrValue = new PlainAttrValue(); + attrValue.setStringValue(""); + + InvalidPlainAttrValueException ex = assertThrows( + InvalidPlainAttrValueException.class, + () -> validator.doValidate(schema, attrValue)); + + assertEquals("\"\" is not a valid email address", ex.getMessage()); + } +} diff --git a/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/attrvalue/URLValidatorTest.java b/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/attrvalue/URLValidatorTest.java new file mode 100644 index 00000000000..efce5cee903 --- /dev/null +++ b/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/attrvalue/URLValidatorTest.java @@ -0,0 +1,72 @@ +/* + * 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.core.persistence.common.attrvalue; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.apache.syncope.core.persistence.api.attrvalue.InvalidPlainAttrValueException; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class URLValidatorTest { + + @Mock + private PlainSchema schema; + + private final URLValidator validator = new URLValidator(); + + @Test + void doValidateValidUrl() { + PlainAttrValue attrValue = new PlainAttrValue(); + attrValue.setStringValue("https://example.com"); + + assertDoesNotThrow(() -> validator.doValidate(schema, attrValue)); + } + + @Test + void doValidateInvalidUrl() { + PlainAttrValue attrValue = new PlainAttrValue(); + attrValue.setStringValue("not a url"); + + InvalidPlainAttrValueException ex = assertThrows( + InvalidPlainAttrValueException.class, + () -> validator.doValidate(schema, attrValue)); + + assertEquals("\"not a url\" is not a valid URL", ex.getMessage()); + } + + @Test + void doValidateEmptyUrl() { + PlainAttrValue attrValue = new PlainAttrValue(); + attrValue.setStringValue(""); + + InvalidPlainAttrValueException ex = assertThrows( + InvalidPlainAttrValueException.class, + () -> validator.doValidate(schema, attrValue)); + + assertEquals("\"\" is not a valid URL", ex.getMessage()); + } +} diff --git a/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/entity/DefaultAnyUtilsTest.java b/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/entity/DefaultAnyUtilsTest.java new file mode 100644 index 00000000000..db81e2f5743 --- /dev/null +++ b/core/persistence-common/src/test/java/org/apache/syncope/core/persistence/common/entity/DefaultAnyUtilsTest.java @@ -0,0 +1,247 @@ +/* + * 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.core.persistence.common.entity; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Field; +import java.util.Optional; +import org.apache.syncope.common.lib.request.AnyCR; +import org.apache.syncope.common.lib.request.AnyObjectCR; +import org.apache.syncope.common.lib.request.AnyUR; +import org.apache.syncope.common.lib.request.GroupCR; +import org.apache.syncope.common.lib.request.UserCR; +import org.apache.syncope.common.lib.to.AnyObjectTO; +import org.apache.syncope.common.lib.to.AnyTO; +import org.apache.syncope.common.lib.to.GroupTO; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.dao.AnyDAO; +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@SuppressWarnings({ "unchecked", "rawtypes" }) +@ExtendWith(MockitoExtension.class) +class DefaultAnyUtilsTest { + + @Mock + private UserDAO userDAO; + + @Mock + private GroupDAO groupDAO; + + @Mock + private AnyObjectDAO anyObjectDAO; + + @Mock + private PlainSchemaDAO plainSchemaDAO; + + @Mock + private EntityFactory entityFactory; + + @Mock + private AnyTypeKind anyTypeKind; + + private DefaultAnyUtils utilsFor(final AnyTypeKind kind) { + when(entityFactory.userClass()).thenReturn((Class) User.class); + when(entityFactory.groupClass()).thenReturn((Class) Group.class); + when(entityFactory.anyObjectClass()). + thenReturn((Class) org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject.class); + + return new DefaultAnyUtils( + userDAO, groupDAO, anyObjectDAO, plainSchemaDAO, entityFactory, kind); + } + + @Test + void anyTypeKindReturnsConfiguredKind() { + AnyTypeKind kind = AnyTypeKind.USER; + DefaultAnyUtils utils = utilsFor(kind); + + assertSame(kind, utils.anyTypeKind()); + } + + @Test + void anyClassForUser() { + DefaultAnyUtils utils = utilsFor(AnyTypeKind.USER); + + assertSame(User.class, utils.anyClass()); + } + + @Test + void anyClassForGroup() { + DefaultAnyUtils utils = utilsFor(AnyTypeKind.GROUP); + + assertSame(Group.class, utils.anyClass()); + } + + @Test + void anyClassForAnyObject() { + DefaultAnyUtils utils = utilsFor(AnyTypeKind.ANY_OBJECT); + + assertSame( + org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject.class, + utils.anyClass()); + } + + @Test + void getFieldReturnsEmptyForInterface() { + DefaultAnyUtils utils = utilsFor(AnyTypeKind.USER); + + // User is an interface; all its fields are static, so initFieldNames finds nothing. + Optional field = utils.getField("anyField"); + + assertFalse(field.isPresent()); + } + + @Test + void getFieldNonExisting() { + DefaultAnyUtils utils = utilsFor(AnyTypeKind.USER); + + Optional field = utils.getField("nonExistingField"); + + assertFalse(field.isPresent()); + } + + @Test + void newAnyTOForUser() { + DefaultAnyUtils utils = utilsFor(AnyTypeKind.USER); + + AnyTO to = utils.newAnyTO(); + + assertNotNull(to); + assertEquals(UserTO.class, to.getClass()); + } + + @Test + void newAnyTOForGroup() { + DefaultAnyUtils utils = utilsFor(AnyTypeKind.GROUP); + + AnyTO to = utils.newAnyTO(); + + assertNotNull(to); + assertEquals(GroupTO.class, to.getClass()); + } + + @Test + void newAnyTOForAnyObject() { + DefaultAnyUtils utils = utilsFor(AnyTypeKind.ANY_OBJECT); + + AnyTO to = utils.newAnyTO(); + + assertNotNull(to); + assertEquals(AnyObjectTO.class, to.getClass()); + } + + @Test + void newAnyCRForUser() { + DefaultAnyUtils utils = utilsFor(AnyTypeKind.USER); + + AnyCR cr = utils.newAnyCR(); + + assertNotNull(cr); + assertEquals(UserCR.class, cr.getClass()); + } + + @Test + void newAnyCRForGroup() { + DefaultAnyUtils utils = utilsFor(AnyTypeKind.GROUP); + + AnyCR cr = utils.newAnyCR(); + + assertNotNull(cr); + assertEquals(GroupCR.class, cr.getClass()); + } + + @Test + void newAnyCRForAnyObject() { + DefaultAnyUtils utils = utilsFor(AnyTypeKind.ANY_OBJECT); + + AnyCR cr = utils.newAnyCR(); + + assertNotNull(cr); + assertEquals(AnyObjectCR.class, cr.getClass()); + } + + @Test + void newAnyURSetsKey() { + DefaultAnyUtils utils = utilsFor(AnyTypeKind.USER); + + AnyUR ur = utils.newAnyUR("test-key"); + + assertNotNull(ur); + assertEquals("test-key", ur.getKey()); + } + + @Test + void newAnyURNullKey() { + DefaultAnyUtils utils = utilsFor(AnyTypeKind.USER); + + AnyUR ur = utils.newAnyUR(null); + + assertNotNull(ur); + assertNull(ur.getKey()); + } + + @Test + void daoForUser() { + DefaultAnyUtils utils = utilsFor(AnyTypeKind.USER); + + AnyDAO dao = utils.dao(); + + assertSame(userDAO, dao); + } + + @Test + void daoForGroup() { + DefaultAnyUtils utils = utilsFor(AnyTypeKind.GROUP); + + AnyDAO dao = utils.dao(); + + assertSame(groupDAO, dao); + } + + @Test + void daoForAnyObject() { + DefaultAnyUtils utils = utilsFor(AnyTypeKind.ANY_OBJECT); + + AnyDAO dao = utils.dao(); + + assertSame(anyObjectDAO, dao); + } + + @Test + void constructorDoesNotThrow() { + assertDoesNotThrow(() -> utilsFor(AnyTypeKind.USER)); + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtImpl.java index 329cfa264ad..8addba85d5d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtImpl.java @@ -92,7 +92,8 @@ public GroupRepoExtImpl( @Transactional(readOnly = true) @Override public Collection findAllResourceKeys(final String key) { - return findById(key).map(Any::getResources). + return Optional.ofNullable(authFind(key)). + map(Any::getResources). orElseGet(List::of). stream().map(ExternalResource::getKey).toList(); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtImpl.java index 7f2d354309f..2f98374c110 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtImpl.java @@ -61,6 +61,13 @@ public class UserRepoExtImpl extends AbstractAnyRepoExt implements UserRepoExt { + // The token column is a CLOB: a plain equality comparison is not supported by Oracle + // (ORA-22848), hence LIKE is used with the LIKE metacharacters escaped so that the token + // is still matched exactly; '#' is used as escape char as it works across all supported DBMS. + protected static String escapeForLike(final String value) { + return value == null ? null : value.replace("#", "##").replace("%", "#%").replace("_", "#_"); + } + protected final RoleDAO roleDAO; protected final AccessTokenDAO accessTokenDAO; @@ -96,13 +103,6 @@ public UserRepoExtImpl( this.securityProperties = securityProperties; } - // The token column is a CLOB: a plain equality comparison is not supported by Oracle - // (ORA-22848), hence LIKE is used with the LIKE metacharacters escaped so that the token - // is still matched exactly; '#' is used as escape char as it works across all supported DBMS. - protected static String escapeForLike(final String value) { - return value == null ? null : value.replace("#", "##").replace("%", "#%").replace("_", "#_"); - } - @Transactional(readOnly = true) @Override public Optional findByToken(final String token) { diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AccessTokenTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AccessTokenTest.java index e36b78905c7..857d9994361 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AccessTokenTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AccessTokenTest.java @@ -36,7 +36,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AccessTokenTest extends AbstractTest { +class AccessTokenTest extends AbstractTest { @Autowired private AccessTokenDAO accessTokenDAO; @@ -52,7 +52,7 @@ private void create(final String key, final long minusSeconds) { } @Test - public void findAll() { + void findAll() { for (long i = 0; i < 5; i++) { create(String.valueOf(6 - i), i); } @@ -89,7 +89,7 @@ public void findAll() { } @Test - public void crud() { + void crud() { AccessToken accessToken = entityFactory.newEntity(AccessToken.class); accessToken.setKey(UUID.randomUUID().toString()); accessToken.setBody("pointless body"); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyMatchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyMatchTest.java index 441cbeddfa7..6f8d585e3d8 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyMatchTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyMatchTest.java @@ -42,7 +42,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AnyMatchTest extends AbstractTest { +class AnyMatchTest extends AbstractTest { @Autowired private UserDAO userDAO; @@ -57,7 +57,7 @@ public class AnyMatchTest extends AbstractTest { private AnyMatchDAO anyMatcher; @Test - public void byResourceCond() { + void byResourceCond() { User user = userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").orElseThrow(); ResourceCond resourceCond = new ResourceCond(); @@ -69,7 +69,7 @@ public void byResourceCond() { } @Test - public void anyObjectMatch() { + void anyObjectMatch() { AnyObject anyObject = anyObjectDAO.findById("fc6dbc3a-6c07-4965-8781-921e7401a4a5").orElseThrow(); RelationshipCond relationshipCond = new RelationshipCond(); @@ -82,7 +82,7 @@ public void anyObjectMatch() { } @Test - public void userMatch() { + void userMatch() { User user = userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").orElseThrow(); MembershipCond groupCond = new MembershipCond(); @@ -108,7 +108,7 @@ public void userMatch() { } @Test - public void groupMatch() { + void groupMatch() { Group group = groupDAO.findById("37d15e4c-cdc1-460b-a591-8505c8133806").orElseThrow(); AnyCond anyCond = new AnyCond(); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyObjectTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyObjectTest.java index 99b1106a099..607e01c6f30 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyObjectTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyObjectTest.java @@ -37,7 +37,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AnyObjectTest extends AbstractTest { +class AnyObjectTest extends AbstractTest { @Autowired private AnyTypeDAO anyTypeDAO; @@ -49,20 +49,20 @@ public class AnyObjectTest extends AbstractTest { private RealmSearchDAO realmSearchDAO; @Test - public void findAll() { + void findAll() { List anyObjects = anyObjectDAO.findAll(); assertEquals(3, anyObjects.size()); } @Test - public void find() { + void find() { AnyObject anyObject = anyObjectDAO.findById("8559d14d-58c2-46eb-a2d4-a7d35161e8f8").orElseThrow(); assertNotNull(anyObject.getType()); assertFalse(anyObject.getType().getClasses().isEmpty()); } @Test - public void findByName() { + void findByName() { AnyObject anyObject = anyObjectDAO.findByName("PRINTER", "HP LJ 1300n").orElseThrow(); assertNotNull(anyObject); assertEquals("fc6dbc3a-6c07-4965-8781-921e7401a4a5", anyObject.getKey()); @@ -71,7 +71,7 @@ public void findByName() { } @Test - public void findKey() { + void findKey() { assertEquals( "fc6dbc3a-6c07-4965-8781-921e7401a4a5", anyObjectDAO.findKey("PRINTER", "HP LJ 1300n").orElseThrow()); @@ -80,7 +80,7 @@ public void findKey() { } @Test - public void findByKeys() { + void findByKeys() { List found = anyObjectDAO.findByKeys(List.of( "8559d14d-58c2-46eb-a2d4-a7d35161e8f8", "9e1d130c-d6a3-48b1-98b3-182477ed0688", "none")); assertEquals(2, found.size()); @@ -89,7 +89,7 @@ public void findByKeys() { } @Test - public void countByType() { + void countByType() { Map byType = anyObjectDAO.countByType(); assertEquals(1, byType.size()); Long count = byType.get("PRINTER"); @@ -97,7 +97,7 @@ public void countByType() { } @Test - public void countByRealm() { + void countByRealm() { Map byRealm = anyObjectDAO.countByRealm("PRINTER"); assertEquals(2, byRealm.size()); Long count = byRealm.get(SyncopeConstants.ROOT_REALM); @@ -105,7 +105,7 @@ public void countByRealm() { } @Test - public void save() { + void save() { AnyObject anyObject = entityFactory.newEntity(AnyObject.class); anyObject.setName("a name"); anyObject.setType(anyTypeDAO.findById("PRINTER").orElseThrow()); @@ -116,7 +116,7 @@ public void save() { } @Test - public void delete() { + void delete() { AnyObject anyObject = anyObjectDAO.findById("8559d14d-58c2-46eb-a2d4-a7d35161e8f8").orElseThrow(); anyObjectDAO.deleteById(anyObject.getKey()); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java index 4d859f8c868..ac7152c8918 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java @@ -81,7 +81,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AnySearchTest extends AbstractTest { +class AnySearchTest extends AbstractTest { private static final String LOGIN_DATE_VALUE = "2009-05-26"; @@ -119,7 +119,7 @@ public class AnySearchTest extends AbstractTest { private EntityCacheDAO entityCacheDAO; @BeforeEach - public void adjustLoginDateForLocalSystem() { + void adjustLoginDateForLocalSystem() { entityCacheDAO.evict(JPAUser.class, "1417acbe-cbf6-4277-9372-e75e04f97000"); User rossini = userDAO.findByUsername("rossini").orElseThrow(); @@ -131,7 +131,7 @@ public void adjustLoginDateForLocalSystem() { } @Test - public void searchTwoPlainSchemas() { + void searchTwoPlainSchemas() { AttrCond firstnameCond = new AttrCond(AttrCond.Type.EQ); firstnameCond.setSchema("firstname"); firstnameCond.setExpression("Gioacchino"); @@ -186,7 +186,7 @@ public void searchTwoPlainSchemas() { } @Test - public void searchWithLikeCondition() { + void searchWithLikeCondition() { AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.LIKE); fullnameLeafCond.setSchema("fullname"); fullnameLeafCond.setExpression("%o%"); @@ -213,7 +213,7 @@ public void searchWithLikeCondition() { } @Test - public void searchCaseInsensitiveWithLikeCondition() { + void searchCaseInsensitiveWithLikeCondition() { AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.ILIKE); fullnameLeafCond.setSchema("fullname"); fullnameLeafCond.setExpression("%O%"); @@ -240,7 +240,7 @@ public void searchCaseInsensitiveWithLikeCondition() { } @Test - public void searchWithNotAttrCond() { + void searchWithNotAttrCond() { AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.EQ); fullnameLeafCond.setSchema("fullname"); fullnameLeafCond.setExpression("Giuseppe Verdi"); @@ -258,7 +258,7 @@ public void searchWithNotAttrCond() { } @Test - public void searchWithNotAnyCond() { + void searchWithNotAnyCond() { AnyCond usernameLeafCond = new AnyCond(AttrCond.Type.EQ); usernameLeafCond.setSchema("username"); usernameLeafCond.setExpression("verdi"); @@ -274,7 +274,7 @@ public void searchWithNotAnyCond() { } @Test - public void searchCaseInsensitiveWithNotCondition() { + void searchCaseInsensitiveWithNotCondition() { AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.IEQ); fullnameLeafCond.setSchema("fullname"); fullnameLeafCond.setExpression("giuseppe verdi"); @@ -292,7 +292,7 @@ public void searchCaseInsensitiveWithNotCondition() { } @Test - public void searchByBoolean() { + void searchByBoolean() { AttrCond coolLeafCond = new AttrCond(AttrCond.Type.EQ); coolLeafCond.setSchema("cool"); coolLeafCond.setExpression("true"); @@ -308,7 +308,7 @@ public void searchByBoolean() { } @Test - public void searchByRealm() { + void searchByRealm() { AnyCond anyCond = new AnyCond(AttrCond.Type.EQ); anyCond.setSchema("realm"); anyCond.setExpression("c5b75db1-fce7-470f-b780-3b9934d82a9d"); @@ -326,7 +326,7 @@ public void searchByRealm() { } @Test - public void searchByUManager() { + void searchByUManager() { AnyCond anyCond = new AnyCond(AttrCond.Type.EQ); anyCond.setSchema("uManager"); anyCond.setExpression("823074dc-d280-436d-a7dd-07399fae48ec"); @@ -344,7 +344,7 @@ public void searchByUManager() { } @Test - public void searchByPageAndSize() { + void searchByPageAndSize() { AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.LIKE); fullnameLeafCond.setSchema("fullname"); fullnameLeafCond.setExpression("%o%"); @@ -383,7 +383,7 @@ public void searchByPageAndSize() { } @Test - public void searchByGroup() { + void searchByGroup() { MembershipCond groupCond = new MembershipCond(); groupCond.setGroup("child"); @@ -418,7 +418,7 @@ public void searchByGroup() { } @Test - public void searchByRole() { + void searchByRole() { RoleCond roleCond = new RoleCond(); roleCond.setRole("Other"); @@ -428,7 +428,7 @@ public void searchByRole() { } @Test - public void searchByIsNull() { + void searchByIsNull() { AttrCond coolLeafCond = new AttrCond(AttrCond.Type.ISNULL); coolLeafCond.setSchema("cool"); @@ -445,7 +445,7 @@ public void searchByIsNull() { } @Test - public void searchByAuxClass() { + void searchByAuxClass() { AuxClassCond ac = new AuxClassCond(); ac.setAuxClass("csv"); @@ -455,7 +455,7 @@ public void searchByAuxClass() { } @Test - public void searchByResource() { + void searchByResource() { ResourceCond ws2 = new ResourceCond(); ws2.setResource("ws-target-resource-2"); @@ -471,7 +471,7 @@ public void searchByResource() { } @Test - public void searchByBooleanAttrCond() { + void searchByBooleanAttrCond() { AttrCond booleanCond = new AttrCond(AnyCond.Type.EQ); booleanCond.setSchema("show"); booleanCond.setExpression("true"); @@ -482,7 +482,7 @@ public void searchByBooleanAttrCond() { } @Test - public void searchByUsernameAndKey() { + void searchByUsernameAndKey() { AnyCond usernameLeafCond = new AnyCond(AnyCond.Type.LIKE); usernameLeafCond.setSchema("username"); usernameLeafCond.setExpression("%ini"); @@ -503,7 +503,7 @@ public void searchByUsernameAndKey() { } @Test - public void searchByGroupNameAndKey() { + void searchByGroupNameAndKey() { AnyCond groupNameLeafCond = new AnyCond(AnyCond.Type.EQ); groupNameLeafCond.setSchema("name"); groupNameLeafCond.setExpression("root"); @@ -526,7 +526,7 @@ public void searchByGroupNameAndKey() { } @Test - public void searchByUsernameAndFullname() { + void searchByUsernameAndFullname() { AnyCond usernameLeafCond = new AnyCond(AnyCond.Type.EQ); usernameLeafCond.setSchema("username"); usernameLeafCond.setExpression("rossini"); @@ -545,7 +545,7 @@ public void searchByUsernameAndFullname() { } @Test - public void searchByUsernameAndFullnameIgnoreCase() { + void searchByUsernameAndFullnameIgnoreCase() { AnyCond usernameLeafCond = new AnyCond(AnyCond.Type.IEQ); usernameLeafCond.setSchema("username"); usernameLeafCond.setExpression("RoSsini"); @@ -564,7 +564,7 @@ public void searchByUsernameAndFullnameIgnoreCase() { } @Test - public void searchByKey() { + void searchByKey() { AnyCond idLeafCond = new AnyCond(AnyCond.Type.EQ); idLeafCond.setSchema("key"); idLeafCond.setExpression("74cd8ece-715a-44a4-a736-e17b46c4e7e6"); @@ -579,7 +579,7 @@ public void searchByKey() { } @Test - public void searchByType() { + void searchByType() { AnyTypeCond tcond = new AnyTypeCond(); tcond.setAnyTypeKey("PRINTER"); @@ -597,7 +597,7 @@ public void searchByType() { } @Test - public void searchByRelationshipType() { + void searchByRelationshipType() { // 1. first search for printers involved in "inclusion" relationship RelationshipTypeCond relationshipTypeCond = new RelationshipTypeCond(); relationshipTypeCond.setRelationshipType("inclusion"); @@ -624,7 +624,7 @@ public void searchByRelationshipType() { } @Test - public void searchByAnyCondDate() { + void searchByAnyCondDate() { AnyCond creationDateCond = new AnyCond(AnyCond.Type.EQ); creationDateCond.setSchema("creationDate"); creationDateCond.setExpression("2021-04-15 12:45:00"); @@ -639,7 +639,7 @@ public void searchByAnyCondDate() { } @Test - public void searchByAttrCondDate() { + void searchByAttrCondDate() { AttrCond loginDateCond = new AttrCond(AnyCond.Type.LT); loginDateCond.setSchema("loginDate"); loginDateCond.setExpression("2009-05-27"); @@ -654,7 +654,7 @@ public void searchByAttrCondDate() { } @Test - public void userOrderBy() { + void userOrderBy() { AnyCond usernameLeafCond = new AnyCond(AnyCond.Type.EQ); usernameLeafCond.setSchema("username"); usernameLeafCond.setExpression("rossini"); @@ -677,7 +677,7 @@ public void userOrderBy() { } @Test - public void groupOrderBy() { + void groupOrderBy() { AnyCond idLeafCond = new AnyCond(AnyCond.Type.LIKE); idLeafCond.setSchema("name"); idLeafCond.setExpression("%r"); @@ -696,7 +696,7 @@ public void groupOrderBy() { } @Test - public void member() { + void member() { MemberCond memberCond = new MemberCond(); memberCond.setMember("1417acbe-cbf6-4277-9372-e75e04f97000"); SearchCond searchCondition = SearchCond.of(memberCond); @@ -709,7 +709,7 @@ public void member() { } @Test - public void asGroupManager() { + void asGroupManager() { // prepare authentication Map> entForRealms = Map.of( IdRepoEntitlement.GROUP_SEARCH, @@ -757,7 +757,7 @@ public void asGroupManager() { } @Test - public void changePwdDate() { + void changePwdDate() { AnyCond statusCond = new AnyCond(AttrCond.Type.IEQ); statusCond.setSchema("status"); statusCond.setExpression("suspended"); @@ -774,7 +774,7 @@ public void changePwdDate() { } @Test - public void findByDerAttrValue() { + void findByDerAttrValue() { List list = searchDAO.findByDerAttrValue( derSchemaDAO.findById("cn").orElseThrow().getExpression(), "Vivaldi, Antonio", false, AnyTypeKind.USER); assertEquals(1, list.size()); @@ -789,21 +789,21 @@ public void findByDerAttrValue() { } @Test - public void findByInvalidDerAttrValue() { + void findByInvalidDerAttrValue() { assertTrue(searchDAO.findByDerAttrValue( derSchemaDAO.findById("cn").orElseThrow().getExpression(), "Antonio, Maria, Rossi", false, AnyTypeKind.USER).isEmpty()); } @Test - public void findByInvalidDerAttrExpression() { + void findByInvalidDerAttrExpression() { assertThrows(IllegalArgumentException.class, () -> searchDAO.findByDerAttrValue( derSchemaDAO.findById("noschema").orElseThrow().getExpression(), "Antonio, Maria", false, AnyTypeKind.USER).isEmpty()); } @Test - public void issue202() { + void issue202() { ResourceCond ws2 = new ResourceCond(); ws2.setResource("ws-target-resource-2"); @@ -821,7 +821,7 @@ public void issue202() { } @Test - public void issue242() { + void issue242() { AnyCond cond = new AnyCond(AttrCond.Type.LIKE); cond.setSchema("key"); cond.setExpression("test%"); @@ -835,7 +835,7 @@ public void issue242() { } @Test - public void issueSYNCOPE46() { + void issueSYNCOPE46() { AnyCond cond = new AnyCond(AttrCond.Type.LIKE); cond.setSchema("username"); cond.setExpression("%ossin%"); @@ -849,7 +849,7 @@ public void issueSYNCOPE46() { } @Test - public void issueSYNCOPE433() { + void issueSYNCOPE433() { AttrCond isNullCond = new AttrCond(AttrCond.Type.ISNULL); isNullCond.setSchema("loginDate"); @@ -865,7 +865,7 @@ public void issueSYNCOPE433() { } @Test - public void issueSYNCOPE929() { + void issueSYNCOPE929() { AttrCond rossiniCond = new AttrCond(AttrCond.Type.EQ); rossiniCond.setSchema("surname"); rossiniCond.setExpression("Rossini"); @@ -888,7 +888,7 @@ public void issueSYNCOPE929() { } @Test - public void issueSYNCOPE980() { + void issueSYNCOPE980() { AnyType service = entityFactory.newEntity(AnyType.class); service.setKey("SERVICE"); service.setKind(AnyTypeKind.ANY_OBJECT); @@ -936,7 +936,7 @@ public void issueSYNCOPE980() { } @Test - public void issueSYNCOPE983() { + void issueSYNCOPE983() { AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.LIKE); fullnameLeafCond.setSchema("surname"); fullnameLeafCond.setExpression("%o%"); @@ -956,7 +956,7 @@ public void issueSYNCOPE983() { } @Test - public void issueSYNCOPE1416() { + void issueSYNCOPE1416() { AttrCond idLeftCond = new AttrCond(AttrCond.Type.ISNOTNULL); idLeftCond.setSchema("surname"); @@ -993,7 +993,7 @@ public void issueSYNCOPE1416() { } @Test - public void issueSYNCOPE1419() { + void issueSYNCOPE1419() { AttrCond loginDateCond = new AttrCond(AttrCond.Type.EQ); loginDateCond.setSchema("loginDate"); loginDateCond.setExpression(LOGIN_DATE_VALUE); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyTypeClassTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyTypeClassTest.java index 9c671ff804e..86bf7480d13 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyTypeClassTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyTypeClassTest.java @@ -32,7 +32,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AnyTypeClassTest extends AbstractTest { +class AnyTypeClassTest extends AbstractTest { @Autowired private PlainSchemaDAO plainSchemaDAO; @@ -41,7 +41,7 @@ public class AnyTypeClassTest extends AbstractTest { private AnyTypeClassDAO anyTypeClassDAO; @Test - public void find() { + void find() { AnyTypeClass minimalGroup = anyTypeClassDAO.findById("minimal group").orElseThrow(); assertFalse(minimalGroup.getPlainSchemas().isEmpty()); @@ -49,13 +49,13 @@ public void find() { } @Test - public void findAll() { + void findAll() { List list = anyTypeClassDAO.findAll(); assertFalse(list.isEmpty()); } @Test - public void save() { + void save() { AnyTypeClass newClass = entityFactory.newEntity(AnyTypeClass.class); newClass.setKey("new class"); newClass.add(plainSchemaDAO.findById("firstname").orElseThrow()); @@ -67,7 +67,7 @@ public void save() { } @Test - public void delete() { + void delete() { AnyTypeClass minimalUser = anyTypeClassDAO.findById("minimal user").orElseThrow(); assertNotNull(minimalUser); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyTypeTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyTypeTest.java index 68fe4c32c87..4db3547a130 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyTypeTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyTypeTest.java @@ -36,7 +36,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AnyTypeTest extends AbstractTest { +class AnyTypeTest extends AbstractTest { @Autowired private AnyTypeDAO anyTypeDAO; @@ -45,7 +45,7 @@ public class AnyTypeTest extends AbstractTest { private AnyTypeClassDAO anyTypeClassDAO; @Test - public void find() { + void find() { AnyType userType = anyTypeDAO.getUser(); assertNotNull(userType); assertEquals(AnyTypeKind.USER, userType.getKind()); @@ -64,13 +64,13 @@ public void find() { } @Test - public void findAll() { + void findAll() { List list = anyTypeDAO.findAll(); assertFalse(list.isEmpty()); } @Test - public void save() { + void save() { AnyType newType = entityFactory.newEntity(AnyType.class); newType.setKey("new type"); newType.setKind(AnyTypeKind.ANY_OBJECT); @@ -83,7 +83,7 @@ public void save() { } @Test - public void saveInvalidKind() { + void saveInvalidKind() { assertThrows(InvalidEntityException.class, () -> { AnyType newType = entityFactory.newEntity(AnyType.class); newType.setKey("new type"); @@ -94,7 +94,7 @@ public void saveInvalidKind() { } @Test - public void saveInvalidName() { + void saveInvalidName() { assertThrows(InvalidEntityException.class, () -> { AnyType newType = entityFactory.newEntity(AnyType.class); newType.setKey("group"); @@ -105,7 +105,7 @@ public void saveInvalidName() { } @Test - public void delete() { + void delete() { AnyType otherType = anyTypeDAO.findById("PRINTER").orElseThrow(); assertNotNull(otherType); @@ -114,7 +114,7 @@ public void delete() { } @Test - public void deleteInvalid() { + void deleteInvalid() { assertThrows(IllegalArgumentException.class, () -> anyTypeDAO.deleteById(AnyTypeKind.USER.name())); } } diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AttrRepoTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AttrRepoTest.java index 503852e5390..299346f1ff3 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AttrRepoTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AttrRepoTest.java @@ -40,13 +40,13 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AttrRepoTest extends AbstractTest { +class AttrRepoTest extends AbstractTest { @Autowired private AttrRepoDAO attrRepoDAO; @Test - public void findAll() { + void findAll() { List modules = attrRepoDAO.findAll(); assertNotNull(modules); assertFalse(modules.isEmpty()); @@ -54,7 +54,7 @@ public void findAll() { } @Test - public void find() { + void find() { AttrRepo attrRepo = attrRepoDAO.findById("DefaultLDAPAttrRepo").orElseThrow(); assertTrue(attrRepo.getConf() instanceof LDAPAttrRepoConf); @@ -70,7 +70,7 @@ public void find() { } @Test - public void findByType() { + void findByType() { List attrRepos = attrRepoDAO.findAll(); assertTrue(attrRepos.stream().anyMatch( attrRepo -> attrRepo.getConf() instanceof LDAPAttrRepoConf @@ -113,7 +113,7 @@ private void saveAttrRepo(final String key, final AttrRepoConf conf) { } @Test - public void saveWithStubRepo() { + void saveWithStubRepo() { StubAttrRepoConf conf = new StubAttrRepoConf(); conf.getAttributes().put("attr1", UUID.randomUUID().toString()); conf.getAttributes().put("attr2", UUID.randomUUID().toString()); @@ -122,7 +122,7 @@ public void saveWithStubRepo() { } @Test - public void saveWithLdapRepo() { + void saveWithLdapRepo() { LDAPAttrRepoConf conf = new LDAPAttrRepoConf(); conf.setBaseDn("dc=example,dc=org"); conf.setSearchFilter("cn={user}"); @@ -134,7 +134,7 @@ public void saveWithLdapRepo() { } @Test - public void saveWithJDBCRepo() { + void saveWithJDBCRepo() { JDBCAttrRepoConf conf = new JDBCAttrRepoConf(); conf.setSql("SELECT * FROM table WHERE name=?"); conf.setUrl("jdbc:h2:mem:syncopedb;DB_CLOSE_DELAY=-1"); @@ -145,7 +145,7 @@ public void saveWithJDBCRepo() { } @Test - public void saveWithSyncopeRepo() { + void saveWithSyncopeRepo() { SyncopeAttrRepoConf conf = new SyncopeAttrRepoConf(); conf.setDomain("Master"); @@ -153,7 +153,7 @@ public void saveWithSyncopeRepo() { } @Test - public void updateWithLDAPRepo() { + void updateWithLDAPRepo() { AttrRepo module = attrRepoDAO.findById("DefaultLDAPAttrRepo").orElseThrow(); AttrRepoConf conf = module.getConf(); LDAPAttrRepoConf.class.cast(conf).setBaseDn("dc=example2,dc=org"); @@ -170,7 +170,7 @@ public void updateWithLDAPRepo() { } @Test - public void updateWithJDBCRepo() { + void updateWithJDBCRepo() { AttrRepo module = attrRepoDAO.findById("DefaultJDBCAttrRepo").orElseThrow(); AttrRepoConf conf = module.getConf(); JDBCAttrRepoConf.class.cast(conf).setSql("SELECT * FROM otherTable WHERE name=?"); @@ -184,7 +184,7 @@ public void updateWithJDBCRepo() { } @Test - public void updateWithStubRepo() { + void updateWithStubRepo() { AttrRepo module = attrRepoDAO.findById("DefaultStubAttrRepo").orElseThrow(); assertEquals(1, StubAttrRepoConf.class.cast(module.getConf()).getAttributes().size()); AttrRepoConf conf = module.getConf(); @@ -198,7 +198,7 @@ public void updateWithStubRepo() { } @Test - public void updateWithSyncopeRepo() { + void updateWithSyncopeRepo() { AttrRepo module = attrRepoDAO.findById("DefaultSyncopeAttrRepo").orElseThrow(); AttrRepoConf conf = module.getConf(); @@ -213,7 +213,7 @@ public void updateWithSyncopeRepo() { } @Test - public void delete() { + void delete() { assertTrue(attrRepoDAO.findById("DefaultSyncopeAttrRepo").isPresent()); attrRepoDAO.deleteById("DefaultSyncopeAttrRepo"); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthModuleTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthModuleTest.java index ad869a8a9eb..f240c45bd30 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthModuleTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthModuleTest.java @@ -46,13 +46,13 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AuthModuleTest extends AbstractTest { +class AuthModuleTest extends AbstractTest { @Autowired private AuthModuleDAO authModuleDAO; @Test - public void findAll() { + void findAll() { List modules = authModuleDAO.findAll(); assertNotNull(modules); assertFalse(modules.isEmpty()); @@ -60,7 +60,7 @@ public void findAll() { } @Test - public void find() { + void find() { AuthModule authModule = authModuleDAO.findById("DefaultLDAPAuthModule").orElseThrow(); assertTrue(authModule.getConf() instanceof LDAPAuthModuleConf); @@ -93,7 +93,7 @@ public void find() { } @Test - public void findByType() { + void findByType() { List authModules = authModuleDAO.findAll(); assertTrue(authModules.stream().anyMatch( authModule -> authModule.getConf() instanceof LDAPAuthModuleConf @@ -151,7 +151,7 @@ private void saveAuthModule(final String key, final AuthModuleConf conf) { } @Test - public void saveWithStaticModule() { + void saveWithStaticModule() { StaticAuthModuleConf conf = new StaticAuthModuleConf(); conf.getUsers().put("user1", UUID.randomUUID().toString()); conf.getUsers().put("user2", "user2Password123"); @@ -160,7 +160,7 @@ public void saveWithStaticModule() { } @Test - public void saveWithJaasModule() { + void saveWithJaasModule() { JaasAuthModuleConf conf = new JaasAuthModuleConf(); conf.setKerberosKdcSystemProperty("sample-value"); conf.setKerberosRealmSystemProperty("sample-value"); @@ -172,7 +172,7 @@ public void saveWithJaasModule() { } @Test - public void saveWithLdapModule() { + void saveWithLdapModule() { LDAPAuthModuleConf conf = new LDAPAuthModuleConf(); conf.setBaseDn("dc=example,dc=org"); conf.setSearchFilter("cn={user}"); @@ -185,7 +185,7 @@ public void saveWithLdapModule() { } @Test - public void saveWithGoogleAuthenticatorModule() { + void saveWithGoogleAuthenticatorModule() { GoogleMfaAuthModuleConf conf = new GoogleMfaAuthModuleConf(); conf.setCodeDigits(6); conf.setIssuer("SyncopeTest"); @@ -197,7 +197,7 @@ public void saveWithGoogleAuthenticatorModule() { } @Test - public void saveWithDuoAuthenticatorModule() { + void saveWithDuoAuthenticatorModule() { DuoMfaAuthModuleConf conf = new DuoMfaAuthModuleConf(); conf.setSecretKey("Q2IU2i6BFNd6VYflZT8Evl6lF7oPlj4PM15BmRU7"); conf.setIntegrationKey("DIOXVRZD1UMZ8XXMNFQ6"); @@ -206,7 +206,7 @@ public void saveWithDuoAuthenticatorModule() { } @Test - public void saveWithOIDCAuthModule() { + void saveWithOIDCAuthModule() { OIDCAuthModuleConf conf = new OIDCAuthModuleConf(); conf.setClientId("OIDCTestId"); conf.setDiscoveryUri("www.testurl.com"); @@ -218,7 +218,7 @@ public void saveWithOIDCAuthModule() { } @Test - public void saveWithJDBCModule() { + void saveWithJDBCModule() { JDBCAuthModuleConf conf = new JDBCAuthModuleConf(); conf.setSql("SELECT * FROM table WHERE name=?"); conf.setFieldPassword("password"); @@ -227,7 +227,7 @@ public void saveWithJDBCModule() { } @Test - public void saveWithSyncopeModule() { + void saveWithSyncopeModule() { SyncopeAuthModuleConf conf = new SyncopeAuthModuleConf(); conf.setDomain("Master"); @@ -235,7 +235,7 @@ public void saveWithSyncopeModule() { } @Test - public void saveWithSAML2IdPModule() { + void saveWithSAML2IdPModule() { SAML2IdPAuthModuleConf conf = new SAML2IdPAuthModuleConf(); conf.setServiceProviderEntityId("testEntityId"); conf.setProviderName("testProviderName"); @@ -243,7 +243,7 @@ public void saveWithSAML2IdPModule() { } @Test - public void saveWithSimpleMfaModule() { + void saveWithSimpleMfaModule() { SimpleMfaAuthModuleConf conf = new SimpleMfaAuthModuleConf(); conf.setTokenLength(9); conf.setTimeToKillInSeconds(120); @@ -251,7 +251,7 @@ public void saveWithSimpleMfaModule() { } @Test - public void updateWithLDAPModule() { + void updateWithLDAPModule() { AuthModule module = authModuleDAO.findById("DefaultLDAPAuthModule").orElseThrow(); AuthModuleConf conf = module.getConf(); LDAPAuthModuleConf.class.cast(conf).setBaseDn("dc=example2,dc=org"); @@ -268,7 +268,7 @@ public void updateWithLDAPModule() { } @Test - public void updateWithJDBCModule() { + void updateWithJDBCModule() { AuthModule module = authModuleDAO.findById("DefaultJDBCAuthModule").orElseThrow(); AuthModuleConf conf = module.getConf(); JDBCAuthModuleConf.class.cast(conf).setSql("SELECT * FROM otherTable WHERE name=?"); @@ -282,7 +282,7 @@ public void updateWithJDBCModule() { } @Test - public void updateWithGoogleMfaModule() { + void updateWithGoogleMfaModule() { AuthModule module = authModuleDAO.findById("DefaultGoogleMfaAuthModule").orElseThrow(); AuthModuleConf conf = module.getConf(); GoogleMfaAuthModuleConf.class.cast(conf).setLabel("newLabel"); @@ -296,7 +296,7 @@ public void updateWithGoogleMfaModule() { } @Test - public void updateWithDuoMfaModule() { + void updateWithDuoMfaModule() { AuthModule module = authModuleDAO.findById("DefaultDuoMfaAuthModule").orElseThrow(); AuthModuleConf conf = module.getConf(); String secretKey = UUID.randomUUID().toString(); @@ -311,7 +311,7 @@ public void updateWithDuoMfaModule() { } @Test - public void updateWithSAML2IdPModule() { + void updateWithSAML2IdPModule() { AuthModule module = authModuleDAO.findById("DefaultSAML2IdPAuthModule").orElseThrow(); AuthModuleConf conf = module.getConf(); SAML2IdPAuthModuleConf.class.cast(conf).setServiceProviderEntityId("newEntityId"); @@ -326,7 +326,7 @@ public void updateWithSAML2IdPModule() { } @Test - public void updateWithOIDCModule() { + void updateWithOIDCModule() { AuthModule module = authModuleDAO.findById("DefaultOIDCAuthModule").orElseThrow(); AuthModuleConf conf = module.getConf(); OIDCAuthModuleConf.class.cast(conf).setResponseType("newCode"); @@ -340,7 +340,7 @@ public void updateWithOIDCModule() { } @Test - public void updateWithJaasModule() { + void updateWithJaasModule() { AuthModule module = authModuleDAO.findById("DefaultJaasAuthModule").orElseThrow(); AuthModuleConf conf = module.getConf(); JaasAuthModuleConf.class.cast(conf).setRealm("SYNCOPE_NEW"); @@ -354,7 +354,7 @@ public void updateWithJaasModule() { } @Test - public void updateWithStaticModule() { + void updateWithStaticModule() { AuthModule module = authModuleDAO.findById("DefaultStaticAuthModule").orElseThrow(); assertNotNull(module); assertEquals(1, StaticAuthModuleConf.class.cast(module.getConf()).getUsers().size()); @@ -370,7 +370,7 @@ public void updateWithStaticModule() { } @Test - public void updateWithSyncopeModule() { + void updateWithSyncopeModule() { AuthModule module = authModuleDAO.findById("DefaultSyncopeAuthModule").orElseThrow(); AuthModuleConf conf = module.getConf(); @@ -385,7 +385,7 @@ public void updateWithSyncopeModule() { } @Test - public void delete() { + void delete() { assertTrue(authModuleDAO.findById("DefaultSyncopeAuthModule").isPresent()); authModuleDAO.deleteById("DefaultSyncopeAuthModule"); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java index 624811c7e02..45b175fbe11 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AuthProfileTest.java @@ -43,18 +43,18 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AuthProfileTest extends AbstractTest { +class AuthProfileTest extends AbstractTest { @Autowired private AuthProfileDAO authProfileDAO; @BeforeEach - public void beforeEach() { + void beforeEach() { entityManager.createQuery("DELETE FROM " + JPAAuthProfile.class.getSimpleName()).executeUpdate(); } @Test - public void googleMfaToken() { + void googleMfaToken() { String id = SecureRandomUtils.generateRandomUUID().toString(); AuthProfile profile = entityFactory.newEntity(AuthProfile.class); @@ -85,7 +85,7 @@ public void googleMfaToken() { } @Test - public void webAuthnRegisteredDevice() { + void webAuthnRegisteredDevice() { String id = SecureRandomUtils.generateRandomUUID().toString(); String record = "[ {" + " \"userIdentity\" : {" @@ -122,7 +122,7 @@ public void webAuthnRegisteredDevice() { } @Test - public void googleMfaAccount() { + void googleMfaAccount() { String id = SecureRandomUtils.generateRandomUUID().toString(); createAuthProfileWithAccount(id); @@ -144,7 +144,7 @@ public void googleMfaAccount() { } @Test - public void impersonationAccounts() { + void impersonationAccounts() { String id = SecureRandomUtils.generateRandomUUID().toString(); createAuthProfileWithAccount(id); @@ -167,7 +167,7 @@ public void impersonationAccounts() { } @Test - public void findByOwnerLike() { + void findByOwnerLike() { createAuthProfileWithAccount("owner1"); createAuthProfileWithAccount("owner2"); createAuthProfileWithAccount("test"); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/CASSPTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/CASSPTest.java index 98401ba63c0..16ef763bedf 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/CASSPTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/CASSPTest.java @@ -32,13 +32,13 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class CASSPTest extends AbstractClientAppTest { +class CASSPTest extends AbstractClientAppTest { @Autowired private CASSPClientAppDAO casspDAO; @Test - public void find() { + void find() { long beforeCount = casspDAO.count(); CASSPClientApp rp = entityFactory.newEntity(CASSPClientApp.class); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ConnInstanceTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ConnInstanceTest.java index b91c28e52bb..d8b8e1edbb2 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ConnInstanceTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ConnInstanceTest.java @@ -45,7 +45,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class ConnInstanceTest extends AbstractTest { +class ConnInstanceTest extends AbstractTest { @Autowired private ConnInstanceDAO connInstanceDAO; @@ -54,7 +54,7 @@ public class ConnInstanceTest extends AbstractTest { private RealmDAO realmDAO; @Test - public void findAll() { + void findAll() { List authorities = IdMEntitlement.values().stream(). map(entitlement -> new SyncopeGrantedAuthority(entitlement, SyncopeConstants.ROOT_REALM)). collect(Collectors.toList()); @@ -75,7 +75,7 @@ public void findAll() { } @Test - public void findById() { + void findById() { ConnInstance connInstance = connInstanceDAO.findById("88a7a819-dab5-46b4-9b90-0b9769eabdb8").orElseThrow(); assertNotNull(connInstance); assertEquals("net.tirasa.connid.bundles.soap.WebServiceConnector", connInstance.getConnectorName()); @@ -90,7 +90,7 @@ public void findById() { } @Test - public void save() { + void save() { ConnInstance connInstance = entityFactory.newEntity(ConnInstance.class); connInstance.setLocation("file:" + System.getProperty("java.io.tmpdir")); connInstance.setVersion("1.0"); @@ -140,7 +140,7 @@ public void save() { } @Test - public void delete() { + void delete() { ConnInstance connInstance = connInstanceDAO.findById("88a7a819-dab5-46b4-9b90-0b9769eabdb8").orElseThrow(); connInstanceDAO.deleteById(connInstance.getKey()); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/DerSchemaTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/DerSchemaTest.java index 07b9229dc0a..cd4bcab0bc9 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/DerSchemaTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/DerSchemaTest.java @@ -33,30 +33,30 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class DerSchemaTest extends AbstractTest { +class DerSchemaTest extends AbstractTest { @Autowired private DerSchemaDAO derSchemaDAO; @Test - public void findAll() { + void findAll() { List list = derSchemaDAO.findAll(); assertEquals(10, list.size()); } @Test - public void findByIdLike() { + void findByIdLike() { List schemas = derSchemaDAO.findByIdLike("mderivedd%"); assertEquals(1, schemas.size()); } @Test - public void findByName() { + void findByName() { assertTrue(derSchemaDAO.findById("cn").isPresent()); } @Test - public void save() { + void save() { DerSchema derivedAttributeSchema = entityFactory.newEntity(DerSchema.class); derivedAttributeSchema.setKey("cn2"); derivedAttributeSchema.setExpression("firstname surname"); @@ -68,7 +68,7 @@ public void save() { } @Test - public void delete() { + void delete() { DerSchema cn = derSchemaDAO.findById("cn").orElseThrow(); derSchemaDAO.deleteById(cn.getKey()); @@ -84,7 +84,7 @@ public void delete() { } @Test - public void issueSYNCOPE418() { + void issueSYNCOPE418() { DerSchema schema = entityFactory.newEntity(DerSchema.class); schema.setKey("http://schemas.examples.org/security/authorization/organizationUnit"); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java index 12a71399bba..6a481b6a28d 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java @@ -34,7 +34,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class GroupTest extends AbstractTest { +class GroupTest extends AbstractTest { @Autowired private GroupDAO groupDAO; @@ -46,13 +46,13 @@ public class GroupTest extends AbstractTest { private AnyTypeDAO anyTypeDAO; @Test - public void findAll() { + void findAll() { List groups = groupDAO.findAll(); assertEquals(16, groups.size()); } @Test - public void find() { + void find() { Group group = groupDAO.findByName("additional").orElseThrow(); assertNotNull(group); assertEquals(1, group.getTypeExtensions().size()); @@ -60,7 +60,7 @@ public void find() { } @Test - public void findKeysByNamePattern() { + void findKeysByNamePattern() { List groups = groupDAO.findKeysByNamePattern("%child"); assertEquals(2, groups.size()); assertTrue(groups.contains("b1f7c12d-ec83-441f-a50e-1691daaedf3b")); @@ -68,7 +68,7 @@ public void findKeysByNamePattern() { } @Test - public void save() { + void save() { Group group = entityFactory.newEntity(Group.class); group.setName("secondChild"); group.setRealm(realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); @@ -79,7 +79,7 @@ public void save() { } @Test - public void delete() { + void delete() { Group group = groupDAO.findById("8fb2d51e-c605-4e80-a72b-13ffecf1aa9a").orElseThrow(); groupDAO.deleteById(group.getKey()); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java index f1f78f26097..08be4b7a51b 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java @@ -34,20 +34,20 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class ImplementationTest extends AbstractTest { +class ImplementationTest extends AbstractTest { @Autowired private ImplementationDAO implementationDAO; @Test - public void findById() { + void findById() { Implementation impl = implementationDAO.findById("ExpiredBatchCleanup").orElseThrow(); assertEquals(IdRepoImplementationType.TASKJOB_DELEGATE, impl.getType()); assertEquals(ImplementationEngine.JAVA, impl.getEngine()); } @Test - public void findAll() { + void findAll() { List implementations = implementationDAO.findAll(); assertFalse(implementations.isEmpty()); @@ -85,7 +85,7 @@ public void findAll() { } @Test - public void create() { + void create() { Implementation impl = entityFactory.newEntity(Implementation.class); impl.setKey("new"); impl.setEngine(ImplementationEngine.GROOVY); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MailTemplateTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MailTemplateTest.java index 9270a10779b..23b1ea97c29 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MailTemplateTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MailTemplateTest.java @@ -32,27 +32,27 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class MailTemplateTest extends AbstractTest { +class MailTemplateTest extends AbstractTest { @Autowired private MailTemplateDAO mailTemplateDAO; @Test - public void find() { + void find() { MailTemplate optin = mailTemplateDAO.findById("optin").orElseThrow(); assertNotNull(optin.getTextTemplate()); assertNotNull(optin.getHTMLTemplate()); } @Test - public void findAll() { + void findAll() { List templates = mailTemplateDAO.findAll(); assertNotNull(templates); assertFalse(templates.isEmpty()); } @Test - public void save() { + void save() { MailTemplate template = entityFactory.newEntity(MailTemplate.class); template.setKey("new"); template.setTextTemplate("Text template"); @@ -70,7 +70,7 @@ public void save() { } @Test - public void delete() { + void delete() { mailTemplateDAO.deleteById("optin"); assertTrue(mailTemplateDAO.findById("optin").isEmpty()); } diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java index ab83a95e8c1..d6c437e11a0 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java @@ -41,7 +41,7 @@ @Tag("multitenancy") @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS) @Transactional -public class MultitenancyTest extends AbstractTest { +class MultitenancyTest extends AbstractTest { static { PersistenceTestContext.TEST_DOMAIN.set("Two"); @@ -65,12 +65,12 @@ public static void restoreDomain() { private UserDAO userDAO; @Test - public void readPlainSchemas() { + void readPlainSchemas() { assertEquals(1, plainSchemaDAO.findAll().size()); } @Test - public void readRealm() { + void readRealm() { assertEquals( 1, realmSearchDAO.findDescendants(realmDAO.getRoot().getFullPath(), null).size()); @@ -80,7 +80,7 @@ public void readRealm() { } @Test - public void createUser() { + void createUser() { assertNull(realmDAO.getRoot().getPasswordPolicy()); User user = entityFactory.newEntity(User.class); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/NotificationTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/NotificationTest.java index d0f85bfd65d..83465797781 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/NotificationTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/NotificationTest.java @@ -34,7 +34,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class NotificationTest extends AbstractTest { +class NotificationTest extends AbstractTest { @Autowired private AnyTypeDAO anyTypeDAO; @@ -46,7 +46,7 @@ public class NotificationTest extends AbstractTest { private MailTemplateDAO mailTemplateDAO; @Test - public void find() { + void find() { Notification notification = notificationDAO.findById("9e2b911c-25de-4c77-bcea-b86ed9451050").orElseThrow(); assertNotNull(notification.getEvents()); assertFalse(notification.getEvents().isEmpty()); @@ -55,14 +55,14 @@ public void find() { } @Test - public void findAll() { + void findAll() { List notifications = notificationDAO.findAll(); assertNotNull(notifications); assertFalse(notifications.isEmpty()); } @Test - public void save() { + void save() { Notification notification = entityFactory.newEntity(Notification.class); notification.getEvents().add("save"); @@ -86,13 +86,13 @@ public void save() { } @Test - public void delete() { + void delete() { notificationDAO.deleteById("9e2b911c-25de-4c77-bcea-b86ed9451050"); assertTrue(notificationDAO.findById("9e2b911c-25de-4c77-bcea-b86ed9451050").isEmpty()); } @Test - public void issueSYNCOPE445() { + void issueSYNCOPE445() { Notification notification = entityFactory.newEntity(Notification.class); notification.getEvents().add("save"); @@ -123,7 +123,7 @@ public void issueSYNCOPE445() { } @Test - public void issueSYNCOPE446() { + void issueSYNCOPE446() { Notification notification = entityFactory.newEntity(Notification.class); notification.getEvents().add("[LOGIC]:[GroupLogic]:[]:[create]:[SUCCESS]"); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCOpEntityTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCOpEntityTest.java index 83ff443b5bf..00000656a7e 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCOpEntityTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCOpEntityTest.java @@ -36,13 +36,13 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class OIDCOpEntityTest extends AbstractTest { +class OIDCOpEntityTest extends AbstractTest { @Autowired private OIDCOpEntityDAO oidcOpEntityDAO; @Test - public void save() throws Exception { + void save() throws Exception { OIDCOpEntity oidcOpEntity = entityFactory.newEntity(OIDCOpEntity.class); RSAKey jwk = new RSAKeyGenerator(2048). diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCRPTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCRPTest.java index 62198157cb4..bea60e73d4e 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCRPTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCRPTest.java @@ -35,13 +35,13 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class OIDCRPTest extends AbstractClientAppTest { +class OIDCRPTest extends AbstractClientAppTest { @Autowired private OIDCRPClientAppDAO oidcrpDAO; @Test - public void find() { + void find() { int beforeCount = oidcrpDAO.findAll().size(); OIDCRPClientApp rp = entityFactory.newEntity(OIDCRPClientApp.class); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PasswordManagementTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PasswordManagementTest.java index 51cd4b90521..4d33c4033ba 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PasswordManagementTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PasswordManagementTest.java @@ -38,13 +38,13 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class PasswordManagementTest extends AbstractTest { +class PasswordManagementTest extends AbstractTest { @Autowired private PasswordManagementDAO passwordManagementDAO; @Test - public void findAll() { + void findAll() { List modules = passwordManagementDAO.findAll(); assertNotNull(modules); assertFalse(modules.isEmpty()); @@ -52,7 +52,7 @@ public void findAll() { } @Test - public void find() { + void find() { PasswordManagement passwordManagement = passwordManagementDAO.findById("DefaultSyncopePasswordManagement") .orElseThrow(); assertInstanceOf(SyncopePasswordManagementConf.class, passwordManagement.getConf()); @@ -68,7 +68,7 @@ public void find() { } @Test - public void findByType() { + void findByType() { List passwordManagements = passwordManagementDAO.findAll(); assertTrue(passwordManagements.stream().anyMatch( passwordManagement -> passwordManagement.getConf() instanceof SyncopePasswordManagementConf @@ -98,7 +98,7 @@ private void savePasswordManagement(final String key, final PasswordManagementCo } @Test - public void saveWithSyncopeModule() { + void saveWithSyncopeModule() { SyncopePasswordManagementConf conf = new SyncopePasswordManagementConf(); conf.setDomain("Master"); @@ -106,7 +106,7 @@ public void saveWithSyncopeModule() { } @Test - public void saveWithLDAPModule() { + void saveWithLDAPModule() { LDAPPasswordManagementConf conf = new LDAPPasswordManagementConf(); conf.setBaseDn("dc=example,dc=org"); conf.setSearchFilter("cn={user}"); @@ -119,7 +119,7 @@ public void saveWithLDAPModule() { } @Test - public void saveWithJDBCModule() { + void saveWithJDBCModule() { JDBCPasswordManagementConf conf = new JDBCPasswordManagementConf(); conf.setSqlFindEmail("SELECT email from users_table where name=?"); conf.setSqlFindPhone("SELECT phoneNumber from users_table where name=?"); @@ -130,7 +130,7 @@ public void saveWithJDBCModule() { } @Test - public void saveWithRESTModule() { + void saveWithRESTModule() { RESTPasswordManagementConf conf = new RESTPasswordManagementConf(); conf.setEndpointPassword("password"); conf.setEndpointUrlAccountUnlock("http://localhost:9443/syncope-fit-build-tools/cxf/rest/unlockAccount"); @@ -148,7 +148,7 @@ public void saveWithRESTModule() { } @Test - public void updateWithSyncopeModule() { + void updateWithSyncopeModule() { PasswordManagement module = passwordManagementDAO.findById("DefaultSyncopePasswordManagement").orElseThrow(); PasswordManagementConf conf = module.getConf(); @@ -164,7 +164,7 @@ public void updateWithSyncopeModule() { } @Test - public void updateWithLDAPModule() { + void updateWithLDAPModule() { PasswordManagement module = passwordManagementDAO.findById("DefaultLDAPPasswordManagement").orElseThrow(); PasswordManagementConf conf = module.getConf(); @@ -182,7 +182,7 @@ public void updateWithLDAPModule() { } @Test - public void updateWithJDBCModule() { + void updateWithJDBCModule() { PasswordManagement module = passwordManagementDAO.findById("DefaultJDBCPasswordManagement").orElseThrow(); PasswordManagementConf conf = module.getConf(); @@ -199,7 +199,7 @@ public void updateWithJDBCModule() { } @Test - public void updateWithRESTModule() { + void updateWithRESTModule() { PasswordManagement module = passwordManagementDAO.findById("DefaultRESTPasswordManagement").orElseThrow(); PasswordManagementConf conf = module.getConf(); @@ -216,7 +216,7 @@ public void updateWithRESTModule() { } @Test - public void delete() { + void delete() { assertTrue(passwordManagementDAO.findById("DefaultSyncopePasswordManagement").isPresent()); passwordManagementDAO.deleteById("DefaultSyncopePasswordManagement"); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainAttrTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainAttrTest.java index 340ba1134ff..449c5baa1db 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainAttrTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainAttrTest.java @@ -50,7 +50,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class PlainAttrTest extends AbstractTest { +class PlainAttrTest extends AbstractTest { @Autowired private UserDAO userDAO; @@ -68,7 +68,7 @@ public class PlainAttrTest extends AbstractTest { private EncryptorManager encryptorManager; @Test - public void save() throws ClassNotFoundException { + void save() throws ClassNotFoundException { PlainSchema emailSchema = plainSchemaDAO.findById("email").orElseThrow(); PlainAttr attr = new PlainAttr(); @@ -92,7 +92,7 @@ public void save() throws ClassNotFoundException { } @Test - public void invalidValueList() { + void invalidValueList() { User user = userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").orElseThrow(); PlainSchema emailSchema = plainSchemaDAO.findById("email").orElseThrow(); @@ -115,7 +115,7 @@ public void invalidValueList() { } @Test - public void saveWithEncrypted() throws Exception { + void saveWithEncrypted() throws Exception { User user = userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").orElseThrow(); PlainSchema obscureSchema = plainSchemaDAO.findById("obscure").orElseThrow(); @@ -138,7 +138,7 @@ public void saveWithEncrypted() throws Exception { } @Test - public void encryptedWithKeyAsSysProp() throws Exception { + void encryptedWithKeyAsSysProp() throws Exception { PlainSchema obscureSchema = plainSchemaDAO.findById("obscure").orElseThrow(); PlainSchema obscureWithKeyAsSysprop = entityFactory.newEntity(PlainSchema.class); @@ -161,7 +161,7 @@ public void encryptedWithKeyAsSysProp() throws Exception { } @Test - public void encryptedWithDecodeConversionPattern() throws Exception { + void encryptedWithDecodeConversionPattern() throws Exception { PlainSchema obscureWithDecodeConversionPattern = entityFactory.newEntity(PlainSchema.class); obscureWithDecodeConversionPattern.setKey("obscureWithDecodeConversionPattern"); obscureWithDecodeConversionPattern.setAnyTypeClass(anyTypeClassDAO.findById("other").orElseThrow()); @@ -187,7 +187,7 @@ public void encryptedWithDecodeConversionPattern() throws Exception { } @Test - public void saveWithBinary() throws UnsupportedEncodingException { + void saveWithBinary() throws UnsupportedEncodingException { User user = userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").orElseThrow(); PlainSchema photoSchema = plainSchemaDAO.findById("photo").orElseThrow(); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java index e354f1f8485..75ee7522f5c 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainSchemaTest.java @@ -43,7 +43,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class PlainSchemaTest extends AbstractTest { +class PlainSchemaTest extends AbstractTest { @Autowired private PlainSchemaDAO plainSchemaDAO; @@ -52,20 +52,20 @@ public class PlainSchemaTest extends AbstractTest { private ImplementationDAO implementationDAO; @Test - public void findAll() { + void findAll() { List schemas = plainSchemaDAO.findAll(); assertEquals(28, schemas.size()); } @Test - public void findByIdLike() { + void findByIdLike() { List schemas = plainSchemaDAO.findByIdLike("fullna%"); assertEquals(1, schemas.size()); assertEquals(0, schemas.getFirst().getLabels().size()); } @Test - public void findByName() { + void findByName() { PlainSchema schema = plainSchemaDAO.findById("firstname").orElseThrow(); assertEquals(3, schema.getLabels().size()); @@ -74,7 +74,7 @@ public void findByName() { } @Test - public void hasAttrs() { + void hasAttrs() { PlainSchema schema = plainSchemaDAO.findById("icon").orElseThrow(); assertTrue(plainSchemaDAO.hasAttrs(schema)); @@ -83,7 +83,7 @@ public void hasAttrs() { } @Test - public void existsPlainAttrUniqueValue() { + void existsPlainAttrUniqueValue() { PlainAttrValue value = new PlainAttrValue(); value.setStringValue("rossini@apache.org"); PlainAttr attr = new PlainAttr(); @@ -110,7 +110,7 @@ public void existsPlainAttrUniqueValue() { } @Test - public void save() { + void save() { PlainSchema schema = entityFactory.newEntity(PlainSchema.class); schema.setKey("secondaryEmail"); schema.setType(AttrSchemaType.String); @@ -125,7 +125,7 @@ public void save() { } @Test - public void saveNonValid() { + void saveNonValid() { assertThrows(InvalidEntityException.class, () -> { PlainSchema schema = entityFactory.newEntity(PlainSchema.class); schema.setKey("secondaryEmail"); @@ -140,7 +140,7 @@ public void saveNonValid() { } @Test - public void checkForEnumType() { + void checkForEnumType() { PlainSchema schema = entityFactory.newEntity(PlainSchema.class); schema.setType(AttrSchemaType.Enum); schema.setKey("color"); @@ -163,7 +163,7 @@ public void checkForEnumType() { } @Test - public void checkForDropdownType() { + void checkForDropdownType() { PlainSchema schema = entityFactory.newEntity(PlainSchema.class); schema.setType(AttrSchemaType.Dropdown); schema.setKey("dropdown"); @@ -185,14 +185,14 @@ public void checkForDropdownType() { } @Test - public void saveInvalidSchema() { + void saveInvalidSchema() { PlainSchema schema = entityFactory.newEntity(PlainSchema.class); schema.setKey("username"); assertThrows(InvalidEntityException.class, () -> plainSchemaDAO.save(schema)); } @Test - public void delete() { + void delete() { PlainSchema firstname = plainSchemaDAO.findById("firstname").orElseThrow(); plainSchemaDAO.deleteById(firstname.getKey()); @@ -201,7 +201,7 @@ public void delete() { } @Test - public void issueSYNCOPE418() { + void issueSYNCOPE418() { PlainSchema schema = entityFactory.newEntity(PlainSchema.class); schema.setKey("http://schemas.examples.org/security/authorization/organizationUnit"); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java index d52c4aaa3fc..5bb83f007f1 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java @@ -62,7 +62,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class PolicyTest extends AbstractTest { +class PolicyTest extends AbstractTest { @Autowired private AnyTypeDAO anyTypeDAO; @@ -74,7 +74,7 @@ public class PolicyTest extends AbstractTest { private ImplementationDAO implementationDAO; @Test - public void findAll() { + void findAll() { assertEquals(17, policyDAO.findAll().size()); assertEquals(1, policyDAO.findAll(AccessPolicy.class).size()); @@ -89,7 +89,7 @@ public void findAll() { } @Test - public void findByKey() { + void findByKey() { PropagationPolicy propagationPolicy = policyDAO.findById( "89d322db-9878-420c-b49c-67be13df9a12", PropagationPolicy.class).orElseThrow(); assertEquals(BackOffStrategy.FIXED, propagationPolicy.getBackOffStrategy()); @@ -131,7 +131,7 @@ public void findByKey() { } @Test - public void createPropagation() { + void createPropagation() { long beforeCount = policyDAO.findAll().size(); PropagationPolicy propagationPolicy = entityFactory.newEntity(PropagationPolicy.class); @@ -151,7 +151,7 @@ public void createPropagation() { } @Test - public void createPull() { + void createPull() { InboundPolicy inboundPolicy = entityFactory.newEntity(InboundPolicy.class); inboundPolicy.setConflictResolutionAction(ConflictResolutionAction.IGNORE); inboundPolicy.setName("Pull policy"); @@ -195,7 +195,7 @@ public void createPull() { } @Test - public void createPush() { + void createPush() { PushPolicy pushPolicy = entityFactory.newEntity(PushPolicy.class); pushPolicy.setName("Push policy"); pushPolicy.setConflictResolutionAction(ConflictResolutionAction.IGNORE); @@ -239,7 +239,7 @@ public void createPush() { } @Test - public void createAccess() { + void createAccess() { long beforeCount = policyDAO.findAll().size(); AccessPolicy accessPolicy = entityFactory.newEntity(AccessPolicy.class); @@ -259,7 +259,7 @@ public void createAccess() { } @Test - public void createAuth() { + void createAuth() { long beforeCount = policyDAO.findAll().size(); AuthPolicy authPolicy = entityFactory.newEntity(AuthPolicy.class); @@ -280,7 +280,7 @@ public void createAuth() { } @Test - public void createAttrRelease() { + void createAttrRelease() { long beforeCount = policyDAO.findAll().size(); AttrReleasePolicy attrReleasePolicy = entityFactory.newEntity(AttrReleasePolicy.class); @@ -304,7 +304,7 @@ public void createAttrRelease() { } @Test - public void createTicketExpiration() { + void createTicketExpiration() { long beforeCount = policyDAO.findAll().size(); TicketExpirationPolicy ticketExpirationPolicy = entityFactory.newEntity(TicketExpirationPolicy.class); @@ -332,7 +332,7 @@ public void createTicketExpiration() { } @Test - public void update() { + void update() { PasswordPolicy policy = policyDAO.findById( "ce93fcda-dc3a-4369-a7b0-a6108c261c85", PasswordPolicy.class).orElseThrow(); assertEquals(1, policy.getRules().size()); @@ -362,7 +362,7 @@ public void update() { } @Test - public void delete() { + void delete() { Policy policy = policyDAO.findById("66691e96-285f-4464-bc19-e68384ea4c85").orElseThrow(); policyDAO.delete(policy); assertTrue(policyDAO.findById("66691e96-285f-4464-bc19-e68384ea4c85").isEmpty()); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RealmTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RealmTest.java index 55453d5d9a0..2dd63410927 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RealmTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RealmTest.java @@ -45,7 +45,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class RealmTest extends AbstractTest { +class RealmTest extends AbstractTest { @Autowired private RealmDAO realmDAO; @@ -57,12 +57,12 @@ public class RealmTest extends AbstractTest { private PolicyDAO policyDAO; @Test - public void getRoot() { + void getRoot() { assertNotNull(realmDAO.getRoot()); } @Test - public void find() { + void find() { Realm realm = realmDAO.findById("e4c28e7a-9dbf-4ee7-9441-93812a0d4a28").orElseThrow(); assertEquals(SyncopeConstants.ROOT_REALM, realm.getName()); assertEquals(SyncopeConstants.ROOT_REALM, realm.getFullPath()); @@ -80,12 +80,12 @@ public void find() { } @Test - public void findInvalidPath() { + void findInvalidPath() { assertThrows(MalformedPathException.class, () -> realmSearchDAO.findByFullPath("even/two")); } @Test - public void findChildren() { + void findChildren() { List children = realmSearchDAO.findChildren( realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); assertEquals(2, children.size()); @@ -97,7 +97,7 @@ public void findChildren() { } @Test - public void findAll() { + void findAll() { List list = realmSearchDAO.findDescendants(realmDAO.getRoot().getFullPath(), null); assertNotNull(list); assertFalse(list.isEmpty()); @@ -113,7 +113,7 @@ public void findAll() { } @Test - public void save() { + void save() { Realm realm = entityFactory.newEntity(Realm.class); realm.setName("last"); realm.setParent(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); @@ -138,7 +138,7 @@ public void save() { } @Test - public void saveInvalidName() { + void saveInvalidName() { Realm realm = entityFactory.newEntity(Realm.class); realm.setName(" a name"); realm.setParent(realmDAO.getRoot()); @@ -152,7 +152,7 @@ public void saveInvalidName() { } @Test - public void saveNullParent() { + void saveNullParent() { Realm realm = entityFactory.newEntity(Realm.class); realm.setName("name"); realm.setParent(null); @@ -166,7 +166,7 @@ public void saveNullParent() { } @Test - public void delete() { + void delete() { Realm realm = entityFactory.newEntity(Realm.class); realm.setName("name"); realm.setParent(realmDAO.getRoot()); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RelationshipTypeTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RelationshipTypeTest.java index 0b4289c44a4..22e533a167f 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RelationshipTypeTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RelationshipTypeTest.java @@ -37,7 +37,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class RelationshipTypeTest extends AbstractTest { +class RelationshipTypeTest extends AbstractTest { @Autowired private RelationshipTypeDAO relationshipTypeDAO; @@ -49,7 +49,7 @@ public class RelationshipTypeTest extends AbstractTest { private AnyTypeDAO anyTypeDAO; @Test - public void find() { + void find() { RelationshipType inclusion = relationshipTypeDAO.findById("inclusion").orElseThrow(); assertEquals("inclusion", inclusion.getKey()); assertEquals(anyTypeDAO.findById("PRINTER").orElseThrow(), inclusion.getLeftEndAnyType()); @@ -57,7 +57,7 @@ public void find() { } @Test - public void findByEndAnyType() { + void findByEndAnyType() { List relationshipTypes = relationshipTypeDAO.findByEndAnyType(anyTypeDAO.findById("PRINTER").orElseThrow()); assertEquals(2, relationshipTypes.size()); @@ -66,13 +66,13 @@ public void findByEndAnyType() { } @Test - public void findAll() { + void findAll() { List list = relationshipTypeDAO.findAll(); assertFalse(list.isEmpty()); } @Test - public void save() { + void save() { RelationshipType newType = entityFactory.newEntity(RelationshipType.class); newType.setKey("new type"); newType.setDescription("description"); @@ -85,7 +85,7 @@ public void save() { } @Test - public void saveInvalidName() { + void saveInvalidName() { assertThrows(InvalidEntityException.class, () -> { RelationshipType newType = entityFactory.newEntity(RelationshipType.class); newType.setKey("membership"); @@ -94,7 +94,7 @@ public void saveInvalidName() { } @Test - public void delete() { + void delete() { RelationshipType type = relationshipTypeDAO.findById("neighborhood").orElseThrow(); relationshipTypeDAO.deleteById(type.getKey()); @@ -103,7 +103,7 @@ public void delete() { } @Test - public void deleteOnAnyObject() { + void deleteOnAnyObject() { RelationshipType neighborhood = relationshipTypeDAO.findById("inclusion").orElseThrow(); AnyObject anyObject = anyObjectDAO.findById("fc6dbc3a-6c07-4965-8781-921e7401a4a5").orElseThrow(); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RemediationTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RemediationTest.java index 8a97f583046..6c4feef09db 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RemediationTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RemediationTest.java @@ -44,7 +44,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class RemediationTest extends AbstractTest { +class RemediationTest extends AbstractTest { @Autowired private RemediationDAO remediationDAO; @@ -56,13 +56,13 @@ public class RemediationTest extends AbstractTest { private TaskDAO taskDAO; @Test - public void findAll() { + void findAll() { List remediations = remediationDAO.findAll(null, null, Pageable.unpaged()); assertTrue(remediations.isEmpty()); } @Test - public void createMissingPayload() { + void createMissingPayload() { Remediation remediation = entityFactory.newEntity(Remediation.class); remediation.setAnyType(anyTypeDAO.findById("PRINTER").orElseThrow()); remediation.setOperation(ResourceOperation.CREATE); @@ -84,7 +84,7 @@ public void createMissingPayload() { } @Test - public void createWrongPayload() { + void createWrongPayload() { Remediation remediation = entityFactory.newEntity(Remediation.class); remediation.setAnyType(anyTypeDAO.findById("PRINTER").orElseThrow()); remediation.setOperation(ResourceOperation.CREATE); @@ -107,7 +107,7 @@ public void createWrongPayload() { } @Test - public void create() { + void create() { Remediation remediation = entityFactory.newEntity(Remediation.class); remediation.setAnyType(anyTypeDAO.findById("PRINTER").orElseThrow()); remediation.setOperation(ResourceOperation.CREATE); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ReportTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ReportTest.java index 601036f3aee..5cda53ded93 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ReportTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ReportTest.java @@ -34,7 +34,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class ReportTest extends AbstractTest { +class ReportTest extends AbstractTest { @Autowired private ReportDAO reportDAO; @@ -43,7 +43,7 @@ public class ReportTest extends AbstractTest { private ImplementationDAO implementationDAO; @Test - public void find() { + void find() { Report report = reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").orElseThrow(); assertNotNull(report); @@ -51,14 +51,14 @@ public void find() { } @Test - public void findAll() { + void findAll() { List reports = reportDAO.findAll(); assertNotNull(reports); assertEquals(1, reports.size()); } @Test - public void save() { + void save() { int beforeCount = reportDAO.findAll().size(); Report report = entityFactory.newEntity(Report.class); @@ -77,7 +77,7 @@ public void save() { } @Test - public void delete() { + void delete() { Report report = reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").orElseThrow(); assertNotNull(report); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ResourceTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ResourceTest.java index 61b2f4738ba..280ced7ad32 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ResourceTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ResourceTest.java @@ -52,13 +52,13 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class ResourceTest extends AbstractTest { +class ResourceTest extends AbstractTest { @Autowired private ExternalResourceDAO resourceDAO; @Test - public void findById() { + void findById() { ExternalResource resource = resourceDAO.findById("ws-target-resource-1").orElseThrow(); ConnInstance connector = resource.getConnector(); @@ -81,19 +81,19 @@ public void findById() { } @Test - public void propagationActions() { + void propagationActions() { ExternalResource resource = resourceDAO.findById("resource-ldap").orElseThrow(); assertEquals(2, resource.getPropagationActions().size()); } @Test - public void findWithOrgUnit() { + void findWithOrgUnit() { ExternalResource resource = resourceDAO.findById("resource-ldap-orgunit").orElseThrow(); assertNotNull(resource.getOrgUnit()); } @Test - public void findAll() { + void findAll() { List authorities = IdMEntitlement.values().stream(). map(entitlement -> new SyncopeGrantedAuthority(entitlement, SyncopeConstants.ROOT_REALM)). collect(Collectors.toList()); @@ -114,14 +114,14 @@ public void findAll() { } @Test - public void getConnObjectKey() { + void getConnObjectKey() { ExternalResource resource = resourceDAO.findById("ws-target-resource-2").orElseThrow(); assertEquals("fullname", resource.getProvisionByAnyType(AnyTypeKind.USER.name()).orElseThrow(). getMapping().getConnObjectKeyItem().orElseThrow().getIntAttrName()); } @Test - public void save() { + void save() { ExternalResource resource = entityFactory.newEntity(ExternalResource.class); resource.setKey("ws-target-resource-basic-save"); resource.setPropagationPriority(2); @@ -156,7 +156,7 @@ public void save() { } @Test - public void saveInvalidMappingIntAttr() { + void saveInvalidMappingIntAttr() { assertThrows(InvalidEntityException.class, () -> { ExternalResource resource = entityFactory.newEntity(ExternalResource.class); resource.setKey("ws-target-resource-basic-save-invalid"); @@ -182,7 +182,7 @@ public void saveInvalidMappingIntAttr() { } @Test - public void saveInvalidMappingExtAttr() { + void saveInvalidMappingExtAttr() { assertThrows(InvalidEntityException.class, () -> { ExternalResource resource = entityFactory.newEntity(ExternalResource.class); resource.setKey("ws-target-resource-basic-save-invalid"); @@ -212,7 +212,7 @@ public void saveInvalidMappingExtAttr() { } @Test - public void saveInvalidProvision() { + void saveInvalidProvision() { assertThrows(InvalidEntityException.class, () -> { ExternalResource resource = entityFactory.newEntity(ExternalResource.class); resource.setKey("invalidProvision"); @@ -245,7 +245,7 @@ public void saveInvalidProvision() { } @Test - public void saveWithGroupMappingType() { + void saveWithGroupMappingType() { ExternalResource resource = entityFactory.newEntity(ExternalResource.class); resource.setKey("ws-target-resource-basic-save-invalid"); @@ -288,7 +288,7 @@ public void saveWithGroupMappingType() { } @Test - public void delete() { + void delete() { ExternalResource resource = resourceDAO.findById("ws-target-resource-2").orElseThrow(); resourceDAO.deleteById(resource.getKey()); @@ -297,7 +297,7 @@ public void delete() { } @Test - public void issueSYNCOPE418() { + void issueSYNCOPE418() { ExternalResource resource = entityFactory.newEntity(ExternalResource.class); resource.setKey("http://schemas.examples.org/security/authorization/organizationUnit"); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java index 605e475cfbd..1ffa15f475c 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RoleTest.java @@ -35,7 +35,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class RoleTest extends AbstractTest { +class RoleTest extends AbstractTest { @Autowired private RoleDAO roleDAO; @@ -47,7 +47,7 @@ public class RoleTest extends AbstractTest { private RealmSearchDAO realmSearchDAO; @Test - public void find() { + void find() { Role role = roleDAO.findById("User manager").orElseThrow(); assertNotNull(role.getKey()); assertFalse(role.getRealms().isEmpty()); @@ -56,7 +56,7 @@ public void find() { } @Test - public void findAll() { + void findAll() { List list = roleDAO.findAll(); assertNotNull(list); assertFalse(list.isEmpty()); @@ -64,7 +64,7 @@ public void findAll() { } @Test - public void save() { + void save() { Role role = entityFactory.newEntity(Role.class); role.setKey("new"); role.add(realmDAO.getRoot()); @@ -77,7 +77,7 @@ public void save() { } @Test - public void delete() { + void delete() { assertTrue(roleDAO.findById("Other").isPresent()); roleDAO.deleteById("Other"); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2IdPEntityTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2IdPEntityTest.java index 4d6bb9b6d5a..10af65e05c0 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2IdPEntityTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2IdPEntityTest.java @@ -32,7 +32,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class SAML2IdPEntityTest extends AbstractTest { +class SAML2IdPEntityTest extends AbstractTest { @Autowired private SAML2IdPEntityDAO saml2IdPEntityDAO; @@ -53,7 +53,7 @@ private SAML2IdPEntity create(final String owner) { } @Test - public void find() { + void find() { create("Syncope"); assertTrue(saml2IdPEntityDAO.findById("Syncope").isPresent()); @@ -62,12 +62,12 @@ public void find() { } @Test - public void save() { + void save() { create("SyncopeCreate"); } @Test - public void update() { + void update() { SAML2IdPEntity entity = create("SyncopeUpdate"); assertNotNull(entity); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPTest.java index 54b3b4a3044..e4f6eb835ff 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPTest.java @@ -35,13 +35,13 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class SAML2SPTest extends AbstractClientAppTest { +class SAML2SPTest extends AbstractClientAppTest { @Autowired private SAML2SPClientAppDAO saml2spDAO; @Test - public void find() { + void find() { long beforeCount = saml2spDAO.count(); SAML2SPClientApp sp = entityFactory.newEntity(SAML2SPClientApp.class); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SecurityQuestionTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SecurityQuestionTest.java index fee9c5b9596..6b5bcdaac26 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SecurityQuestionTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SecurityQuestionTest.java @@ -31,26 +31,26 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class SecurityQuestionTest extends AbstractTest { +class SecurityQuestionTest extends AbstractTest { @Autowired private SecurityQuestionDAO securityQuestionDAO; @Test - public void find() { + void find() { SecurityQuestion question = securityQuestionDAO.findById("887028ea-66fc-41e7-b397-620d7ea6dfbb").orElseThrow(); assertNotNull(question.getContent()); } @Test - public void findAll() { + void findAll() { List securityQuestions = securityQuestionDAO.findAll(); assertNotNull(securityQuestions); assertFalse(securityQuestions.isEmpty()); } @Test - public void save() { + void save() { SecurityQuestion securityQuestion = entityFactory.newEntity(SecurityQuestion.class); securityQuestion.setContent("What is your favorite pet's name?"); @@ -60,7 +60,7 @@ public void save() { } @Test - public void delete() { + void delete() { assertTrue(securityQuestionDAO.findById("887028ea-66fc-41e7-b397-620d7ea6dfbb").isPresent()); securityQuestionDAO.deleteById("887028ea-66fc-41e7-b397-620d7ea6dfbb"); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskExecTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskExecTest.java index 55a9bc8a109..3fc803f01cd 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskExecTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskExecTest.java @@ -38,7 +38,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class TaskExecTest extends AbstractTest { +class TaskExecTest extends AbstractTest { @Autowired private TaskExecDAO taskExecDAO; @@ -50,7 +50,7 @@ public class TaskExecTest extends AbstractTest { private TaskUtilsFactory taskUtilsFactory; @Test - public void findAll() { + void findAll() { PropagationTask task = (PropagationTask) taskDAO.findById( TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); assertNotNull(task); @@ -63,7 +63,7 @@ public void findAll() { } @Test - public void findLatestStarted() { + void findLatestStarted() { PropagationTask task = (PropagationTask) taskDAO.findById( TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); assertNotNull(task); @@ -73,7 +73,7 @@ public void findLatestStarted() { } @Test - public void issueSYNCOPE214() { + void issueSYNCOPE214() { PropagationTask task = (PropagationTask) taskDAO.findById( TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); assertNotNull(task); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskTest.java index 37b83ca3515..f0355093551 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskTest.java @@ -67,7 +67,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class TaskTest extends AbstractTest { +class TaskTest extends AbstractTest { @Autowired private TaskDAO taskDAO; @@ -82,14 +82,14 @@ public class TaskTest extends AbstractTest { private ImplementationDAO implementationDAO; @Test - public void findByName() { + void findByName() { Optional task = taskDAO.findByName(TaskType.SCHEDULED, "SampleJob Task"); assertTrue(task.isPresent()); assertEquals(taskDAO.findById(TaskType.SCHEDULED, "e95555d2-1b09-42c8-b25b-f4c4ec597979"), task); } @Test - public void findWithoutExecs() { + void findWithoutExecs() { assertEquals(3, taskDAO.countToExec(TaskType.PROPAGATION)); List tasks = taskDAO.findToExec(TaskType.PROPAGATION, Pageable.unpaged()); @@ -98,7 +98,7 @@ public void findWithoutExecs() { } @Test - public void findPaginated() { + void findPaginated() { List tasks = taskDAO.findAll( TaskType.PROPAGATION, null, null, null, null, PageRequest.of(0, 2)); assertNotNull(tasks); @@ -124,7 +124,7 @@ public void findPaginated() { } @Test - public void findAll() { + void findAll() { assertEquals(6, taskDAO.findAll(TaskType.PROPAGATION).size()); assertEquals(1, taskDAO.findAll(TaskType.NOTIFICATION).size()); assertEquals(3, taskDAO.findAll(TaskType.SCHEDULED).size()); @@ -147,7 +147,7 @@ public void findAll() { } @Test - public void savePropagationTask() { + void savePropagationTask() { ExternalResource resource = resourceDAO.findById("ws-target-resource-1").orElseThrow(); PropagationTask task = entityFactory.newEntity(PropagationTask.class); @@ -170,7 +170,7 @@ public void savePropagationTask() { } @Test - public void saveMacroTask() throws Exception { + void saveMacroTask() throws Exception { MacroTask task = entityFactory.newEntity(MacroTask.class); task.setRealm(realmDAO.getRoot()); task.setJobDelegate(implementationDAO.findById("MacroJobDelegate").orElseThrow()); @@ -227,7 +227,7 @@ public void saveMacroTask() throws Exception { } @Test - public void delete() { + void delete() { PropagationTask task = (PropagationTask) taskDAO.findById( TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); assertNotNull(task); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java index bd853dee7aa..9641ef086ac 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java @@ -43,7 +43,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class UserTest extends AbstractTest { +class UserTest extends AbstractTest { @Autowired private PasswordGenerator passwordGenerator; @@ -67,7 +67,7 @@ public class UserTest extends AbstractTest { private EncryptorManager encryptorManager; @Test - public void find() { + void find() { User user = userDAO.findById("823074dc-d280-436d-a7dd-07399fae48ec").orElseThrow(); assertEquals("puccini", user.getUsername()); assertFalse(user.isSuspended()); @@ -83,12 +83,12 @@ public void find() { } @Test - public void findUsername() { + void findUsername() { assertEquals("puccini", userDAO.findUsername("823074dc-d280-436d-a7dd-07399fae48ec").orElseThrow()); } @Test - public void findByToken() { + void findByToken() { assertTrue(userDAO.findByToken("WRONG TOKEN").isEmpty()); User user = userDAO.findById("823074dc-d280-436d-a7dd-07399fae48ec").orElseThrow(); @@ -105,32 +105,32 @@ public void findByToken() { } @Test - public void findAll() { + void findAll() { List users = userDAO.findAll(); assertEquals(5, users.size()); } @Test - public void count() { + void count() { long count = userDAO.count(); assertNotNull(count); assertEquals(5, count); } @Test - public void findByKey() { + void findByKey() { assertTrue(userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").isPresent()); } @Test - public void findByUsername() { + void findByUsername() { assertTrue(userDAO.findByUsername("rossini").isPresent()); assertTrue(userDAO.findByUsername("vivaldi").isPresent()); assertTrue(userDAO.findByUsername("user6").isEmpty()); } @Test - public void save() { + void save() { User user = entityFactory.newEntity(User.class); user.setUsername("username"); user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); @@ -144,7 +144,7 @@ public void save() { } @Test - public void delete() { + void delete() { User user = userDAO.findById("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee").orElseThrow(); userDAO.deleteById(user.getKey()); @@ -153,7 +153,7 @@ public void delete() { } @Test - public void issue237() { + void issue237() { User user = entityFactory.newEntity(User.class); user.setUsername("username"); user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); @@ -168,7 +168,7 @@ public void issue237() { } @Test - public void issueSYNCOPE391() { + void issueSYNCOPE391() { User user = entityFactory.newEntity(User.class); user.setUsername("username"); user.setCipherAlgorithm(CipherAlgorithm.AES); @@ -181,7 +181,7 @@ public void issueSYNCOPE391() { } @Test - public void testPasswordGenerator() { + void testPasswordGenerator() { String password = passwordGenerator.generate( resourceDAO.findById("ws-target-resource-nopropagation").orElseThrow(), List.of(realmDAO.getRoot())); @@ -193,7 +193,7 @@ public void testPasswordGenerator() { } @Test - public void passwordGeneratorFailing() { + void passwordGeneratorFailing() { assertThrows(IllegalArgumentException.class, () -> { String password = passwordGenerator.generate( resourceDAO.findById("ws-target-resource-nopropagation").orElseThrow(), @@ -209,7 +209,7 @@ public void passwordGeneratorFailing() { } @Test - public void issueSYNCOPE1666() { + void issueSYNCOPE1666() { User user = entityFactory.newEntity(User.class); user.setUsername("username"); user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); @@ -229,7 +229,7 @@ public void issueSYNCOPE1666() { } @Test - public void issueSYNCOPE1937() throws Exception { + void issueSYNCOPE1937() throws Exception { User user = entityFactory.newEntity(User.class); user.setUsername("username"); user.setRealm(realmDAO.getRoot()); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/WAConfigTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/WAConfigTest.java index caaa93fead0..aed7281518b 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/WAConfigTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/WAConfigTest.java @@ -36,13 +36,13 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class WAConfigTest extends AbstractTest { +class WAConfigTest extends AbstractTest { @Autowired private WAConfigDAO configDAO; @BeforeEach - public void beforeEach() { + void beforeEach() { entityManager.createQuery("DELETE FROM " + JPAWAConfigEntry.class.getSimpleName()).executeUpdate(); } @@ -58,32 +58,32 @@ private WAConfigEntry create(final String name, final List value) { } @Test - public void saveCommaSeparatedValueStrings() { + void saveCommaSeparatedValueStrings() { create("system.example.key[0]", Arrays.asList("value1", "value2", "value3")); assertFalse(configDAO.findAll().isEmpty()); } @Test - public void saveNumbers() { + void saveNumbers() { create("system.example.key[0]", List.of("1984")); assertFalse(configDAO.findAll().isEmpty()); } @Test - public void saveCollection() { + void saveCollection() { WAConfigEntry entry = create("system.example.key[0]", new ArrayList<>(Arrays.asList("1", "2"))); assertNotNull(entry.getValues()); assertFalse(configDAO.findAll().isEmpty()); } @Test - public void saveList() { + void saveList() { create("system.example.key[0].key1", List.of("value1")); assertFalse(configDAO.findAll().isEmpty()); } @Test - public void update() { + void update() { WAConfigEntry entry = create("system.syncope.key[0]", Arrays.asList("1", "2", "3", "4")); assertNotNull(entry); entry.setValues(List.of("v1")); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java index c87034b9dac..65063c00a83 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java @@ -56,7 +56,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AnySearchTest extends AbstractTest { +class AnySearchTest extends AbstractTest { @Autowired private UserDAO userDAO; @@ -80,7 +80,7 @@ public class AnySearchTest extends AbstractTest { private PlainAttrValidationManager validator; @Test - public void searchAsGroupOwner() { + void searchAsGroupOwner() { // 1. define rossini as member of director User rossini = userDAO.findByUsername("rossini").orElseThrow(); @@ -119,7 +119,7 @@ public void searchAsGroupOwner() { } @Test - public void searchByMembershipAttribute() { + void searchByMembershipAttribute() { AttrCond attrCond = new AttrCond(AttrCond.Type.EQ); attrCond.setSchema("ctype"); attrCond.setExpression("otherchildctype"); @@ -150,7 +150,7 @@ public void searchByMembershipAttribute() { } @Test - public void issueSYNCOPE95() { + void issueSYNCOPE95() { groupDAO.findAll().forEach(group -> groupDAO.deleteById(group.getKey())); entityManager.flush(); @@ -169,7 +169,7 @@ public void issueSYNCOPE95() { } @Test - public void issueSYNCOPE800() { + void issueSYNCOPE800() { // create derived attribute (literal as prefix) DerSchema prefix = entityFactory.newEntity(DerSchema.class); prefix.setKey("kprefix"); @@ -206,7 +206,7 @@ public void issueSYNCOPE800() { } @Test - public void issueSYNCOPE1417() { + void issueSYNCOPE1417() { AnyCond usernameLeafCond = new AnyCond(AnyCond.Type.EQ); usernameLeafCond.setSchema("username"); usernameLeafCond.setExpression("rossini"); @@ -229,7 +229,7 @@ public void issueSYNCOPE1417() { } @Test - public void issueSYNCOPE1512() { + void issueSYNCOPE1512() { Group group = groupDAO.findByName("root").orElseThrow(); // non unique @@ -266,7 +266,7 @@ public void issueSYNCOPE1512() { } @Test - public void issueSYNCOPE1790() { + void issueSYNCOPE1790() { // 0. search by email AttrCond emailCond = new AttrCond(AttrCond.Type.EQ); emailCond.setSchema("email"); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnyTypeClassTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnyTypeClassTest.java index b8899dbe0bb..53c7e2c6b6a 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnyTypeClassTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnyTypeClassTest.java @@ -33,7 +33,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AnyTypeClassTest extends AbstractTest { +class AnyTypeClassTest extends AbstractTest { @Autowired private PlainSchemaDAO plainSchemaDAO; @@ -42,7 +42,7 @@ public class AnyTypeClassTest extends AbstractTest { private AnyTypeClassDAO anyTypeClassDAO; @Test - public void create() { + void create() { PlainSchema newSchema = entityFactory.newEntity(PlainSchema.class); newSchema.setKey("new_plain_schema"); newSchema.setType(AttrSchemaType.String); @@ -71,7 +71,7 @@ public void create() { } @Test - public void delete() { + void delete() { AnyTypeClass minimalUser = anyTypeClassDAO.findById("minimal user").orElseThrow(); PlainSchema surname = plainSchemaDAO.findById("surname").orElseThrow(); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnyTypeTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnyTypeTest.java index 813fc9fbd1c..801217394ff 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnyTypeTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnyTypeTest.java @@ -33,7 +33,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AnyTypeTest extends AbstractTest { +class AnyTypeTest extends AbstractTest { @Autowired private AnyTypeDAO anyTypeDAO; @@ -42,7 +42,7 @@ public class AnyTypeTest extends AbstractTest { private AnyTypeClassDAO anyTypeClassDAO; @Test - public void findByClassesContaining() { + void findByClassesContaining() { List found = anyTypeDAO.findByClassesContaining( anyTypeClassDAO.findById("minimal user").orElseThrow()); assertEquals(List.of(anyTypeDAO.getUser()), found); @@ -58,7 +58,7 @@ public void findByClassesContaining() { } @Test - public void manyToMany() { + void manyToMany() { AnyTypeClass other = anyTypeClassDAO.findById("other").orElseThrow(); AnyType user = anyTypeDAO.getUser(); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConnInstanceTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConnInstanceTest.java index 59b85e2e180..36aae09d3f8 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConnInstanceTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ConnInstanceTest.java @@ -35,7 +35,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class ConnInstanceTest extends AbstractTest { +class ConnInstanceTest extends AbstractTest { @Autowired private ExternalResourceDAO resourceDAO; @@ -44,7 +44,7 @@ public class ConnInstanceTest extends AbstractTest { private ConnInstanceDAO connInstanceDAO; @Test - public void deleteCascade() { + void deleteCascade() { ConnInstance connInstance = connInstanceDAO.findById("fcf9f2b0-f7d6-42c9-84a6-61b28255a42b").orElseThrow(); List resources = resourceDAO.findByConnInstance(connInstance.getKey()); @@ -60,7 +60,7 @@ public void deleteCascade() { } @Test - public void issue176() { + void issue176() { ConnInstance connInstance = connInstanceDAO.findById("fcf9f2b0-f7d6-42c9-84a6-61b28255a42b").orElseThrow(); assertTrue(connInstance.getCapabilities().isEmpty()); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DelegationTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DelegationTest.java index 9b3e345d38b..b27077329a4 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DelegationTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DelegationTest.java @@ -31,7 +31,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class DelegationTest extends AbstractTest { +class DelegationTest extends AbstractTest { @Autowired private DelegationDAO delegationDAO; @@ -40,7 +40,7 @@ public class DelegationTest extends AbstractTest { private UserDAO userDAO; @Test - public void findValidFor() { + void findValidFor() { Delegation delegation = entityFactory.newEntity(Delegation.class); delegation.setDelegating(userDAO.findByUsername("bellini").orElseThrow()); delegation.setDelegated(userDAO.findByUsername("rossini").orElseThrow()); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/FIQLQueryTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/FIQLQueryTest.java index a478b1b6bfa..086833d73e7 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/FIQLQueryTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/FIQLQueryTest.java @@ -30,7 +30,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class FIQLQueryTest extends AbstractTest { +class FIQLQueryTest extends AbstractTest { @Autowired private FIQLQueryDAO fiqlQueryDAO; @@ -39,7 +39,7 @@ public class FIQLQueryTest extends AbstractTest { private UserDAO userDAO; @Test - public void findByOwner() { + void findByOwner() { FIQLQuery query = entityFactory.newEntity(FIQLQuery.class); query.setOwner(userDAO.findByUsername("rossini").orElseThrow()); query.setName("name"); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/GroupTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/GroupTest.java index 306ed2b0c23..ea74f2405f1 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/GroupTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/GroupTest.java @@ -56,7 +56,7 @@ @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS) @Transactional -public class GroupTest extends AbstractTest { +class GroupTest extends AbstractTest { @Autowired private AnyTypeDAO anyTypeDAO; @@ -92,7 +92,7 @@ public class GroupTest extends AbstractTest { private PlainAttrValidationManager validator; @Test - public void findByResourcesContaining() { + void findByResourcesContaining() { List found = groupDAO.findByResourcesContaining(resourceDAO.findById("resource-csv").orElseThrow()); assertEquals(2, found.size()); assertTrue(found.contains(groupDAO.findById("0626100b-a4ba-4e00-9971-86fad52a6216").orElseThrow())); @@ -103,7 +103,7 @@ public void findByResourcesContaining() { } @Test - public void uMemberships() { + void uMemberships() { List memberships = groupDAO.findUMemberships( groupDAO.findById("37d15e4c-cdc1-460b-a591-8505c8133806").orElseThrow(), Pageable.unpaged()); assertEquals(2, memberships.size()); @@ -127,7 +127,7 @@ public void uMemberships() { } @Test - public void saveWithTwoManagers() { + void saveWithTwoManagers() { assertThrows(InvalidEntityException.class, () -> { Group root = groupDAO.findByName("root").orElseThrow(); @@ -144,7 +144,7 @@ public void saveWithTwoManagers() { } @Test - public void create() { + void create() { Group group = entityFactory.newEntity(Group.class); group.setRealm(realmDAO.getRoot()); group.setName("new"); @@ -167,7 +167,7 @@ public void create() { } @Test - public void createWithInternationalCharacters() { + void createWithInternationalCharacters() { Group group = entityFactory.newEntity(Group.class); group.setName("räksmörgås"); group.setRealm(realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); @@ -177,7 +177,7 @@ public void createWithInternationalCharacters() { } @Test - public void delete() { + void delete() { Collection groups = userDAO.findAllGroups(userDAO.findByUsername("verdi").orElseThrow()); assertTrue(groups.stream().anyMatch(g -> "b1f7c12d-ec83-441f-a50e-1691daaedf3b".equals(g.getKey()))); int before = userDAO.findAllGroups(userDAO.findByUsername("verdi").orElseThrow()).size(); @@ -192,7 +192,7 @@ public void delete() { } @Test - public void relationships() { + void relationships() { RelationshipType groupType = entityFactory.newEntity(RelationshipType.class); groupType.setKey("group type"); groupType.setLeftEndAnyType(anyTypeDAO.getGroup()); @@ -221,7 +221,7 @@ public void relationships() { } @Test - public void issueSYNCOPE1512() { + void issueSYNCOPE1512() { Group group = groupDAO.findByName("root").orElseThrow(); // non unique diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainSchemaTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainSchemaTest.java index 59194dd28d8..648bc584402 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainSchemaTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainSchemaTest.java @@ -55,7 +55,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class PlainSchemaTest extends AbstractTest { +class PlainSchemaTest extends AbstractTest { @Autowired private UserDAO userDAO; @@ -92,7 +92,7 @@ public static void unsetAuthContext() { } @Test - public void findByAnyTypeClasses() { + void findByAnyTypeClasses() { List found = plainSchemaDAO.findByAnyTypeClasses( List.of(anyTypeClassDAO.findById("minimal user").orElseThrow())); assertEquals(5, found.size()); @@ -103,7 +103,7 @@ public void findByAnyTypeClasses() { } @Test - public void checkIdUniqueness() { + void checkIdUniqueness() { assertNotNull(derSchemaDAO.findById("cn").orElseThrow()); PlainSchema schema = entityFactory.newEntity(PlainSchema.class); @@ -130,7 +130,7 @@ private List getMappingItems(final String intAttrName) { } @Test - public void deleteFullname() { + void deleteFullname() { // fullname is mapped as ConnObjectKey for various resources, need to swap it otherwise validation errors // will be raised resourceDAO.findAll().stream(). @@ -165,7 +165,7 @@ public void deleteFullname() { } @Test - public void deleteSurname() { + void deleteSurname() { // search for user schema surname PlainSchema schema = plainSchemaDAO.findById("surname").orElseThrow(); @@ -186,7 +186,7 @@ public void deleteSurname() { } @Test - public void deleteFirstname() { + void deleteFirstname() { int pre = resourceDAO.findById("resource-db-pull").orElseThrow(). getProvisionByAnyType(AnyTypeKind.USER.name()).get().getMapping().getItems().size(); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PolicyTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PolicyTest.java index 34048d1b02a..869db712b30 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PolicyTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PolicyTest.java @@ -38,7 +38,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class PolicyTest extends AbstractClientAppTest { +class PolicyTest extends AbstractClientAppTest { @Autowired private OIDCRPClientAppDAO oidcRelyingPartyDAO; @@ -50,7 +50,7 @@ public class PolicyTest extends AbstractClientAppTest { private RealmSearchDAO realmSearchDAO; @Test - public void authPolicyCanBeNull() { + void authPolicyCanBeNull() { Realm realm = realmSearchDAO.findByFullPath("/odd").orElseThrow(); // Create new client app and assign policy @@ -69,7 +69,7 @@ public void authPolicyCanBeNull() { } @Test - public void removePolicyFromRealm() { + void removePolicyFromRealm() { AuthPolicy authPolicy = buildAndSaveAuthPolicy(); AccessPolicy accessPolicy = buildAndSaveAccessPolicy(); AttrReleasePolicy attrPolicy = buildAndSaveAttrRelPolicy(); @@ -100,7 +100,7 @@ public void removePolicyFromRealm() { } @Test - public void removePolicyFromApps() { + void removePolicyFromApps() { // Create new policy AccessPolicy accessPolicy = buildAndSaveAccessPolicy(); AuthPolicy authPolicy = buildAndSaveAuthPolicy(); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RealmSearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RealmSearchTest.java index 8a904ed4599..23288031ba9 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RealmSearchTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RealmSearchTest.java @@ -49,7 +49,7 @@ @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) @Transactional -public class RealmSearchTest extends AbstractTest { +class RealmSearchTest extends AbstractTest { @Autowired private AnyTypeClassDAO anyTypeClassDAO; @@ -107,7 +107,7 @@ void prepare() { } @Test - public void byName() { + void byName() { AnyCond name = new AnyCond(AttrCond.Type.EQ); name.setSchema("name"); name.setExpression("two"); @@ -121,7 +121,7 @@ public void byName() { } @Test - public void byAttr() { + void byAttr() { AttrCond attrEq = new AttrCond(AttrCond.Type.EQ); attrEq.setSchema("aLong"); attrEq.setExpression("42"); @@ -176,7 +176,7 @@ public void byAttr() { } @Test - public void byNameAndAttr() { + void byNameAndAttr() { AnyCond name = new AnyCond(AttrCond.Type.EQ); name.setSchema("name"); name.setExpression("two"); @@ -195,7 +195,7 @@ public void byNameAndAttr() { } @Test - public void and() { + void and() { AttrCond cond1 = new AttrCond(AttrCond.Type.EQ); cond1.setSchema("aLong"); cond1.setExpression("42"); @@ -210,7 +210,7 @@ public void and() { } @Test - public void auxClass() { + void auxClass() { AuxClassCond auxClassCond = new AuxClassCond(); auxClassCond.setAuxClass("other"); @@ -224,7 +224,7 @@ public void auxClass() { } @Test - public void resource() { + void resource() { ResourceCond resourceCond = new ResourceCond(); resourceCond.setResource("resource-ldap-orgunit"); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RealmTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RealmTest.java index ad9bb45f728..98073e3a619 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RealmTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RealmTest.java @@ -37,7 +37,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class RealmTest extends AbstractTest { +class RealmTest extends AbstractTest { @Autowired private AnyTypeClassDAO anyTypeClassDAO; @@ -58,7 +58,7 @@ public class RealmTest extends AbstractTest { private PlainAttrValidationManager validator; @Test - public void plainAttrs() { + void plainAttrs() { Realm realm = realmSearchDAO.findByFullPath("/odd").orElseThrow(); assertTrue(realm.getAnyTypeClasses().isEmpty()); assertTrue(realm.getPlainAttrs().isEmpty()); @@ -85,7 +85,7 @@ public void plainAttrs() { } @Test - public void delete() { + void delete() { Realm realm = realmSearchDAO.findByFullPath("/odd").orElseThrow(); // need to remove this group in order to remove the realm, which is otherwise empty diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ReportTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ReportTest.java index 69f36a87364..d898c7ccce1 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ReportTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ReportTest.java @@ -38,7 +38,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class ReportTest extends AbstractTest { +class ReportTest extends AbstractTest { @Autowired private ReportDAO reportDAO; @@ -50,7 +50,7 @@ public class ReportTest extends AbstractTest { private ImplementationDAO implementationDAO; @Test - public void find() { + void find() { Report report = reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").orElseThrow(); assertNotNull(report.getExecs()); @@ -59,7 +59,7 @@ public void find() { } @Test - public void saveWithExistingName() { + void saveWithExistingName() { assertThrows(EntityExistsException.class, () -> { Report report = reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").orElseThrow(); @@ -78,7 +78,7 @@ public void saveWithExistingName() { } @Test - public void save() { + void save() { Report report = reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").orElseThrow(); assertEquals(1, report.getExecs().size()); @@ -99,7 +99,7 @@ public void save() { } @Test - public void deleteReport() { + void deleteReport() { reportDAO.deleteById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b"); entityManager.flush(); @@ -109,7 +109,7 @@ public void deleteReport() { } @Test - public void deleteReportExecution() { + void deleteReportExecution() { ReportExec execution = reportExecDAO.findById("c13f39c5-0d35-4bff-ba79-3cd5de940369").orElseThrow(); int executionNumber = execution.getReport().getExecs().size(); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceTest.java index b47cbceaf2c..25de41f4034 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceTest.java @@ -55,7 +55,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class ResourceTest extends AbstractTest { +class ResourceTest extends AbstractTest { @Autowired private ExternalResourceDAO resourceDAO; @@ -76,7 +76,7 @@ public class ResourceTest extends AbstractTest { private ImplementationDAO implementationDAO; @Test - public void accountPolicy() { + void accountPolicy() { ExternalResource resource = resourceDAO.findById("resource-testdb").orElseThrow(); assertEquals( policyDAO.findById("20ab5a8c-4b0c-432c-b957-f7fb9784d9f7").orElseThrow(), @@ -84,14 +84,14 @@ public void accountPolicy() { } @Test - public void findByConnInstance() { + void findByConnInstance() { List resources = resourceDAO.findByConnInstance("88a7a819-dab5-46b4-9b90-0b9769eabdb8"); assertEquals(6, resources.size()); assertTrue(resources.contains(resourceDAO.findById("ws-target-resource-1").orElseThrow())); } @Test - public void findByProvisionSorter() { + void findByProvisionSorter() { Implementation impl = entityFactory.newEntity(Implementation.class); impl.setType(IdMImplementationType.PROVISION_SORTER); impl.setEngine(ImplementationEngine.GROOVY); @@ -112,7 +112,7 @@ public void findByProvisionSorter() { } @Test - public void findByPropagationActionsContaining() { + void findByPropagationActionsContaining() { Implementation impl = implementationDAO.findById("GenerateRandomPasswordPropagationActions").orElseThrow(); assertEquals( @@ -122,7 +122,7 @@ public void findByPropagationActionsContaining() { } @Test - public void createWithPasswordPolicy() { + void createWithPasswordPolicy() { final String resourceName = "resourceWithPasswordPolicy"; PasswordPolicy policy = policyDAO.findById("986d1236-3ac5-4a19-810c-5ab21d79cba1", PasswordPolicy.class). @@ -149,7 +149,7 @@ public void createWithPasswordPolicy() { } @Test - public void save() { + void save() { ExternalResource resource = entityFactory.newEntity(ExternalResource.class); resource.setKey("ws-target-resource-save"); @@ -228,7 +228,7 @@ public void save() { } @Test - public void delete() { + void delete() { ExternalResource resource = resourceDAO.findById("resource-testdb").orElseThrow(); // ------------------------------------- @@ -278,7 +278,7 @@ public void delete() { } @Test - public void issue243() { + void issue243() { ExternalResource csv = resourceDAO.findById("resource-csv").orElseThrow(); int origMapItems = csv.getProvisionByAnyType( diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RoleTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RoleTest.java index 2b4fed49610..77a8d9439fb 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RoleTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/RoleTest.java @@ -39,7 +39,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class RoleTest extends AbstractTest { +class RoleTest extends AbstractTest { @Autowired private RoleDAO roleDAO; @@ -57,7 +57,7 @@ public class RoleTest extends AbstractTest { private DelegationDAO delegationDAO; @Test - public void delete() { + void delete() { // 0. create role Role role = entityFactory.newEntity(Role.class); role.setKey("new"); @@ -89,7 +89,7 @@ public void delete() { } @Test - public void deleteCascadeOnDelegations() { + void deleteCascadeOnDelegations() { User bellini = userDAO.findByUsername("bellini").orElseThrow(); User rossini = userDAO.findByUsername("rossini").orElseThrow(); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/SecurityQuestionTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/SecurityQuestionTest.java index a7fef9a9af3..a82ab61114e 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/SecurityQuestionTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/SecurityQuestionTest.java @@ -29,7 +29,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class SecurityQuestionTest extends AbstractTest { +class SecurityQuestionTest extends AbstractTest { @Autowired private SecurityQuestionDAO securityQuestionDAO; @@ -38,7 +38,7 @@ public class SecurityQuestionTest extends AbstractTest { private UserDAO userDAO; @Test - public void test() { + void test() { User user = userDAO.findByUsername("bellini").orElseThrow(); assertNull(user.getSecurityQuestion()); assertNull(user.getSecurityAnswer()); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java index 0ca3e73584d..cf10cdeb6ad 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java @@ -70,7 +70,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class TaskTest extends AbstractTest { +class TaskTest extends AbstractTest { @Autowired private TaskDAO taskDAO; @@ -91,7 +91,7 @@ public class TaskTest extends AbstractTest { private TaskUtilsFactory taskUtilsFactory; @Test - public void read() { + void read() { Task task = taskDAO.findById(TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); assertNotNull(task); @@ -101,7 +101,7 @@ public void read() { } @Test - public void readMultipleOrderBy() { + void readMultipleOrderBy() { List orderByClauses = new ArrayList<>(); orderByClauses.add(new Sort.Order(Sort.DEFAULT_DIRECTION, "start")); orderByClauses.add(new Sort.Order(Sort.DEFAULT_DIRECTION, "latestExecStatus")); @@ -112,7 +112,7 @@ public void readMultipleOrderBy() { } @Test - public void savePropagationTask() { + void savePropagationTask() { ExternalResource resource = resourceDAO.findById("ws-target-resource-1").orElseThrow(); PropagationTask task = entityFactory.newEntity(PropagationTask.class); @@ -141,7 +141,7 @@ public void savePropagationTask() { } @Test - public void addPropagationTaskExec() { + void addPropagationTaskExec() { PropagationTask task = (PropagationTask) taskDAO.findById( TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); @@ -163,7 +163,7 @@ public void addPropagationTaskExec() { } @Test - public void addPullTaskExec() { + void addPullTaskExec() { PullTask task = (PullTask) taskDAO.findById( TaskType.PULL, "c41b9b71-9bfa-4f90-89f2-84787def4c5c").orElseThrow(); @@ -186,7 +186,7 @@ public void addPullTaskExec() { } @Test - public void addPushTaskExec() { + void addPushTaskExec() { PushTask task = (PushTask) taskDAO.findById( TaskType.PUSH, "af558be4-9d2f-4359-bf85-a554e6e90be1").orElseThrow(); @@ -208,7 +208,7 @@ public void addPushTaskExec() { } @Test - public void deleteTask() { + void deleteTask() { taskDAO.delete(TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c"); entityManager.flush(); @@ -218,7 +218,7 @@ public void deleteTask() { } @Test - public void deleteTaskExec() { + void deleteTaskExec() { @SuppressWarnings("unchecked") TaskExec execution = (TaskExec) taskExecDAO.findById( TaskType.PROPAGATION, "e58ca1c7-178a-4012-8a71-8aa14eaf0655").orElseThrow(); @@ -236,7 +236,7 @@ public void deleteTaskExec() { } @Test - public void savePullTask() { + void savePullTask() { PullTask task = entityFactory.newEntity(PullTask.class); task.setName("savePullTask"); task.setDescription("PullTask description"); @@ -286,7 +286,7 @@ public void savePullTask() { } @Test - public void saveLiveSyncTask() { + void saveLiveSyncTask() { LiveSyncTask task = entityFactory.newEntity(LiveSyncTask.class); task.setName("saveLiveSyncTask"); task.setDescription("LiveSyncTask description"); @@ -308,7 +308,7 @@ public void saveLiveSyncTask() { } @Test - public void saveMacroTaskSameCommandMultipleOccurrences() { + void saveMacroTaskSameCommandMultipleOccurrences() { MacroTask task = entityFactory.newEntity(MacroTask.class); task.setRealm(realmDAO.getRoot()); task.setJobDelegate(implementationDAO.findById("MacroJobDelegate").orElseThrow()); @@ -358,7 +358,7 @@ public void saveMacroTaskSameCommandMultipleOccurrences() { } @Test - public void issueSYNCOPE144() { + void issueSYNCOPE144() { ExternalResource resource = resourceDAO.findById("ws-target-resource-1").orElseThrow(); assertNotNull(resource); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java index a9764f8780d..9bdefd5eb5b 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java @@ -64,7 +64,7 @@ @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS) @Transactional -public class UserTest extends AbstractTest { +class UserTest extends AbstractTest { @Autowired private RelationshipTypeDAO relationshipTypeDAO; @@ -106,7 +106,7 @@ public class UserTest extends AbstractTest { private PlainAttrValidationManager validator; @Test - public void delete() { + void delete() { List memberships = groupDAO.findUMemberships( groupDAO.findByName("managingDirector").orElseThrow(), Pageable.unpaged()); assertFalse(memberships.isEmpty()); @@ -124,7 +124,7 @@ public void delete() { } @Test - public void ships() { + void ships() { User user = userDAO.findByUsername("bellini").orElseThrow(); assertEquals(1, user.getMemberships().size()); assertEquals("bf825fe1-7320-4a54-bd64-143b5c18ab97", user.getMemberships().getFirst().getRightEnd().getKey()); @@ -198,7 +198,7 @@ private LinkedAccount newLinkedAccount(final String connObjectKeyValue) { } @Test - public void findLinkedAccount() { + void findLinkedAccount() { LinkedAccount account = newLinkedAccount("findLinkedAccount"); assertNotNull(account.getKey()); assertEquals(1, account.getPlainAttrs().size()); @@ -218,7 +218,7 @@ public void findLinkedAccount() { } @Test - public void deleteLinkedAccountResourceCascade() { + void deleteLinkedAccountResourceCascade() { LinkedAccount account = newLinkedAccount("deleteLinkedAccountResourceCascade"); assertNotNull(account.getKey()); @@ -232,7 +232,7 @@ public void deleteLinkedAccountResourceCascade() { } @Test - public void deleteCascadeOnDelegations() { + void deleteCascadeOnDelegations() { User bellini = userDAO.findByUsername("bellini").orElseThrow(); User rossini = userDAO.findByUsername("rossini").orElseThrow(); @@ -260,7 +260,7 @@ public void deleteCascadeOnDelegations() { } @Test - public void auxClasses() { + void auxClasses() { User user = entityFactory.newEntity(User.class); user.setRealm(realmDAO.getRoot()); @@ -287,7 +287,7 @@ public void auxClasses() { * Search by derived attribute. */ @Test - public void issueSYNCOPE800() { + void issueSYNCOPE800() { // create derived attribute (literal as prefix) DerSchema prefix = entityFactory.newEntity(DerSchema.class); prefix.setKey("kprefix"); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/XMLContentExporterTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/XMLContentExporterTest.java index 0c2711140d9..6270afe77f0 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/XMLContentExporterTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/XMLContentExporterTest.java @@ -33,7 +33,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class XMLContentExporterTest extends AbstractTest { +class XMLContentExporterTest extends AbstractTest { @Autowired private ContentExporter exporter; @@ -53,7 +53,7 @@ private static void checkRealms(final String exported) { * @throws Exception exception thrown when dealing with IO. */ @Test - public void issueSYNCOPE1128() throws Exception { + void issueSYNCOPE1128() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); exporter.export(SyncopeConstants.MASTER_DOMAIN, 100, baos); @@ -65,7 +65,7 @@ public void issueSYNCOPE1128() throws Exception { } @Test - public void exportElements() throws Exception { + void exportElements() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); exporter.export(SyncopeConstants.MASTER_DOMAIN, 100, baos, "AccountPolicy", "Realm"); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AccessTokenTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AccessTokenTest.java index 8d52db20c97..3858e808555 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AccessTokenTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AccessTokenTest.java @@ -38,7 +38,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AccessTokenTest extends AbstractTest { +class AccessTokenTest extends AbstractTest { @Autowired private AccessTokenDAO accessTokenDAO; @@ -54,7 +54,7 @@ private void create(final String key, final long minusMinutes) { } @Test - public void findAll() { + void findAll() { for (long i = 0; i < 5; i++) { create(String.valueOf(6 - i), i); } @@ -90,7 +90,7 @@ public void findAll() { } @Test - public void crud() { + void crud() { AccessToken accessToken = entityFactory.newEntity(AccessToken.class); accessToken.setKey(UUID.randomUUID().toString()); accessToken.setBody("pointless body"); @@ -112,7 +112,7 @@ public void crud() { } @Test - public void unique() { + void unique() { AccessToken accessToken = entityFactory.newEntity(AccessToken.class); accessToken.setKey(UUID.randomUUID().toString()); accessToken.setBody("pointless body"); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyMatchTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyMatchTest.java index a3e32138ea0..8a65a5186e4 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyMatchTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyMatchTest.java @@ -42,7 +42,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AnyMatchTest extends AbstractTest { +class AnyMatchTest extends AbstractTest { @Autowired private UserDAO userDAO; @@ -57,7 +57,7 @@ public class AnyMatchTest extends AbstractTest { private AnyMatchDAO anyMatcher; @Test - public void byResourceCond() { + void byResourceCond() { User user = userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").orElseThrow(); ResourceCond resourceCond = new ResourceCond(); @@ -69,7 +69,7 @@ public void byResourceCond() { } @Test - public void anyObjectMatch() { + void anyObjectMatch() { AnyObject anyObject = anyObjectDAO.findById("fc6dbc3a-6c07-4965-8781-921e7401a4a5").orElseThrow(); RelationshipCond relationshipCond = new RelationshipCond(); @@ -82,7 +82,7 @@ public void anyObjectMatch() { } @Test - public void userMatch() { + void userMatch() { User user = userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").orElseThrow(); MembershipCond groupCond = new MembershipCond(); @@ -108,7 +108,7 @@ public void userMatch() { } @Test - public void groupMatch() { + void groupMatch() { Group group = groupDAO.findById("37d15e4c-cdc1-460b-a591-8505c8133806").orElseThrow(); AnyCond anyCond = new AnyCond(); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyObjectTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyObjectTest.java index 67efd74cac5..15c740daadc 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyObjectTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyObjectTest.java @@ -38,7 +38,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AnyObjectTest extends AbstractTest { +class AnyObjectTest extends AbstractTest { @Autowired private AnyTypeDAO anyTypeDAO; @@ -50,13 +50,13 @@ public class AnyObjectTest extends AbstractTest { private RealmSearchDAO realmSearchDAO; @Test - public void findAll() { + void findAll() { List anyObjects = anyObjectDAO.findAll(); assertEquals(3, anyObjects.size()); } @Test - public void find() { + void find() { AnyObject anyObject = anyObjectDAO.findById("8559d14d-58c2-46eb-a2d4-a7d35161e8f8").orElseThrow(); assertNotNull(anyObject.getType()); assertFalse(anyObject.getType().getClasses().isEmpty()); @@ -69,7 +69,7 @@ public void find() { } @Test - public void findByName() { + void findByName() { AnyObject anyObject = anyObjectDAO.findByName("PRINTER", "HP LJ 1300n").orElseThrow(); assertNotNull(anyObject); assertEquals("fc6dbc3a-6c07-4965-8781-921e7401a4a5", anyObject.getKey()); @@ -78,7 +78,7 @@ public void findByName() { } @Test - public void findKey() { + void findKey() { assertEquals( "fc6dbc3a-6c07-4965-8781-921e7401a4a5", anyObjectDAO.findKey("PRINTER", "HP LJ 1300n").orElseThrow()); @@ -87,7 +87,7 @@ public void findKey() { } @Test - public void findByKeys() { + void findByKeys() { List found = anyObjectDAO.findByKeys(List.of( "8559d14d-58c2-46eb-a2d4-a7d35161e8f8", "9e1d130c-d6a3-48b1-98b3-182477ed0688", "none")); assertEquals(2, found.size()); @@ -96,7 +96,7 @@ public void findByKeys() { } @Test - public void countByType() { + void countByType() { Map byType = anyObjectDAO.countByType(); assertEquals(1, byType.size()); Long count = byType.get("PRINTER"); @@ -104,7 +104,7 @@ public void countByType() { } @Test - public void countByRealm() { + void countByRealm() { Map byRealm = anyObjectDAO.countByRealm("PRINTER"); assertEquals(2, byRealm.size()); Long count = byRealm.get(SyncopeConstants.ROOT_REALM); @@ -112,7 +112,7 @@ public void countByRealm() { } @Test - public void save() { + void save() { AnyObject anyObject = entityFactory.newEntity(AnyObject.class); anyObject.setName("a name"); anyObject.setType(anyTypeDAO.findById("PRINTER").orElseThrow()); @@ -123,7 +123,7 @@ public void save() { } @Test - public void deleteAttr() { + void deleteAttr() { AnyObject anyObject = anyObjectDAO.findById("fc6dbc3a-6c07-4965-8781-921e7401a4a5").orElseThrow(); PlainAttr attr = anyObject.getPlainAttr("location").orElseThrow(); anyObject.remove(attr); @@ -135,7 +135,7 @@ public void deleteAttr() { } @Test - public void delete() { + void delete() { AnyObject anyObject = anyObjectDAO.findById("8559d14d-58c2-46eb-a2d4-a7d35161e8f8").orElseThrow(); anyObjectDAO.deleteById(anyObject.getKey()); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnySearchTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnySearchTest.java index 28da4d06c3c..a1f18c10af0 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnySearchTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnySearchTest.java @@ -79,7 +79,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AnySearchTest extends AbstractTest { +class AnySearchTest extends AbstractTest { private static final String LOGIN_DATE_VALUE = "2009-05-26"; @@ -114,7 +114,7 @@ public class AnySearchTest extends AbstractTest { private PlainAttrValidationManager validator; @BeforeEach - public void adjustLoginDateForLocalSystem() { + void adjustLoginDateForLocalSystem() { User rossini = userDAO.findByUsername("rossini").orElseThrow(); PlainAttr loginDate = rossini.getPlainAttr("loginDate").get(); @@ -124,7 +124,7 @@ public void adjustLoginDateForLocalSystem() { } @Test - public void orOfThree() { + void orOfThree() { AnyCond cond1 = new AnyCond(AttrCond.Type.EQ); cond1.setSchema("username"); cond1.setExpression("rossini"); @@ -149,7 +149,7 @@ public void orOfThree() { } @Test - public void searchTwoPlainSchemas() { + void searchTwoPlainSchemas() { AttrCond firstnameCond = new AttrCond(AttrCond.Type.EQ); firstnameCond.setSchema("firstname"); firstnameCond.setExpression("Gioacchino"); @@ -204,7 +204,7 @@ public void searchTwoPlainSchemas() { } @Test - public void searchWithLikeCondition() { + void searchWithLikeCondition() { AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.LIKE); fullnameLeafCond.setSchema("fullname"); fullnameLeafCond.setExpression("%o%"); @@ -231,7 +231,7 @@ public void searchWithLikeCondition() { } @Test - public void searchCaseInsensitiveWithLikeCondition() { + void searchCaseInsensitiveWithLikeCondition() { AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.ILIKE); fullnameLeafCond.setSchema("fullname"); fullnameLeafCond.setExpression("%O%"); @@ -258,7 +258,7 @@ public void searchCaseInsensitiveWithLikeCondition() { } @Test - public void searchWithNotAttrCond() { + void searchWithNotAttrCond() { AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.EQ); fullnameLeafCond.setSchema("fullname"); fullnameLeafCond.setExpression("Giuseppe Verdi"); @@ -276,7 +276,7 @@ public void searchWithNotAttrCond() { } @Test - public void searchWithNotAnyCond() { + void searchWithNotAnyCond() { AnyCond usernameLeafCond = new AnyCond(AttrCond.Type.EQ); usernameLeafCond.setSchema("username"); usernameLeafCond.setExpression("verdi"); @@ -292,7 +292,7 @@ public void searchWithNotAnyCond() { } @Test - public void searchCaseInsensitiveWithNotCondition() { + void searchCaseInsensitiveWithNotCondition() { AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.IEQ); fullnameLeafCond.setSchema("fullname"); fullnameLeafCond.setExpression("giuseppe verdi"); @@ -310,7 +310,7 @@ public void searchCaseInsensitiveWithNotCondition() { } @Test - public void searchByBoolean() { + void searchByBoolean() { AttrCond coolLeafCond = new AttrCond(AttrCond.Type.EQ); coolLeafCond.setSchema("cool"); coolLeafCond.setExpression("true"); @@ -326,7 +326,7 @@ public void searchByBoolean() { } @Test - public void searchByRealm() { + void searchByRealm() { AnyCond anyCond = new AnyCond(AttrCond.Type.EQ); anyCond.setSchema("realm"); anyCond.setExpression("c5b75db1-fce7-470f-b780-3b9934d82a9d"); @@ -344,7 +344,7 @@ public void searchByRealm() { } @Test - public void searchByUManager() { + void searchByUManager() { AnyCond anyCond = new AnyCond(AttrCond.Type.EQ); anyCond.setSchema("uManager"); anyCond.setExpression("823074dc-d280-436d-a7dd-07399fae48ec"); @@ -362,7 +362,7 @@ public void searchByUManager() { } @Test - public void searchByPageAndSize() { + void searchByPageAndSize() { AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.LIKE); fullnameLeafCond.setSchema("fullname"); fullnameLeafCond.setExpression("%o%"); @@ -401,7 +401,7 @@ public void searchByPageAndSize() { } @Test - public void searchByGroup() { + void searchByGroup() { MembershipCond groupCond = new MembershipCond(); groupCond.setGroup("child"); @@ -436,7 +436,7 @@ public void searchByGroup() { } @Test - public void searchByRole() { + void searchByRole() { RoleCond roleCond = new RoleCond(); roleCond.setRole("Other"); @@ -446,7 +446,7 @@ public void searchByRole() { } @Test - public void searchByIsNull() { + void searchByIsNull() { AttrCond coolLeafCond = new AttrCond(AttrCond.Type.ISNULL); coolLeafCond.setSchema("cool"); @@ -463,7 +463,7 @@ public void searchByIsNull() { } @Test - public void searchByAuxClass() { + void searchByAuxClass() { AuxClassCond ac = new AuxClassCond(); ac.setAuxClass("csv"); @@ -473,7 +473,7 @@ public void searchByAuxClass() { } @Test - public void searchByResource() { + void searchByResource() { ResourceCond ws2 = new ResourceCond(); ws2.setResource("ws-target-resource-2"); @@ -489,7 +489,7 @@ public void searchByResource() { } @Test - public void searchByBooleanAttrCond() { + void searchByBooleanAttrCond() { AttrCond booleanCond = new AttrCond(AnyCond.Type.EQ); booleanCond.setSchema("show"); booleanCond.setExpression("true"); @@ -500,7 +500,7 @@ public void searchByBooleanAttrCond() { } @Test - public void searchByUsernameAndKey() { + void searchByUsernameAndKey() { AnyCond usernameLeafCond = new AnyCond(AnyCond.Type.LIKE); usernameLeafCond.setSchema("username"); usernameLeafCond.setExpression("%ini"); @@ -521,7 +521,7 @@ public void searchByUsernameAndKey() { } @Test - public void searchByGroupNameAndKey() { + void searchByGroupNameAndKey() { AnyCond groupNameLeafCond = new AnyCond(AnyCond.Type.EQ); groupNameLeafCond.setSchema("name"); groupNameLeafCond.setExpression("root"); @@ -544,7 +544,7 @@ public void searchByGroupNameAndKey() { } @Test - public void searchByUsernameAndFullname() { + void searchByUsernameAndFullname() { AnyCond usernameLeafCond = new AnyCond(AnyCond.Type.EQ); usernameLeafCond.setSchema("username"); usernameLeafCond.setExpression("rossini"); @@ -563,7 +563,7 @@ public void searchByUsernameAndFullname() { } @Test - public void searchByUsernameAndFullnameIgnoreCase() { + void searchByUsernameAndFullnameIgnoreCase() { AnyCond usernameLeafCond = new AnyCond(AnyCond.Type.IEQ); usernameLeafCond.setSchema("username"); usernameLeafCond.setExpression("RoSsini"); @@ -582,7 +582,7 @@ public void searchByUsernameAndFullnameIgnoreCase() { } @Test - public void searchByKey() { + void searchByKey() { AnyCond idLeafCond = new AnyCond(AnyCond.Type.EQ); idLeafCond.setSchema("key"); idLeafCond.setExpression("74cd8ece-715a-44a4-a736-e17b46c4e7e6"); @@ -597,7 +597,7 @@ public void searchByKey() { } @Test - public void searchByType() { + void searchByType() { AnyTypeCond tcond = new AnyTypeCond(); tcond.setAnyTypeKey("PRINTER"); @@ -615,7 +615,7 @@ public void searchByType() { } @Test - public void searchByRelationshipType() { + void searchByRelationshipType() { // 1. first search for printers involved in "inclusion" relationship RelationshipTypeCond relationshipTypeCond = new RelationshipTypeCond(); relationshipTypeCond.setRelationshipType("inclusion"); @@ -642,7 +642,7 @@ public void searchByRelationshipType() { } @Test - public void searchByAnyCondDate() { + void searchByAnyCondDate() { AnyCond creationDateCond = new AnyCond(AnyCond.Type.EQ); creationDateCond.setSchema("creationDate"); creationDateCond.setExpression("2021-04-15 12:45:00"); @@ -657,7 +657,7 @@ public void searchByAnyCondDate() { } @Test - public void searchByAttrCondDate() { + void searchByAttrCondDate() { AttrCond loginDateCond = new AttrCond(AnyCond.Type.LT); loginDateCond.setSchema("loginDate"); loginDateCond.setExpression("2009-05-27"); @@ -672,7 +672,7 @@ public void searchByAttrCondDate() { } @Test - public void userOrderBy() { + void userOrderBy() { AnyCond usernameLeafCond = new AnyCond(AnyCond.Type.EQ); usernameLeafCond.setSchema("username"); usernameLeafCond.setExpression("rossini"); @@ -697,7 +697,7 @@ public void userOrderBy() { } @Test - public void groupOrderBy() { + void groupOrderBy() { AnyCond idLeafCond = new AnyCond(AnyCond.Type.LIKE); idLeafCond.setSchema("name"); idLeafCond.setExpression("%r"); @@ -716,7 +716,7 @@ public void groupOrderBy() { } @Test - public void member() { + void member() { MemberCond memberCond = new MemberCond(); memberCond.setMember("1417acbe-cbf6-4277-9372-e75e04f97000"); SearchCond searchCondition = SearchCond.of(memberCond); @@ -729,7 +729,7 @@ public void member() { } @Test - public void asGroupManager() { + void asGroupManager() { // prepare authentication Map> entForRealms = Map.of( IdRepoEntitlement.GROUP_SEARCH, @@ -777,7 +777,7 @@ public void asGroupManager() { } @Test - public void changePwdDate() { + void changePwdDate() { AnyCond statusCond = new AnyCond(AttrCond.Type.IEQ); statusCond.setSchema("status"); statusCond.setExpression("suspended"); @@ -794,7 +794,7 @@ public void changePwdDate() { } @Test - public void findByDerAttrValue() { + void findByDerAttrValue() { List list = searchDAO.findByDerAttrValue( derSchemaDAO.findById("cn").orElseThrow().getExpression(), "Vivaldi, Antonio", false, AnyTypeKind.USER); assertEquals(1, list.size()); @@ -809,21 +809,21 @@ public void findByDerAttrValue() { } @Test - public void findByInvalidDerAttrValue() { + void findByInvalidDerAttrValue() { assertTrue(searchDAO.findByDerAttrValue( derSchemaDAO.findById("cn").orElseThrow().getExpression(), "Antonio, Maria, Rossi", false, AnyTypeKind.USER).isEmpty()); } @Test - public void findByInvalidDerAttrExpression() { + void findByInvalidDerAttrExpression() { assertThrows(IllegalArgumentException.class, () -> searchDAO.findByDerAttrValue( derSchemaDAO.findById("noschema").orElseThrow().getExpression(), "Antonio, Maria", false, AnyTypeKind.USER).isEmpty()); } @Test - public void issue202() { + void issue202() { ResourceCond ws2 = new ResourceCond(); ws2.setResource("ws-target-resource-2"); @@ -841,7 +841,7 @@ public void issue202() { } @Test - public void issue242() { + void issue242() { AnyCond cond = new AnyCond(AttrCond.Type.LIKE); cond.setSchema("key"); cond.setExpression("test%"); @@ -855,7 +855,7 @@ public void issue242() { } @Test - public void issueSYNCOPE46() { + void issueSYNCOPE46() { AnyCond cond = new AnyCond(AttrCond.Type.LIKE); cond.setSchema("username"); cond.setExpression("%ossin%"); @@ -869,7 +869,7 @@ public void issueSYNCOPE46() { } @Test - public void issueSYNCOPE433() { + void issueSYNCOPE433() { AttrCond isNullCond = new AttrCond(AttrCond.Type.ISNULL); isNullCond.setSchema("loginDate"); @@ -886,7 +886,7 @@ public void issueSYNCOPE433() { } @Test - public void issueSYNCOPE929() { + void issueSYNCOPE929() { AttrCond rossiniCond = new AttrCond(AttrCond.Type.EQ); rossiniCond.setSchema("surname"); rossiniCond.setExpression("Rossini"); @@ -909,7 +909,7 @@ public void issueSYNCOPE929() { } @Test - public void issueSYNCOPE980() { + void issueSYNCOPE980() { AnyType service = entityFactory.newEntity(AnyType.class); service.setKey("SERVICE"); service.setKind(AnyTypeKind.ANY_OBJECT); @@ -955,7 +955,7 @@ public void issueSYNCOPE980() { } @Test - public void issueSYNCOPE983() { + void issueSYNCOPE983() { AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.LIKE); fullnameLeafCond.setSchema("surname"); fullnameLeafCond.setExpression("%o%"); @@ -975,7 +975,7 @@ public void issueSYNCOPE983() { } @Test - public void issueSYNCOPE1416() { + void issueSYNCOPE1416() { AttrCond idLeftCond = new AttrCond(AttrCond.Type.ISNOTNULL); idLeftCond.setSchema("surname"); @@ -1012,7 +1012,7 @@ public void issueSYNCOPE1416() { } @Test - public void issueSYNCOPE1419() { + void issueSYNCOPE1419() { AttrCond loginDateCond = new AttrCond(AttrCond.Type.EQ); loginDateCond.setSchema("loginDate"); loginDateCond.setExpression(LOGIN_DATE_VALUE); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyTypeClassTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyTypeClassTest.java index 8b0a7d904ab..2503ff8fedb 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyTypeClassTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyTypeClassTest.java @@ -34,7 +34,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AnyTypeClassTest extends AbstractTest { +class AnyTypeClassTest extends AbstractTest { @Autowired private PlainSchemaDAO plainSchemaDAO; @@ -43,7 +43,7 @@ public class AnyTypeClassTest extends AbstractTest { private AnyTypeClassDAO anyTypeClassDAO; @Test - public void find() { + void find() { AnyTypeClass minimalGroup = anyTypeClassDAO.findById("minimal group").orElseThrow(); assertFalse(minimalGroup.getPlainSchemas().isEmpty()); @@ -51,13 +51,13 @@ public void find() { } @Test - public void findAll() { + void findAll() { List list = anyTypeClassDAO.findAll(); assertFalse(list.isEmpty()); } @Test - public void save() { + void save() { PlainSchema firstname = plainSchemaDAO.findById("firstname").orElseThrow(); AnyTypeClass newClass = entityFactory.newEntity(AnyTypeClass.class); @@ -77,7 +77,7 @@ public void save() { } @Test - public void delete() { + void delete() { AnyTypeClass minimalUser = anyTypeClassDAO.findById("minimal user").orElseThrow(); assertNotNull(minimalUser); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyTypeTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyTypeTest.java index c66dfa3c219..213c2357425 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyTypeTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyTypeTest.java @@ -36,7 +36,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AnyTypeTest extends AbstractTest { +class AnyTypeTest extends AbstractTest { @Autowired private AnyTypeDAO anyTypeDAO; @@ -45,7 +45,7 @@ public class AnyTypeTest extends AbstractTest { private AnyTypeClassDAO anyTypeClassDAO; @Test - public void find() { + void find() { AnyType userType = anyTypeDAO.getUser(); assertNotNull(userType); assertEquals(AnyTypeKind.USER, userType.getKind()); @@ -64,13 +64,13 @@ public void find() { } @Test - public void findAll() { + void findAll() { List list = anyTypeDAO.findAll(); assertFalse(list.isEmpty()); } @Test - public void save() { + void save() { AnyType newType = entityFactory.newEntity(AnyType.class); newType.setKey("new type"); newType.setKind(AnyTypeKind.ANY_OBJECT); @@ -83,7 +83,7 @@ public void save() { } @Test - public void saveInvalidKind() { + void saveInvalidKind() { assertThrows(InvalidEntityException.class, () -> { AnyType newType = entityFactory.newEntity(AnyType.class); newType.setKey("new type"); @@ -93,7 +93,7 @@ public void saveInvalidKind() { } @Test - public void saveInvalidName() { + void saveInvalidName() { assertThrows(InvalidEntityException.class, () -> { AnyType newType = entityFactory.newEntity(AnyType.class); newType.setKey("group"); @@ -103,7 +103,7 @@ public void saveInvalidName() { } @Test - public void delete() { + void delete() { AnyType otherType = anyTypeDAO.findById("PRINTER").orElseThrow(); assertNotNull(otherType); @@ -112,7 +112,7 @@ public void delete() { } @Test - public void deleteInvalid() { + void deleteInvalid() { assertThrows(IllegalArgumentException.class, () -> anyTypeDAO.deleteById(AnyTypeKind.USER.name())); } } diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AttrRepoTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AttrRepoTest.java index 6b09ddc753a..5566773d1de 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AttrRepoTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AttrRepoTest.java @@ -40,13 +40,13 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AttrRepoTest extends AbstractTest { +class AttrRepoTest extends AbstractTest { @Autowired private AttrRepoDAO attrRepoDAO; @Test - public void findAll() { + void findAll() { List modules = attrRepoDAO.findAll(); assertNotNull(modules); assertFalse(modules.isEmpty()); @@ -54,7 +54,7 @@ public void findAll() { } @Test - public void find() { + void find() { AttrRepo attrRepo = attrRepoDAO.findById("DefaultLDAPAttrRepo").orElseThrow(); assertTrue(attrRepo.getConf() instanceof LDAPAttrRepoConf); @@ -70,7 +70,7 @@ public void find() { } @Test - public void findByType() { + void findByType() { List attrRepos = attrRepoDAO.findAll(); assertTrue(attrRepos.stream().anyMatch( attrRepo -> attrRepo.getConf() instanceof LDAPAttrRepoConf @@ -112,7 +112,7 @@ private void saveAttrRepo(final String key, final AttrRepoConf conf) { } @Test - public void saveWithStubRepo() { + void saveWithStubRepo() { StubAttrRepoConf conf = new StubAttrRepoConf(); conf.getAttributes().put("attr1", UUID.randomUUID().toString()); conf.getAttributes().put("attr2", UUID.randomUUID().toString()); @@ -121,7 +121,7 @@ public void saveWithStubRepo() { } @Test - public void saveWithLdapRepo() { + void saveWithLdapRepo() { LDAPAttrRepoConf conf = new LDAPAttrRepoConf(); conf.setBaseDn("dc=example,dc=org"); conf.setSearchFilter("cn={user}"); @@ -133,7 +133,7 @@ public void saveWithLdapRepo() { } @Test - public void saveWithJDBCRepo() { + void saveWithJDBCRepo() { JDBCAttrRepoConf conf = new JDBCAttrRepoConf(); conf.setSql("SELECT * FROM table WHERE name=?"); conf.setUrl("jdbc:h2:mem:syncopedb;DB_CLOSE_DELAY=-1"); @@ -144,7 +144,7 @@ public void saveWithJDBCRepo() { } @Test - public void saveWithSyncopeRepo() { + void saveWithSyncopeRepo() { SyncopeAttrRepoConf conf = new SyncopeAttrRepoConf(); conf.setDomain("Master"); @@ -152,7 +152,7 @@ public void saveWithSyncopeRepo() { } @Test - public void updateWithLDAPRepo() { + void updateWithLDAPRepo() { AttrRepo module = attrRepoDAO.findById("DefaultLDAPAttrRepo").orElseThrow(); AttrRepoConf conf = module.getConf(); LDAPAttrRepoConf.class.cast(conf).setBaseDn("dc=example2,dc=org"); @@ -169,7 +169,7 @@ public void updateWithLDAPRepo() { } @Test - public void updateWithJDBCRepo() { + void updateWithJDBCRepo() { AttrRepo module = attrRepoDAO.findById("DefaultJDBCAttrRepo").orElseThrow(); AttrRepoConf conf = module.getConf(); JDBCAttrRepoConf.class.cast(conf).setSql("SELECT * FROM otherTable WHERE name=?"); @@ -183,7 +183,7 @@ public void updateWithJDBCRepo() { } @Test - public void updateWithStubRepo() { + void updateWithStubRepo() { AttrRepo module = attrRepoDAO.findById("DefaultStubAttrRepo").orElseThrow(); assertEquals(1, StubAttrRepoConf.class.cast(module.getConf()).getAttributes().size()); AttrRepoConf conf = module.getConf(); @@ -197,7 +197,7 @@ public void updateWithStubRepo() { } @Test - public void updateWithSyncopeRepo() { + void updateWithSyncopeRepo() { AttrRepo module = attrRepoDAO.findById("DefaultSyncopeAttrRepo").orElseThrow(); AttrRepoConf conf = module.getConf(); @@ -212,7 +212,7 @@ public void updateWithSyncopeRepo() { } @Test - public void delete() { + void delete() { assertTrue(attrRepoDAO.findById("DefaultSyncopeAttrRepo").isPresent()); attrRepoDAO.deleteById("DefaultSyncopeAttrRepo"); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthModuleTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthModuleTest.java index c39e014731b..0584159f8c0 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthModuleTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthModuleTest.java @@ -46,13 +46,13 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AuthModuleTest extends AbstractTest { +class AuthModuleTest extends AbstractTest { @Autowired private AuthModuleDAO authModuleDAO; @Test - public void findAll() { + void findAll() { List modules = authModuleDAO.findAll(); assertNotNull(modules); assertFalse(modules.isEmpty()); @@ -60,7 +60,7 @@ public void findAll() { } @Test - public void find() { + void find() { AuthModule authModule = authModuleDAO.findById("DefaultLDAPAuthModule").orElseThrow(); assertTrue(authModule.getConf() instanceof LDAPAuthModuleConf); @@ -93,7 +93,7 @@ public void find() { } @Test - public void findByType() { + void findByType() { List authModules = authModuleDAO.findAll(); assertTrue(authModules.stream().anyMatch( authModule -> authModule.getConf() instanceof LDAPAuthModuleConf @@ -150,7 +150,7 @@ private void saveAuthModule(final String key, final AuthModuleConf conf) { } @Test - public void saveWithStaticModule() { + void saveWithStaticModule() { StaticAuthModuleConf conf = new StaticAuthModuleConf(); conf.getUsers().put("user1", UUID.randomUUID().toString()); conf.getUsers().put("user2", "user2Password123"); @@ -159,7 +159,7 @@ public void saveWithStaticModule() { } @Test - public void saveWithJaasModule() { + void saveWithJaasModule() { JaasAuthModuleConf conf = new JaasAuthModuleConf(); conf.setKerberosKdcSystemProperty("sample-value"); conf.setKerberosRealmSystemProperty("sample-value"); @@ -171,7 +171,7 @@ public void saveWithJaasModule() { } @Test - public void saveWithLdapModule() { + void saveWithLdapModule() { LDAPAuthModuleConf conf = new LDAPAuthModuleConf(); conf.setBaseDn("dc=example,dc=org"); conf.setSearchFilter("cn={user}"); @@ -184,7 +184,7 @@ public void saveWithLdapModule() { } @Test - public void saveWithGoogleAuthenticatorModule() { + void saveWithGoogleAuthenticatorModule() { GoogleMfaAuthModuleConf conf = new GoogleMfaAuthModuleConf(); conf.setCodeDigits(6); conf.setIssuer("SyncopeTest"); @@ -196,7 +196,7 @@ public void saveWithGoogleAuthenticatorModule() { } @Test - public void saveWithDuoAuthenticatorModule() { + void saveWithDuoAuthenticatorModule() { DuoMfaAuthModuleConf conf = new DuoMfaAuthModuleConf(); conf.setSecretKey("Q2IU2i6BFNd6VYflZT8Evl6lF7oPlj4PM15BmRU7"); conf.setIntegrationKey("DIOXVRZD1UMZ8XXMNFQ6"); @@ -205,7 +205,7 @@ public void saveWithDuoAuthenticatorModule() { } @Test - public void saveWithOIDCAuthModule() { + void saveWithOIDCAuthModule() { OIDCAuthModuleConf conf = new OIDCAuthModuleConf(); conf.setClientId("OIDCTestId"); conf.setDiscoveryUri("www.testurl.com"); @@ -217,7 +217,7 @@ public void saveWithOIDCAuthModule() { } @Test - public void saveWithJDBCModule() { + void saveWithJDBCModule() { JDBCAuthModuleConf conf = new JDBCAuthModuleConf(); conf.setSql("SELECT * FROM table WHERE name=?"); conf.setFieldPassword("password"); @@ -226,7 +226,7 @@ public void saveWithJDBCModule() { } @Test - public void saveWithSyncopeModule() { + void saveWithSyncopeModule() { SyncopeAuthModuleConf conf = new SyncopeAuthModuleConf(); conf.setDomain("Master"); @@ -234,7 +234,7 @@ public void saveWithSyncopeModule() { } @Test - public void saveWithSAML2IdPModule() { + void saveWithSAML2IdPModule() { SAML2IdPAuthModuleConf conf = new SAML2IdPAuthModuleConf(); conf.setServiceProviderEntityId("testEntityId"); conf.setProviderName("testProviderName"); @@ -242,7 +242,7 @@ public void saveWithSAML2IdPModule() { } @Test - public void saveWithSimpleMfaModule() { + void saveWithSimpleMfaModule() { SimpleMfaAuthModuleConf conf = new SimpleMfaAuthModuleConf(); conf.setTokenLength(9); conf.setTimeToKillInSeconds(120); @@ -250,7 +250,7 @@ public void saveWithSimpleMfaModule() { } @Test - public void updateWithLDAPModule() { + void updateWithLDAPModule() { AuthModule module = authModuleDAO.findById("DefaultLDAPAuthModule").orElseThrow(); AuthModuleConf conf = module.getConf(); LDAPAuthModuleConf.class.cast(conf).setBaseDn("dc=example2,dc=org"); @@ -267,7 +267,7 @@ public void updateWithLDAPModule() { } @Test - public void updateWithJDBCModule() { + void updateWithJDBCModule() { AuthModule module = authModuleDAO.findById("DefaultJDBCAuthModule").orElseThrow(); AuthModuleConf conf = module.getConf(); JDBCAuthModuleConf.class.cast(conf).setSql("SELECT * FROM otherTable WHERE name=?"); @@ -281,7 +281,7 @@ public void updateWithJDBCModule() { } @Test - public void updateWithGoogleMfaModule() { + void updateWithGoogleMfaModule() { AuthModule module = authModuleDAO.findById("DefaultGoogleMfaAuthModule").orElseThrow(); AuthModuleConf conf = module.getConf(); GoogleMfaAuthModuleConf.class.cast(conf).setLabel("newLabel"); @@ -295,7 +295,7 @@ public void updateWithGoogleMfaModule() { } @Test - public void updateWithDuoMfaModule() { + void updateWithDuoMfaModule() { AuthModule module = authModuleDAO.findById("DefaultDuoMfaAuthModule").orElseThrow(); AuthModuleConf conf = module.getConf(); String secretKey = UUID.randomUUID().toString(); @@ -310,7 +310,7 @@ public void updateWithDuoMfaModule() { } @Test - public void updateWithSAML2IdPModule() { + void updateWithSAML2IdPModule() { AuthModule module = authModuleDAO.findById("DefaultSAML2IdPAuthModule").orElseThrow(); AuthModuleConf conf = module.getConf(); SAML2IdPAuthModuleConf.class.cast(conf).setServiceProviderEntityId("newEntityId"); @@ -325,7 +325,7 @@ public void updateWithSAML2IdPModule() { } @Test - public void updateWithOIDCModule() { + void updateWithOIDCModule() { AuthModule module = authModuleDAO.findById("DefaultOIDCAuthModule").orElseThrow(); AuthModuleConf conf = module.getConf(); OIDCAuthModuleConf.class.cast(conf).setResponseType("newCode"); @@ -339,7 +339,7 @@ public void updateWithOIDCModule() { } @Test - public void updateWithJaasModule() { + void updateWithJaasModule() { AuthModule module = authModuleDAO.findById("DefaultJaasAuthModule").orElseThrow(); AuthModuleConf conf = module.getConf(); JaasAuthModuleConf.class.cast(conf).setRealm("SYNCOPE_NEW"); @@ -353,7 +353,7 @@ public void updateWithJaasModule() { } @Test - public void updateWithStaticModule() { + void updateWithStaticModule() { AuthModule module = authModuleDAO.findById("DefaultStaticAuthModule").orElseThrow(); assertNotNull(module); assertEquals(1, StaticAuthModuleConf.class.cast(module.getConf()).getUsers().size()); @@ -369,7 +369,7 @@ public void updateWithStaticModule() { } @Test - public void updateWithSyncopeModule() { + void updateWithSyncopeModule() { AuthModule module = authModuleDAO.findById("DefaultSyncopeAuthModule").orElseThrow(); AuthModuleConf conf = module.getConf(); @@ -384,7 +384,7 @@ public void updateWithSyncopeModule() { } @Test - public void delete() { + void delete() { assertTrue(authModuleDAO.findById("DefaultSyncopeAuthModule").isPresent()); authModuleDAO.deleteById("DefaultSyncopeAuthModule"); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthProfileTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthProfileTest.java index def51824ee5..1f733aba987 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthProfileTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthProfileTest.java @@ -44,7 +44,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AuthProfileTest extends AbstractTest { +class AuthProfileTest extends AbstractTest { @Autowired private AuthProfileDAO authProfileDAO; @@ -53,12 +53,12 @@ public class AuthProfileTest extends AbstractTest { private Neo4jTemplate neo4jTemplate; @BeforeEach - public void beforeEach() { + void beforeEach() { neo4jTemplate.deleteAll(Neo4jAuthProfile.class); } @Test - public void googleMfaToken() { + void googleMfaToken() { String id = SecureRandomUtils.generateRandomUUID().toString(); AuthProfile profile = entityFactory.newEntity(AuthProfile.class); @@ -85,7 +85,7 @@ public void googleMfaToken() { } @Test - public void webAuthnRegisteredDevice() { + void webAuthnRegisteredDevice() { String id = SecureRandomUtils.generateRandomUUID().toString(); String record = "[ {" + " \"userIdentity\" : {" @@ -122,7 +122,7 @@ public void webAuthnRegisteredDevice() { } @Test - public void googleMfaAccount() { + void googleMfaAccount() { String id = SecureRandomUtils.generateRandomUUID().toString(); createAuthProfileWithAccount(id); @@ -144,7 +144,7 @@ public void googleMfaAccount() { } @Test - public void impersonationAccounts() { + void impersonationAccounts() { String id = SecureRandomUtils.generateRandomUUID().toString(); createAuthProfileWithAccount(id); @@ -167,7 +167,7 @@ public void impersonationAccounts() { } @Test - public void findByOwnerLike() { + void findByOwnerLike() { createAuthProfileWithAccount("owner1"); createAuthProfileWithAccount("owner2"); createAuthProfileWithAccount("test"); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/CASSPTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/CASSPTest.java index 78243876f30..a97119d99fa 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/CASSPTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/CASSPTest.java @@ -32,13 +32,13 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class CASSPTest extends AbstractClientAppTest { +class CASSPTest extends AbstractClientAppTest { @Autowired private CASSPClientAppDAO casspDAO; @Test - public void find() { + void find() { long beforeCount = casspDAO.count(); CASSPClientApp rp = entityFactory.newEntity(CASSPClientApp.class); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ConnInstanceTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ConnInstanceTest.java index 2f7a226c753..eda4efc9f11 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ConnInstanceTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ConnInstanceTest.java @@ -45,7 +45,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class ConnInstanceTest extends AbstractTest { +class ConnInstanceTest extends AbstractTest { @Autowired private ConnInstanceDAO connInstanceDAO; @@ -54,7 +54,7 @@ public class ConnInstanceTest extends AbstractTest { private RealmDAO realmDAO; @Test - public void findAll() { + void findAll() { List authorities = IdMEntitlement.values().stream(). map(entitlement -> new SyncopeGrantedAuthority(entitlement, SyncopeConstants.ROOT_REALM)). collect(Collectors.toList()); @@ -75,7 +75,7 @@ public void findAll() { } @Test - public void findById() { + void findById() { ConnInstance connInstance = connInstanceDAO.findById("88a7a819-dab5-46b4-9b90-0b9769eabdb8").orElseThrow(); assertNotNull(connInstance); assertEquals("net.tirasa.connid.bundles.soap.WebServiceConnector", connInstance.getConnectorName()); @@ -90,7 +90,7 @@ public void findById() { } @Test - public void save() { + void save() { ConnInstance connInstance = entityFactory.newEntity(ConnInstance.class); connInstance.setLocation("file:" + System.getProperty("java.io.tmpdir")); connInstance.setVersion("1.0"); @@ -140,7 +140,7 @@ public void save() { } @Test - public void delete() { + void delete() { ConnInstance connInstance = connInstanceDAO.findById("88a7a819-dab5-46b4-9b90-0b9769eabdb8").orElseThrow(); connInstanceDAO.deleteById(connInstance.getKey()); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/DerSchemaTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/DerSchemaTest.java index deb61f79873..a9ac6873cb0 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/DerSchemaTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/DerSchemaTest.java @@ -33,30 +33,30 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class DerSchemaTest extends AbstractTest { +class DerSchemaTest extends AbstractTest { @Autowired private DerSchemaDAO derSchemaDAO; @Test - public void findAll() { + void findAll() { List list = derSchemaDAO.findAll(); assertEquals(10, list.size()); } @Test - public void findByIdLike() { + void findByIdLike() { List schemas = derSchemaDAO.findByIdLike("mderivedd%"); assertEquals(1, schemas.size()); } @Test - public void findByName() { + void findByName() { assertTrue(derSchemaDAO.findById("cn").isPresent()); } @Test - public void save() { + void save() { DerSchema derivedAttributeSchema = entityFactory.newEntity(DerSchema.class); derivedAttributeSchema.setKey("cn2"); derivedAttributeSchema.setExpression("firstname surname"); @@ -68,7 +68,7 @@ public void save() { } @Test - public void delete() { + void delete() { DerSchema cn = derSchemaDAO.findById("cn").orElseThrow(); derSchemaDAO.deleteById(cn.getKey()); @@ -84,7 +84,7 @@ public void delete() { } @Test - public void issueSYNCOPE418() { + void issueSYNCOPE418() { DerSchema schema = entityFactory.newEntity(DerSchema.class); schema.setKey("http://schemas.examples.org/security/authorization/organizationUnit"); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/GroupTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/GroupTest.java index d9afab454e6..77da6ca9513 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/GroupTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/GroupTest.java @@ -34,7 +34,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class GroupTest extends AbstractTest { +class GroupTest extends AbstractTest { @Autowired private GroupDAO groupDAO; @@ -46,13 +46,13 @@ public class GroupTest extends AbstractTest { private AnyTypeDAO anyTypeDAO; @Test - public void findAll() { + void findAll() { List groups = groupDAO.findAll(); assertEquals(16, groups.size()); } @Test - public void find() { + void find() { Group group = groupDAO.findByName("additional").orElseThrow(); assertNotNull(group); assertEquals(1, group.getTypeExtensions().size()); @@ -60,7 +60,7 @@ public void find() { } @Test - public void findKeysByNamePattern() { + void findKeysByNamePattern() { List groups = groupDAO.findKeysByNamePattern(".*child"); assertEquals(2, groups.size()); assertTrue(groups.contains("b1f7c12d-ec83-441f-a50e-1691daaedf3b")); @@ -68,7 +68,7 @@ public void findKeysByNamePattern() { } @Test - public void save() { + void save() { Group group = entityFactory.newEntity(Group.class); group.setName("secondChild"); group.setRealm(realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); @@ -79,7 +79,7 @@ public void save() { } @Test - public void delete() { + void delete() { Group group = groupDAO.findById("8fb2d51e-c605-4e80-a72b-13ffecf1aa9a").orElseThrow(); groupDAO.deleteById(group.getKey()); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ImplementationTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ImplementationTest.java index 4b0da853146..a6bb020852f 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ImplementationTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ImplementationTest.java @@ -34,20 +34,20 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class ImplementationTest extends AbstractTest { +class ImplementationTest extends AbstractTest { @Autowired private ImplementationDAO implementationDAO; @Test - public void findById() { + void findById() { Implementation impl = implementationDAO.findById("ExpiredBatchCleanup").orElseThrow(); assertEquals(IdRepoImplementationType.TASKJOB_DELEGATE, impl.getType()); assertEquals(ImplementationEngine.JAVA, impl.getEngine()); } @Test - public void findAll() { + void findAll() { List implementations = implementationDAO.findAll(); assertFalse(implementations.isEmpty()); @@ -85,7 +85,7 @@ public void findAll() { } @Test - public void create() { + void create() { Implementation impl = entityFactory.newEntity(Implementation.class); impl.setKey("new"); impl.setEngine(ImplementationEngine.GROOVY); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/MailTemplateTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/MailTemplateTest.java index 13f898a9212..1b35abc9e52 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/MailTemplateTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/MailTemplateTest.java @@ -32,27 +32,27 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class MailTemplateTest extends AbstractTest { +class MailTemplateTest extends AbstractTest { @Autowired private MailTemplateDAO mailTemplateDAO; @Test - public void find() { + void find() { MailTemplate optin = mailTemplateDAO.findById("optin").orElseThrow(); assertNotNull(optin.getTextTemplate()); assertNotNull(optin.getHTMLTemplate()); } @Test - public void findAll() { + void findAll() { List templates = mailTemplateDAO.findAll(); assertNotNull(templates); assertFalse(templates.isEmpty()); } @Test - public void save() { + void save() { MailTemplate template = entityFactory.newEntity(MailTemplate.class); template.setKey("new"); template.setTextTemplate("Text template"); @@ -70,7 +70,7 @@ public void save() { } @Test - public void delete() { + void delete() { mailTemplateDAO.deleteById("optin"); assertTrue(mailTemplateDAO.findById("optin").isEmpty()); } diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/MultitenancyTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/MultitenancyTest.java index dfc7ee33e1c..e3ce0b9e912 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/MultitenancyTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/MultitenancyTest.java @@ -37,7 +37,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class MultitenancyTest extends AbstractTest { +class MultitenancyTest extends AbstractTest { static { PersistenceTestContext.TEST_DOMAIN.set("Two"); @@ -61,12 +61,12 @@ public static void restoreDomain() { private UserDAO userDAO; @Test - public void readPlainSchemas() { + void readPlainSchemas() { assertEquals(1, plainSchemaDAO.findAll().size()); } @Test - public void readRealm() { + void readRealm() { assertEquals( 1, realmSearchDAO.findDescendants(realmDAO.getRoot().getFullPath(), null).size()); @@ -76,7 +76,7 @@ public void readRealm() { } @Test - public void createUser() { + void createUser() { assertNull(realmDAO.getRoot().getPasswordPolicy()); User user = entityFactory.newEntity(User.class); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/NotificationTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/NotificationTest.java index 0528692ca60..14fee29145c 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/NotificationTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/NotificationTest.java @@ -34,7 +34,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class NotificationTest extends AbstractTest { +class NotificationTest extends AbstractTest { @Autowired private AnyTypeDAO anyTypeDAO; @@ -46,7 +46,7 @@ public class NotificationTest extends AbstractTest { private MailTemplateDAO mailTemplateDAO; @Test - public void find() { + void find() { Notification notification = notificationDAO.findById("9e2b911c-25de-4c77-bcea-b86ed9451050").orElseThrow(); assertNotNull(notification.getEvents()); assertFalse(notification.getEvents().isEmpty()); @@ -55,14 +55,14 @@ public void find() { } @Test - public void findAll() { + void findAll() { List notifications = notificationDAO.findAll(); assertNotNull(notifications); assertFalse(notifications.isEmpty()); } @Test - public void save() { + void save() { Notification notification = entityFactory.newEntity(Notification.class); notification.getEvents().add("save"); @@ -86,13 +86,13 @@ public void save() { } @Test - public void delete() { + void delete() { notificationDAO.deleteById("9e2b911c-25de-4c77-bcea-b86ed9451050"); assertTrue(notificationDAO.findById("9e2b911c-25de-4c77-bcea-b86ed9451050").isEmpty()); } @Test - public void issueSYNCOPE445() { + void issueSYNCOPE445() { Notification notification = entityFactory.newEntity(Notification.class); notification.getEvents().add("save"); @@ -121,7 +121,7 @@ public void issueSYNCOPE445() { } @Test - public void issueSYNCOPE446() { + void issueSYNCOPE446() { Notification notification = entityFactory.newEntity(Notification.class); notification.getEvents().add("[LOGIC]:[GroupLogic]:[]:[create]:[SUCCESS]"); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/OIDCOpEntityTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/OIDCOpEntityTest.java index 6fd036d0a45..5653df28e80 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/OIDCOpEntityTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/OIDCOpEntityTest.java @@ -36,13 +36,13 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class OIDCOpEntityTest extends AbstractTest { +class OIDCOpEntityTest extends AbstractTest { @Autowired private OIDCOpEntityDAO oidcOpEntityDAO; @Test - public void save() throws Exception { + void save() throws Exception { OIDCOpEntity oidcOpEntity = entityFactory.newEntity(OIDCOpEntity.class); RSAKey jwk = new RSAKeyGenerator(2048). diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/OIDCRPTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/OIDCRPTest.java index 174cc9cb5d8..5ab5c8a37b9 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/OIDCRPTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/OIDCRPTest.java @@ -35,13 +35,13 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class OIDCRPTest extends AbstractClientAppTest { +class OIDCRPTest extends AbstractClientAppTest { @Autowired private OIDCRPClientAppDAO oidcrpDAO; @Test - public void find() { + void find() { int beforeCount = oidcrpDAO.findAll().size(); OIDCRPClientApp rp = entityFactory.newEntity(OIDCRPClientApp.class); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PasswordManagementTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PasswordManagementTest.java index 2d3e10848cb..0c18d8ffd66 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PasswordManagementTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PasswordManagementTest.java @@ -38,13 +38,13 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class PasswordManagementTest extends AbstractTest { +class PasswordManagementTest extends AbstractTest { @Autowired private PasswordManagementDAO passwordManagementDAO; @Test - public void findAll() { + void findAll() { List modules = passwordManagementDAO.findAll(); assertNotNull(modules); assertFalse(modules.isEmpty()); @@ -52,7 +52,7 @@ public void findAll() { } @Test - public void find() { + void find() { PasswordManagement passwordManagement = passwordManagementDAO.findById("DefaultSyncopePasswordManagement") .orElseThrow(); assertInstanceOf(SyncopePasswordManagementConf.class, passwordManagement.getConf()); @@ -65,7 +65,7 @@ public void find() { } @Test - public void findByType() { + void findByType() { List passwordManagements = passwordManagementDAO.findAll(); assertTrue(passwordManagements.stream().anyMatch( passwordManagement -> passwordManagement.getConf() instanceof SyncopePasswordManagementConf @@ -95,7 +95,7 @@ private void savePasswordManagement(final String key, final PasswordManagementCo } @Test - public void saveWithSyncopeModule() { + void saveWithSyncopeModule() { SyncopePasswordManagementConf conf = new SyncopePasswordManagementConf(); conf.setDomain("Master"); @@ -103,7 +103,7 @@ public void saveWithSyncopeModule() { } @Test - public void saveWithLDAPModule() { + void saveWithLDAPModule() { LDAPPasswordManagementConf conf = new LDAPPasswordManagementConf(); conf.setBaseDn("dc=example,dc=org"); conf.setSearchFilter("cn={user}"); @@ -116,7 +116,7 @@ public void saveWithLDAPModule() { } @Test - public void saveWithJDBCModule() { + void saveWithJDBCModule() { JDBCPasswordManagementConf conf = new JDBCPasswordManagementConf(); conf.setSqlFindEmail("SELECT email from users_table where name=?"); conf.setSqlFindPhone("SELECT phoneNumber from users_table where name=?"); @@ -127,7 +127,7 @@ public void saveWithJDBCModule() { } @Test - public void saveWithRESTModule() { + void saveWithRESTModule() { RESTPasswordManagementConf conf = new RESTPasswordManagementConf(); conf.setEndpointPassword("password"); conf.setEndpointUrlAccountUnlock("http://localhost:9443/syncope-fit-build-tools/cxf/rest/unlockAccount"); @@ -145,7 +145,7 @@ public void saveWithRESTModule() { } @Test - public void updateWithSyncopeModule() { + void updateWithSyncopeModule() { PasswordManagement module = passwordManagementDAO.findById("DefaultSyncopePasswordManagement").orElseThrow(); PasswordManagementConf conf = module.getConf(); @@ -161,7 +161,7 @@ public void updateWithSyncopeModule() { } @Test - public void updateWithLDAPModule() { + void updateWithLDAPModule() { PasswordManagement module = passwordManagementDAO.findById("DefaultLDAPPasswordManagement").orElseThrow(); PasswordManagementConf conf = module.getConf(); @@ -179,7 +179,7 @@ public void updateWithLDAPModule() { } @Test - public void updateWithJDBCModule() { + void updateWithJDBCModule() { PasswordManagement module = passwordManagementDAO.findById("DefaultJDBCPasswordManagement").orElseThrow(); PasswordManagementConf conf = module.getConf(); @@ -196,7 +196,7 @@ public void updateWithJDBCModule() { } @Test - public void updateWithRESTModule() { + void updateWithRESTModule() { PasswordManagement module = passwordManagementDAO.findById("DefaultRESTPasswordManagement").orElseThrow(); PasswordManagementConf conf = module.getConf(); @@ -213,7 +213,7 @@ public void updateWithRESTModule() { } @Test - public void delete() { + void delete() { assertTrue(passwordManagementDAO.findById("DefaultSyncopePasswordManagement").isPresent()); passwordManagementDAO.deleteById("DefaultSyncopePasswordManagement"); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PlainAttrTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PlainAttrTest.java index df515d517c3..2f18ce5997c 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PlainAttrTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PlainAttrTest.java @@ -52,7 +52,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class PlainAttrTest extends AbstractTest { +class PlainAttrTest extends AbstractTest { @Autowired private UserDAO userDAO; @@ -73,7 +73,7 @@ public class PlainAttrTest extends AbstractTest { private EncryptorManager encryptorManager; @Test - public void save() throws ClassNotFoundException { + void save() throws ClassNotFoundException { PlainSchema emailSchema = plainSchemaDAO.findById("email").orElseThrow(); if (emailSchema.getValidator() == null) { emailSchema.setValidator( @@ -102,7 +102,7 @@ public void save() throws ClassNotFoundException { } @Test - public void invalidValueList() { + void invalidValueList() { User user = userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").orElseThrow(); PlainSchema emailSchema = plainSchemaDAO.findById("email").orElseThrow(); @@ -125,7 +125,7 @@ public void invalidValueList() { } @Test - public void saveWithEncrypted() throws Exception { + void saveWithEncrypted() throws Exception { User user = userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").orElseThrow(); PlainSchema obscureSchema = plainSchemaDAO.findById("obscure").orElseThrow(); @@ -149,7 +149,7 @@ public void saveWithEncrypted() throws Exception { } @Test - public void encryptedWithKeyAsSysProp() throws Exception { + void encryptedWithKeyAsSysProp() throws Exception { PlainSchema obscureSchema = plainSchemaDAO.findById("obscure").orElseThrow(); PlainSchema obscureWithKeyAsSysprop = entityFactory.newEntity(PlainSchema.class); @@ -174,7 +174,7 @@ public void encryptedWithKeyAsSysProp() throws Exception { } @Test - public void encryptedWithDecodeConversionPattern() throws Exception { + void encryptedWithDecodeConversionPattern() throws Exception { PlainSchema obscureWithDecodeConversionPattern = entityFactory.newEntity(PlainSchema.class); obscureWithDecodeConversionPattern.setKey("obscureWithDecodeConversionPattern"); obscureWithDecodeConversionPattern.setAnyTypeClass(anyTypeClassDAO.findById("other").orElseThrow()); @@ -201,7 +201,7 @@ public void encryptedWithDecodeConversionPattern() throws Exception { } @Test - public void saveWithBinary() throws UnsupportedEncodingException { + void saveWithBinary() throws UnsupportedEncodingException { User user = userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").orElseThrow(); PlainSchema photoSchema = plainSchemaDAO.findById("photo").orElseThrow(); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PlainSchemaTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PlainSchemaTest.java index ead9afc1c48..9888b887641 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PlainSchemaTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PlainSchemaTest.java @@ -43,7 +43,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class PlainSchemaTest extends AbstractTest { +class PlainSchemaTest extends AbstractTest { @Autowired private PlainSchemaDAO plainSchemaDAO; @@ -52,20 +52,20 @@ public class PlainSchemaTest extends AbstractTest { private ImplementationDAO implementationDAO; @Test - public void findAll() { + void findAll() { List schemas = plainSchemaDAO.findAll(); assertEquals(28, schemas.size()); } @Test - public void findByIdLike() { + void findByIdLike() { List schemas = plainSchemaDAO.findByIdLike("fullna%"); assertEquals(1, schemas.size()); assertEquals(0, schemas.getFirst().getLabels().size()); } @Test - public void findByName() { + void findByName() { PlainSchema schema = plainSchemaDAO.findById("firstname").orElseThrow(); assertEquals(3, schema.getLabels().size()); @@ -74,7 +74,7 @@ public void findByName() { } @Test - public void hasAttrs() { + void hasAttrs() { PlainSchema schema = plainSchemaDAO.findById("icon").orElseThrow(); assertTrue(plainSchemaDAO.hasAttrs(schema)); @@ -83,7 +83,7 @@ public void hasAttrs() { } @Test - public void existsPlainAttrUniqueValue() { + void existsPlainAttrUniqueValue() { PlainAttrValue value = new PlainAttrValue(); value.setStringValue("rossini@apache.org"); PlainAttr attr = new PlainAttr(); @@ -110,7 +110,7 @@ public void existsPlainAttrUniqueValue() { } @Test - public void save() { + void save() { PlainSchema schema = entityFactory.newEntity(PlainSchema.class); schema.setKey("secondaryEmail"); schema.setType(AttrSchemaType.String); @@ -128,7 +128,7 @@ public void save() { } @Test - public void saveNonValid() { + void saveNonValid() { assertThrows(InvalidEntityException.class, () -> { PlainSchema schema = entityFactory.newEntity(PlainSchema.class); schema.setKey("secondaryEmail"); @@ -143,7 +143,7 @@ public void saveNonValid() { } @Test - public void checkForEnumType() { + void checkForEnumType() { PlainSchema schema = entityFactory.newEntity(PlainSchema.class); schema.setType(AttrSchemaType.Enum); schema.setKey("color"); @@ -166,7 +166,7 @@ public void checkForEnumType() { } @Test - public void checkForDropdownType() { + void checkForDropdownType() { PlainSchema schema = entityFactory.newEntity(PlainSchema.class); schema.setType(AttrSchemaType.Dropdown); schema.setKey("dropdown"); @@ -188,14 +188,14 @@ public void checkForDropdownType() { } @Test - public void saveInvalidSchema() { + void saveInvalidSchema() { PlainSchema schema = entityFactory.newEntity(PlainSchema.class); schema.setKey("username"); assertThrows(InvalidEntityException.class, () -> plainSchemaDAO.save(schema)); } @Test - public void delete() { + void delete() { PlainSchema firstname = plainSchemaDAO.findById("firstname").orElseThrow(); plainSchemaDAO.deleteById(firstname.getKey()); @@ -204,7 +204,7 @@ public void delete() { } @Test - public void issueSYNCOPE418() { + void issueSYNCOPE418() { PlainSchema schema = entityFactory.newEntity(PlainSchema.class); schema.setKey("http://schemas.examples.org/security/authorization/organizationUnit"); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PolicyTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PolicyTest.java index 7eb9ee156b1..967b8a9c61c 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PolicyTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PolicyTest.java @@ -62,7 +62,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class PolicyTest extends AbstractTest { +class PolicyTest extends AbstractTest { @Autowired private AnyTypeDAO anyTypeDAO; @@ -74,7 +74,7 @@ public class PolicyTest extends AbstractTest { private ImplementationDAO implementationDAO; @Test - public void findAll() { + void findAll() { assertEquals(17, policyDAO.count()); assertEquals(17, policyDAO.findAll().size()); @@ -89,7 +89,7 @@ public void findAll() { } @Test - public void findByKey() { + void findByKey() { PropagationPolicy propagationPolicy = policyDAO.findById( "89d322db-9878-420c-b49c-67be13df9a12", PropagationPolicy.class).orElseThrow(); assertEquals(BackOffStrategy.FIXED, propagationPolicy.getBackOffStrategy()); @@ -131,7 +131,7 @@ public void findByKey() { } @Test - public void createPropagation() { + void createPropagation() { long beforeCount = policyDAO.count(); PropagationPolicy propagationPolicy = entityFactory.newEntity(PropagationPolicy.class); @@ -151,7 +151,7 @@ public void createPropagation() { } @Test - public void createPull() { + void createPull() { InboundPolicy inboundPolicy = entityFactory.newEntity(InboundPolicy.class); inboundPolicy.setConflictResolutionAction(ConflictResolutionAction.IGNORE); inboundPolicy.setName("Pull policy"); @@ -195,7 +195,7 @@ public void createPull() { } @Test - public void createPush() { + void createPush() { PushPolicy pushPolicy = entityFactory.newEntity(PushPolicy.class); pushPolicy.setName("Push policy"); pushPolicy.setConflictResolutionAction(ConflictResolutionAction.IGNORE); @@ -239,7 +239,7 @@ public void createPush() { } @Test - public void createAccess() { + void createAccess() { long beforeCount = policyDAO.count(); AccessPolicy accessPolicy = entityFactory.newEntity(AccessPolicy.class); @@ -259,7 +259,7 @@ public void createAccess() { } @Test - public void createAuth() { + void createAuth() { long beforeCount = policyDAO.count(); AuthPolicy authPolicy = entityFactory.newEntity(AuthPolicy.class); @@ -280,7 +280,7 @@ public void createAuth() { } @Test - public void createAttrRelease() { + void createAttrRelease() { long beforeCount = policyDAO.count(); AttrReleasePolicy attrReleasePolicy = entityFactory.newEntity(AttrReleasePolicy.class); @@ -304,7 +304,7 @@ public void createAttrRelease() { } @Test - public void createTicketExpiration() { + void createTicketExpiration() { long beforeCount = policyDAO.count(); TicketExpirationPolicy ticketExpirationPolicy = entityFactory.newEntity(TicketExpirationPolicy.class); @@ -332,7 +332,7 @@ public void createTicketExpiration() { } @Test - public void update() { + void update() { PasswordPolicy policy = policyDAO.findById( "ce93fcda-dc3a-4369-a7b0-a6108c261c85", PasswordPolicy.class).orElseThrow(); assertEquals(1, policy.getRules().size()); @@ -362,7 +362,7 @@ public void update() { } @Test - public void delete() { + void delete() { Policy policy = policyDAO.findById("66691e96-285f-4464-bc19-e68384ea4c85").orElseThrow(); policyDAO.delete(policy); assertTrue(policyDAO.findById("66691e96-285f-4464-bc19-e68384ea4c85").isEmpty()); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RealmTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RealmTest.java index ad53b741b5f..b00bc5dc206 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RealmTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RealmTest.java @@ -45,7 +45,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class RealmTest extends AbstractTest { +class RealmTest extends AbstractTest { @Autowired private RealmDAO realmDAO; @@ -57,12 +57,12 @@ public class RealmTest extends AbstractTest { private PolicyDAO policyDAO; @Test - public void getRoot() { + void getRoot() { assertNotNull(realmDAO.getRoot()); } @Test - public void find() { + void find() { Realm realm = realmDAO.findById("e4c28e7a-9dbf-4ee7-9441-93812a0d4a28").orElseThrow(); assertEquals(SyncopeConstants.ROOT_REALM, realm.getName()); assertEquals(SyncopeConstants.ROOT_REALM, realm.getFullPath()); @@ -80,12 +80,12 @@ public void find() { } @Test - public void findInvalidPath() { + void findInvalidPath() { assertThrows(MalformedPathException.class, () -> realmSearchDAO.findByFullPath("even/two")); } @Test - public void findDescendants() { + void findDescendants() { List found = realmSearchDAO.findDescendants(SyncopeConstants.ROOT_REALM, SyncopeConstants.ROOT_REALM); assertEquals(4, found.size()); assertTrue(found.stream().allMatch(f -> SyncopeConstants.UUID_PATTERN.matcher(f.getKey()).matches())); @@ -94,7 +94,7 @@ public void findDescendants() { } @Test - public void findChildren() { + void findChildren() { List children = realmSearchDAO.findChildren( realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); assertEquals(2, children.size()); @@ -106,7 +106,7 @@ public void findChildren() { } @Test - public void findAll() { + void findAll() { List list = realmSearchDAO.findDescendants(realmDAO.getRoot().getFullPath(), null); assertNotNull(list); assertFalse(list.isEmpty()); @@ -122,7 +122,7 @@ public void findAll() { } @Test - public void save() { + void save() { Realm realm = entityFactory.newEntity(Realm.class); realm.setName("last"); realm.setParent(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); @@ -147,7 +147,7 @@ public void save() { } @Test - public void saveInvalidName() { + void saveInvalidName() { Realm realm = entityFactory.newEntity(Realm.class); realm.setName(" a name"); realm.setParent(realmDAO.getRoot()); @@ -161,7 +161,7 @@ public void saveInvalidName() { } @Test - public void saveNullParent() { + void saveNullParent() { Realm realm = entityFactory.newEntity(Realm.class); realm.setName("name"); realm.setParent(null); @@ -175,7 +175,7 @@ public void saveNullParent() { } @Test - public void delete() { + void delete() { Realm realm = entityFactory.newEntity(Realm.class); realm.setName("name"); realm.setParent(realmDAO.getRoot()); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RelationshipTypeTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RelationshipTypeTest.java index 28d69566681..d8669ede2b8 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RelationshipTypeTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RelationshipTypeTest.java @@ -37,7 +37,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class RelationshipTypeTest extends AbstractTest { +class RelationshipTypeTest extends AbstractTest { @Autowired private RelationshipTypeDAO relationshipTypeDAO; @@ -49,7 +49,7 @@ public class RelationshipTypeTest extends AbstractTest { private AnyTypeDAO anyTypeDAO; @Test - public void find() { + void find() { RelationshipType inclusion = relationshipTypeDAO.findById("inclusion").orElseThrow(); assertEquals("inclusion", inclusion.getKey()); assertEquals(anyTypeDAO.findById("PRINTER").orElseThrow(), inclusion.getLeftEndAnyType()); @@ -57,7 +57,7 @@ public void find() { } @Test - public void findByEndAnyType() { + void findByEndAnyType() { List relationshipTypes = relationshipTypeDAO.findByEndAnyType(anyTypeDAO.findById("PRINTER").orElseThrow()); assertEquals(2, relationshipTypes.size()); @@ -66,13 +66,13 @@ public void findByEndAnyType() { } @Test - public void findAll() { + void findAll() { List list = relationshipTypeDAO.findAll(); assertFalse(list.isEmpty()); } @Test - public void save() { + void save() { RelationshipType newType = entityFactory.newEntity(RelationshipType.class); newType.setKey("new type"); newType.setDescription("description"); @@ -85,7 +85,7 @@ public void save() { } @Test - public void saveInvalidName() { + void saveInvalidName() { assertThrows(InvalidEntityException.class, () -> { RelationshipType newType = entityFactory.newEntity(RelationshipType.class); newType.setKey("membership"); @@ -94,7 +94,7 @@ public void saveInvalidName() { } @Test - public void delete() { + void delete() { RelationshipType type = relationshipTypeDAO.findById("neighborhood").orElseThrow(); relationshipTypeDAO.deleteById(type.getKey()); @@ -103,7 +103,7 @@ public void delete() { } @Test - public void deleteOnAnyObject() { + void deleteOnAnyObject() { RelationshipType neighborhood = relationshipTypeDAO.findById("inclusion").orElseThrow(); AnyObject anyObject = anyObjectDAO.findById("fc6dbc3a-6c07-4965-8781-921e7401a4a5").orElseThrow(); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RemediationTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RemediationTest.java index c91297f3ff5..00687c7ebf9 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RemediationTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RemediationTest.java @@ -44,7 +44,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class RemediationTest extends AbstractTest { +class RemediationTest extends AbstractTest { @Autowired private RemediationDAO remediationDAO; @@ -56,13 +56,13 @@ public class RemediationTest extends AbstractTest { private TaskDAO taskDAO; @Test - public void findAll() { + void findAll() { List remediations = remediationDAO.findAll(null, null, Pageable.unpaged()); assertTrue(remediations.isEmpty()); } @Test - public void createMissingPayload() { + void createMissingPayload() { Remediation remediation = entityFactory.newEntity(Remediation.class); remediation.setAnyType(anyTypeDAO.findById("PRINTER").orElseThrow()); remediation.setOperation(ResourceOperation.CREATE); @@ -84,7 +84,7 @@ public void createMissingPayload() { } @Test - public void createWrongPayload() { + void createWrongPayload() { Remediation remediation = entityFactory.newEntity(Remediation.class); remediation.setAnyType(anyTypeDAO.findById("PRINTER").orElseThrow()); remediation.setOperation(ResourceOperation.CREATE); @@ -107,7 +107,7 @@ public void createWrongPayload() { } @Test - public void create() { + void create() { Remediation remediation = entityFactory.newEntity(Remediation.class); remediation.setAnyType(anyTypeDAO.findById("PRINTER").orElseThrow()); remediation.setOperation(ResourceOperation.CREATE); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ReportTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ReportTest.java index f2acfccb249..40e5e731546 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ReportTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ReportTest.java @@ -34,7 +34,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class ReportTest extends AbstractTest { +class ReportTest extends AbstractTest { @Autowired private ReportDAO reportDAO; @@ -43,7 +43,7 @@ public class ReportTest extends AbstractTest { private ImplementationDAO implementationDAO; @Test - public void find() { + void find() { Report report = reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").orElseThrow(); assertNotNull(report); @@ -51,14 +51,14 @@ public void find() { } @Test - public void findAll() { + void findAll() { List reports = reportDAO.findAll(); assertNotNull(reports); assertEquals(1, reports.size()); } @Test - public void save() { + void save() { int beforeCount = reportDAO.findAll().size(); Report report = entityFactory.newEntity(Report.class); @@ -77,7 +77,7 @@ public void save() { } @Test - public void delete() { + void delete() { Report report = reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").orElseThrow(); assertNotNull(report); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ResourceTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ResourceTest.java index c503a795a2d..19f45245996 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ResourceTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ResourceTest.java @@ -52,13 +52,13 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class ResourceTest extends AbstractTest { +class ResourceTest extends AbstractTest { @Autowired private ExternalResourceDAO resourceDAO; @Test - public void findById() { + void findById() { ExternalResource resource = resourceDAO.findById("ws-target-resource-1").orElseThrow(); ConnInstance connector = resource.getConnector(); @@ -81,19 +81,19 @@ public void findById() { } @Test - public void propagationActions() { + void propagationActions() { ExternalResource resource = resourceDAO.findById("resource-ldap").orElseThrow(); assertEquals(2, resource.getPropagationActions().size()); } @Test - public void findWithOrgUnit() { + void findWithOrgUnit() { ExternalResource resource = resourceDAO.findById("resource-ldap-orgunit").orElseThrow(); assertNotNull(resource.getOrgUnit()); } @Test - public void findAll() { + void findAll() { List authorities = IdMEntitlement.values().stream(). map(entitlement -> new SyncopeGrantedAuthority(entitlement, SyncopeConstants.ROOT_REALM)). collect(Collectors.toList()); @@ -114,14 +114,14 @@ public void findAll() { } @Test - public void getConnObjectKey() { + void getConnObjectKey() { ExternalResource resource = resourceDAO.findById("ws-target-resource-2").orElseThrow(); assertEquals("fullname", resource.getProvisionByAnyType(AnyTypeKind.USER.name()).orElseThrow(). getMapping().getConnObjectKeyItem().orElseThrow().getIntAttrName()); } @Test - public void save() { + void save() { ExternalResource resource = entityFactory.newEntity(ExternalResource.class); resource.setKey("ws-target-resource-basic-save"); resource.setPropagationPriority(2); @@ -155,7 +155,7 @@ public void save() { } @Test - public void saveInvalidMappingIntAttr() { + void saveInvalidMappingIntAttr() { assertThrows(InvalidEntityException.class, () -> { ExternalResource resource = entityFactory.newEntity(ExternalResource.class); resource.setKey("ws-target-resource-basic-save-invalid"); @@ -181,7 +181,7 @@ public void saveInvalidMappingIntAttr() { } @Test - public void saveInvalidMappingExtAttr() { + void saveInvalidMappingExtAttr() { assertThrows(InvalidEntityException.class, () -> { ExternalResource resource = entityFactory.newEntity(ExternalResource.class); resource.setKey("ws-target-resource-basic-save-invalid"); @@ -211,7 +211,7 @@ public void saveInvalidMappingExtAttr() { } @Test - public void saveInvalidProvision() { + void saveInvalidProvision() { assertThrows(InvalidEntityException.class, () -> { ExternalResource resource = entityFactory.newEntity(ExternalResource.class); resource.setKey("invalidProvision"); @@ -244,7 +244,7 @@ public void saveInvalidProvision() { } @Test - public void saveWithGroupMappingType() { + void saveWithGroupMappingType() { ExternalResource resource = entityFactory.newEntity(ExternalResource.class); resource.setKey("ws-target-resource-basic-save-invalid"); @@ -286,7 +286,7 @@ public void saveWithGroupMappingType() { } @Test - public void delete() { + void delete() { ExternalResource resource = resourceDAO.findById("ws-target-resource-2").orElseThrow(); resourceDAO.deleteById(resource.getKey()); @@ -295,7 +295,7 @@ public void delete() { } @Test - public void issueSYNCOPE418() { + void issueSYNCOPE418() { ExternalResource resource = entityFactory.newEntity(ExternalResource.class); resource.setKey("http://schemas.examples.org/security/authorization/organizationUnit"); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RoleTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RoleTest.java index bd67f8ecc72..beee91f2833 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RoleTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RoleTest.java @@ -35,7 +35,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class RoleTest extends AbstractTest { +class RoleTest extends AbstractTest { @Autowired private RoleDAO roleDAO; @@ -47,7 +47,7 @@ public class RoleTest extends AbstractTest { private RealmSearchDAO realmSearchDAO; @Test - public void find() { + void find() { Role role = roleDAO.findById("User manager").orElseThrow(); assertNotNull(role.getKey()); assertFalse(role.getRealms().isEmpty()); @@ -56,7 +56,7 @@ public void find() { } @Test - public void findAll() { + void findAll() { List list = roleDAO.findAll(); assertNotNull(list); assertFalse(list.isEmpty()); @@ -64,7 +64,7 @@ public void findAll() { } @Test - public void save() { + void save() { Role role = entityFactory.newEntity(Role.class); role.setKey("new"); role.add(realmDAO.getRoot()); @@ -77,7 +77,7 @@ public void save() { } @Test - public void delete() { + void delete() { assertTrue(roleDAO.findById("Other").isPresent()); roleDAO.deleteById("Other"); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2IdPEntityTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2IdPEntityTest.java index 1160db18908..29b161bd610 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2IdPEntityTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2IdPEntityTest.java @@ -32,7 +32,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class SAML2IdPEntityTest extends AbstractTest { +class SAML2IdPEntityTest extends AbstractTest { @Autowired private SAML2IdPEntityDAO saml2IdPEntityDAO; @@ -53,7 +53,7 @@ private SAML2IdPEntity create(final String owner) { } @Test - public void find() { + void find() { create("Syncope"); assertTrue(saml2IdPEntityDAO.findById("Syncope").isPresent()); @@ -62,12 +62,12 @@ public void find() { } @Test - public void save() { + void save() { create("SyncopeCreate"); } @Test - public void update() { + void update() { SAML2IdPEntity entity = create("SyncopeUpdate"); assertNotNull(entity); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2SPTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2SPTest.java index 8f55c29de8d..a842f4649be 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2SPTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2SPTest.java @@ -35,13 +35,13 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class SAML2SPTest extends AbstractClientAppTest { +class SAML2SPTest extends AbstractClientAppTest { @Autowired private SAML2SPClientAppDAO saml2spDAO; @Test - public void find() { + void find() { long beforeCount = saml2spDAO.count(); SAML2SPClientApp sp = entityFactory.newEntity(SAML2SPClientApp.class); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SecurityQuestionTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SecurityQuestionTest.java index d6aa353fbdb..67bb955bee3 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SecurityQuestionTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SecurityQuestionTest.java @@ -31,26 +31,26 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class SecurityQuestionTest extends AbstractTest { +class SecurityQuestionTest extends AbstractTest { @Autowired private SecurityQuestionDAO securityQuestionDAO; @Test - public void find() { + void find() { SecurityQuestion question = securityQuestionDAO.findById("887028ea-66fc-41e7-b397-620d7ea6dfbb").orElseThrow(); assertNotNull(question.getContent()); } @Test - public void findAll() { + void findAll() { List securityQuestions = securityQuestionDAO.findAll(); assertNotNull(securityQuestions); assertFalse(securityQuestions.isEmpty()); } @Test - public void save() { + void save() { SecurityQuestion securityQuestion = entityFactory.newEntity(SecurityQuestion.class); securityQuestion.setContent("What is your favorite pet's name?"); @@ -60,7 +60,7 @@ public void save() { } @Test - public void delete() { + void delete() { assertTrue(securityQuestionDAO.findById("887028ea-66fc-41e7-b397-620d7ea6dfbb").isPresent()); securityQuestionDAO.deleteById("887028ea-66fc-41e7-b397-620d7ea6dfbb"); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/TaskExecTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/TaskExecTest.java index d6d27bb91e3..bb481432cec 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/TaskExecTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/TaskExecTest.java @@ -38,7 +38,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class TaskExecTest extends AbstractTest { +class TaskExecTest extends AbstractTest { @Autowired private TaskExecDAO taskExecDAO; @@ -50,7 +50,7 @@ public class TaskExecTest extends AbstractTest { private TaskUtilsFactory taskUtilsFactory; @Test - public void findAll() { + void findAll() { PropagationTask task = (PropagationTask) taskDAO.findById( TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); assertNotNull(task); @@ -63,7 +63,7 @@ public void findAll() { } @Test - public void findLatestStarted() { + void findLatestStarted() { PropagationTask task = (PropagationTask) taskDAO.findById( TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); assertNotNull(task); @@ -73,7 +73,7 @@ public void findLatestStarted() { } @Test - public void issueSYNCOPE214() { + void issueSYNCOPE214() { PropagationTask task = (PropagationTask) taskDAO.findById( TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); assertNotNull(task); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/TaskTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/TaskTest.java index 8659f57cc23..27e27221cb5 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/TaskTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/TaskTest.java @@ -67,7 +67,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class TaskTest extends AbstractTest { +class TaskTest extends AbstractTest { @Autowired private TaskDAO taskDAO; @@ -82,14 +82,14 @@ public class TaskTest extends AbstractTest { private ImplementationDAO implementationDAO; @Test - public void findByName() { + void findByName() { Optional task = taskDAO.findByName(TaskType.SCHEDULED, "SampleJob Task"); assertTrue(task.isPresent()); assertEquals(taskDAO.findById(TaskType.SCHEDULED, "e95555d2-1b09-42c8-b25b-f4c4ec597979"), task); } @Test - public void findWithoutExecs() { + void findWithoutExecs() { assertEquals(3, taskDAO.countToExec(TaskType.PROPAGATION)); List tasks = taskDAO.findToExec(TaskType.PROPAGATION, Pageable.unpaged()); @@ -98,7 +98,7 @@ public void findWithoutExecs() { } @Test - public void findPaginated() { + void findPaginated() { List tasks = taskDAO.findAll( TaskType.PROPAGATION, null, null, null, null, PageRequest.of(0, 2)); assertNotNull(tasks); @@ -124,7 +124,7 @@ public void findPaginated() { } @Test - public void findAll() { + void findAll() { assertEquals(6, taskDAO.findAll(TaskType.PROPAGATION).size()); assertEquals(1, taskDAO.findAll(TaskType.NOTIFICATION).size()); assertEquals(3, taskDAO.findAll(TaskType.SCHEDULED).size()); @@ -147,7 +147,7 @@ public void findAll() { } @Test - public void savePropagationTask() { + void savePropagationTask() { ExternalResource resource = resourceDAO.findById("ws-target-resource-1").orElseThrow(); PropagationTask task = entityFactory.newEntity(PropagationTask.class); @@ -170,7 +170,7 @@ public void savePropagationTask() { } @Test - public void saveMacroTask() throws Exception { + void saveMacroTask() throws Exception { MacroTask task = entityFactory.newEntity(MacroTask.class); task.setRealm(realmDAO.getRoot()); task.setJobDelegate(implementationDAO.findById("MacroJobDelegate").orElseThrow()); @@ -227,7 +227,7 @@ public void saveMacroTask() throws Exception { } @Test - public void delete() { + void delete() { PropagationTask task = (PropagationTask) taskDAO.findById( TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); assertNotNull(task); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/UserTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/UserTest.java index 7554af6e655..6e93e342ebd 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/UserTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/UserTest.java @@ -43,7 +43,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class UserTest extends AbstractTest { +class UserTest extends AbstractTest { @Autowired private PasswordGenerator passwordGenerator; @@ -67,7 +67,7 @@ public class UserTest extends AbstractTest { private EncryptorManager encryptorManager; @Test - public void find() { + void find() { User user = userDAO.findById("823074dc-d280-436d-a7dd-07399fae48ec").orElseThrow(); assertEquals("puccini", user.getUsername()); assertFalse(user.isSuspended()); @@ -83,12 +83,12 @@ public void find() { } @Test - public void findUsername() { + void findUsername() { assertEquals("puccini", userDAO.findUsername("823074dc-d280-436d-a7dd-07399fae48ec").orElseThrow()); } @Test - public void findByToken() { + void findByToken() { assertTrue(userDAO.findByToken("WRONG TOKEN").isEmpty()); User user = userDAO.findById("823074dc-d280-436d-a7dd-07399fae48ec").orElseThrow(); @@ -105,32 +105,32 @@ public void findByToken() { } @Test - public void findAll() { + void findAll() { List users = userDAO.findAll(); assertEquals(5, users.size()); } @Test - public void count() { + void count() { long count = userDAO.count(); assertNotNull(count); assertEquals(5, count); } @Test - public void findByKey() { + void findByKey() { assertTrue(userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").isPresent()); } @Test - public void findByUsername() { + void findByUsername() { assertTrue(userDAO.findByUsername("rossini").isPresent()); assertTrue(userDAO.findByUsername("vivaldi").isPresent()); assertTrue(userDAO.findByUsername("user6").isEmpty()); } @Test - public void save() { + void save() { User user = entityFactory.newEntity(User.class); user.setUsername("username"); user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); @@ -144,7 +144,7 @@ public void save() { } @Test - public void delete() { + void delete() { User user = userDAO.findById("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee").orElseThrow(); userDAO.deleteById(user.getKey()); @@ -153,7 +153,7 @@ public void delete() { } @Test - public void issue237() { + void issue237() { User user = entityFactory.newEntity(User.class); user.setUsername("username"); user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); @@ -168,7 +168,7 @@ public void issue237() { } @Test - public void issueSYNCOPE391() { + void issueSYNCOPE391() { User user = entityFactory.newEntity(User.class); user.setUsername("username"); user.setCipherAlgorithm(CipherAlgorithm.AES); @@ -181,7 +181,7 @@ public void issueSYNCOPE391() { } @Test - public void testPasswordGenerator() { + void testPasswordGenerator() { String password = passwordGenerator.generate( resourceDAO.findById("ws-target-resource-nopropagation").orElseThrow(), List.of(realmDAO.getRoot())); @@ -193,7 +193,7 @@ public void testPasswordGenerator() { } @Test - public void passwordGeneratorFailing() { + void passwordGeneratorFailing() { assertThrows(IllegalArgumentException.class, () -> { String password = passwordGenerator.generate( resourceDAO.findById("ws-target-resource-nopropagation").orElseThrow(), @@ -209,7 +209,7 @@ public void passwordGeneratorFailing() { } @Test - public void issueSYNCOPE1666() { + void issueSYNCOPE1666() { User user = entityFactory.newEntity(User.class); user.setUsername("username"); user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); @@ -229,7 +229,7 @@ public void issueSYNCOPE1666() { } @Test - public void issueSYNCOPE1937() throws Exception { + void issueSYNCOPE1937() throws Exception { User user = entityFactory.newEntity(User.class); user.setUsername("username"); user.setRealm(realmDAO.getRoot()); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/WAConfigTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/WAConfigTest.java index 377b01a2990..76ed5ecd508 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/WAConfigTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/WAConfigTest.java @@ -37,7 +37,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class WAConfigTest extends AbstractTest { +class WAConfigTest extends AbstractTest { @Autowired private WAConfigDAO configDAO; @@ -46,7 +46,7 @@ public class WAConfigTest extends AbstractTest { private Neo4jTemplate neo4jTemplate; @BeforeEach - public void beforeEach() { + void beforeEach() { neo4jTemplate.deleteAll(Neo4jWAConfigEntry.class); } @@ -62,32 +62,32 @@ private WAConfigEntry create(final String name, final List value) { } @Test - public void saveCommaSeparatedValueStrings() { + void saveCommaSeparatedValueStrings() { create("system.example.key[0]", Arrays.asList("value1", "value2", "value3")); assertFalse(configDAO.findAll().isEmpty()); } @Test - public void saveNumbers() { + void saveNumbers() { create("system.example.key[0]", List.of("1984")); assertFalse(configDAO.findAll().isEmpty()); } @Test - public void saveCollection() { + void saveCollection() { WAConfigEntry entry = create("system.example.key[0]", new ArrayList<>(Arrays.asList("1", "2"))); assertNotNull(entry.getValues()); assertFalse(configDAO.findAll().isEmpty()); } @Test - public void saveList() { + void saveList() { create("system.example.key[0].key1", List.of("value1")); assertFalse(configDAO.findAll().isEmpty()); } @Test - public void update() { + void update() { WAConfigEntry entry = create("system.syncope.key[0]", Arrays.asList("1", "2", "3", "4")); assertNotNull(entry); entry.setValues(List.of("v1")); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnySearchTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnySearchTest.java index a89ebc2523a..3cbb98f6acf 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnySearchTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnySearchTest.java @@ -58,7 +58,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AnySearchTest extends AbstractTest { +class AnySearchTest extends AbstractTest { @Autowired private UserDAO userDAO; @@ -85,7 +85,7 @@ public class AnySearchTest extends AbstractTest { private PlainAttrValidationManager validator; @Test - public void searchAsGroupOwner() { + void searchAsGroupOwner() { // 1. define rossini as member of director User rossini = userDAO.findByUsername("rossini").orElseThrow(); @@ -122,7 +122,7 @@ public void searchAsGroupOwner() { } @Test - public void searchByMembershipAttribute() { + void searchByMembershipAttribute() { AnyTypeCond typeCond = new AnyTypeCond(); typeCond.setAnyTypeKey("PRINTER"); @@ -170,7 +170,7 @@ public void searchByMembershipAttribute() { } @Test - public void issueSYNCOPE95() { + void issueSYNCOPE95() { groupDAO.findAll().forEach(group -> groupDAO.deleteById(group.getKey())); AttrCond coolLeafCond = new AttrCond(AttrCond.Type.EQ); @@ -188,7 +188,7 @@ public void issueSYNCOPE95() { } @Test - public void issueSYNCOPE800() { + void issueSYNCOPE800() { // create derived attribute (literal as prefix) DerSchema prefix = entityFactory.newEntity(DerSchema.class); prefix.setKey("kprefix"); @@ -223,7 +223,7 @@ public void issueSYNCOPE800() { } @Test - public void issueSYNCOPE1417() { + void issueSYNCOPE1417() { AnyCond usernameLeafCond = new AnyCond(AnyCond.Type.EQ); usernameLeafCond.setSchema("username"); usernameLeafCond.setExpression("rossini"); @@ -246,7 +246,7 @@ public void issueSYNCOPE1417() { } @Test - public void issueSYNCOPE1512() { + void issueSYNCOPE1512() { Group group = groupDAO.findByName("root").orElseThrow(); // non unique @@ -281,7 +281,7 @@ public void issueSYNCOPE1512() { } @Test - public void issueSYNCOPE1790() { + void issueSYNCOPE1790() { // 0. search by email AttrCond emailCond = new AttrCond(AttrCond.Type.EQ); emailCond.setSchema("email"); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnyTypeClassTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnyTypeClassTest.java index c6eed794e3c..ffb0ef9a5b2 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnyTypeClassTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnyTypeClassTest.java @@ -33,7 +33,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AnyTypeClassTest extends AbstractTest { +class AnyTypeClassTest extends AbstractTest { @Autowired private PlainSchemaDAO plainSchemaDAO; @@ -42,7 +42,7 @@ public class AnyTypeClassTest extends AbstractTest { private AnyTypeClassDAO anyTypeClassDAO; @Test - public void create() { + void create() { PlainSchema newSchema = entityFactory.newEntity(PlainSchema.class); newSchema.setKey("new_plain_schema"); newSchema.setType(AttrSchemaType.String); @@ -67,7 +67,7 @@ public void create() { } @Test - public void delete() { + void delete() { AnyTypeClass minimalUser = anyTypeClassDAO.findById("minimal user").orElseThrow(); PlainSchema surname = plainSchemaDAO.findById("surname").orElseThrow(); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnyTypeTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnyTypeTest.java index 1b86adcddc2..697819770a9 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnyTypeTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnyTypeTest.java @@ -33,7 +33,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class AnyTypeTest extends AbstractTest { +class AnyTypeTest extends AbstractTest { @Autowired private AnyTypeDAO anyTypeDAO; @@ -42,7 +42,7 @@ public class AnyTypeTest extends AbstractTest { private AnyTypeClassDAO anyTypeClassDAO; @Test - public void findByClassesContaining() { + void findByClassesContaining() { List found = anyTypeDAO.findByClassesContaining( anyTypeClassDAO.findById("minimal user").orElseThrow()); assertEquals(List.of(anyTypeDAO.getUser()), found); @@ -58,7 +58,7 @@ public void findByClassesContaining() { } @Test - public void manyToMany() { + void manyToMany() { AnyTypeClass other = anyTypeClassDAO.findById("other").orElseThrow(); AnyType user = anyTypeDAO.getUser(); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ConnInstanceTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ConnInstanceTest.java index 1c14e6c729d..b8998c52bd1 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ConnInstanceTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ConnInstanceTest.java @@ -35,7 +35,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class ConnInstanceTest extends AbstractTest { +class ConnInstanceTest extends AbstractTest { @Autowired private ExternalResourceDAO resourceDAO; @@ -44,7 +44,7 @@ public class ConnInstanceTest extends AbstractTest { private ConnInstanceDAO connInstanceDAO; @Test - public void deleteCascade() { + void deleteCascade() { ConnInstance connInstance = connInstanceDAO.findById("fcf9f2b0-f7d6-42c9-84a6-61b28255a42b").orElseThrow(); List resources = resourceDAO.findByConnInstance(connInstance.getKey()); @@ -60,7 +60,7 @@ public void deleteCascade() { } @Test - public void issue176() { + void issue176() { ConnInstance connInstance = connInstanceDAO.findById("fcf9f2b0-f7d6-42c9-84a6-61b28255a42b").orElseThrow(); assertTrue(connInstance.getCapabilities().isEmpty()); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/DelegationTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/DelegationTest.java index c6ebbabf544..5ff1f606661 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/DelegationTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/DelegationTest.java @@ -32,7 +32,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class DelegationTest extends AbstractTest { +class DelegationTest extends AbstractTest { @Autowired private DelegationDAO delegationDAO; @@ -41,7 +41,7 @@ public class DelegationTest extends AbstractTest { private UserDAO userDAO; @Test - public void findValidFor() { + void findValidFor() { Delegation delegation = entityFactory.newEntity(Delegation.class); delegation.setDelegating(userDAO.findByUsername("bellini").orElseThrow()); delegation.setDelegated(userDAO.findByUsername("rossini").orElseThrow()); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/FIQLQueryTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/FIQLQueryTest.java index e5173c5ac81..19aa5441183 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/FIQLQueryTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/FIQLQueryTest.java @@ -30,7 +30,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class FIQLQueryTest extends AbstractTest { +class FIQLQueryTest extends AbstractTest { @Autowired private FIQLQueryDAO fiqlQueryDAO; @@ -39,7 +39,7 @@ public class FIQLQueryTest extends AbstractTest { private UserDAO userDAO; @Test - public void findByOwner() { + void findByOwner() { FIQLQuery query = entityFactory.newEntity(FIQLQuery.class); query.setOwner(userDAO.findByUsername("rossini").orElseThrow()); query.setName("name"); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/GroupTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/GroupTest.java index 1bb2e80edce..c34fe405acc 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/GroupTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/GroupTest.java @@ -54,7 +54,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class GroupTest extends AbstractTest { +class GroupTest extends AbstractTest { @Autowired private AnyTypeDAO anyTypeDAO; @@ -90,7 +90,7 @@ public class GroupTest extends AbstractTest { private PlainAttrValidationManager validator; @Test - public void findByResourcesContaining() { + void findByResourcesContaining() { List found = groupDAO.findByResourcesContaining(resourceDAO.findById("resource-csv").orElseThrow()); assertEquals(2, found.size()); assertTrue(found.contains(groupDAO.findById("0626100b-a4ba-4e00-9971-86fad52a6216").orElseThrow())); @@ -101,7 +101,7 @@ public void findByResourcesContaining() { } @Test - public void uMemberships() { + void uMemberships() { List memberships = groupDAO.findUMemberships( groupDAO.findById("37d15e4c-cdc1-460b-a591-8505c8133806").orElseThrow(), Pageable.unpaged()); assertEquals(2, memberships.size()); @@ -125,7 +125,7 @@ public void uMemberships() { } @Test - public void saveWithTwoManagers() { + void saveWithTwoManagers() { assertThrows(InvalidEntityException.class, () -> { Group root = groupDAO.findByName("root").orElseThrow(); @@ -142,7 +142,7 @@ public void saveWithTwoManagers() { } @Test - public void create() { + void create() { Group group = entityFactory.newEntity(Group.class); group.setRealm(realmDAO.getRoot()); group.setName("new"); @@ -163,7 +163,7 @@ public void create() { } @Test - public void createWithInternationalCharacters() { + void createWithInternationalCharacters() { Group group = entityFactory.newEntity(Group.class); group.setName("räksmörgås"); group.setRealm(realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); @@ -172,7 +172,7 @@ public void createWithInternationalCharacters() { } @Test - public void delete() { + void delete() { Collection groups = userDAO.findAllGroups(userDAO.findByUsername("verdi").orElseThrow()); assertTrue(groups.stream().anyMatch(g -> "b1f7c12d-ec83-441f-a50e-1691daaedf3b".equals(g.getKey()))); int before = userDAO.findAllGroups(userDAO.findByUsername("verdi").orElseThrow()).size(); @@ -185,7 +185,7 @@ public void delete() { } @Test - public void relationships() { + void relationships() { RelationshipType groupType = entityFactory.newEntity(RelationshipType.class); groupType.setKey("group type"); groupType.setLeftEndAnyType(anyTypeDAO.getGroup()); @@ -210,7 +210,7 @@ public void relationships() { } @Test - public void issueSYNCOPE1512() { + void issueSYNCOPE1512() { Group group = groupDAO.findByName("root").orElseThrow(); // non unique diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/PlainSchemaTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/PlainSchemaTest.java index 766bc795a56..49678d2a6d8 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/PlainSchemaTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/PlainSchemaTest.java @@ -54,7 +54,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class PlainSchemaTest extends AbstractTest { +class PlainSchemaTest extends AbstractTest { @Autowired private UserDAO userDAO; @@ -91,7 +91,7 @@ public static void unsetAuthContext() { } @Test - public void findByAnyTypeClasses() { + void findByAnyTypeClasses() { List found = plainSchemaDAO.findByAnyTypeClasses( List.of(anyTypeClassDAO.findById("minimal user").orElseThrow())); assertEquals(5, found.size()); @@ -102,7 +102,7 @@ public void findByAnyTypeClasses() { } @Test - public void checkIdUniqueness() { + void checkIdUniqueness() { assertNotNull(derSchemaDAO.findById("cn").orElseThrow()); PlainSchema schema = entityFactory.newEntity(PlainSchema.class); @@ -126,7 +126,7 @@ private List getMappingItems(final String intAttrName) { } @Test - public void deleteFullname() { + void deleteFullname() { // fullname is mapped as ConnObjectKey for various resources, need to swap it otherwise validation errors // will be raised resourceDAO.findAll().stream(). @@ -159,7 +159,7 @@ public void deleteFullname() { } @Test - public void deleteSurname() { + void deleteSurname() { // search for user schema surname PlainSchema schema = plainSchemaDAO.findById("surname").orElseThrow(); @@ -178,7 +178,7 @@ public void deleteSurname() { } @Test - public void deleteFirstname() { + void deleteFirstname() { int pre = resourceDAO.findById("resource-db-pull").orElseThrow(). getProvisionByAnyType(AnyTypeKind.USER.name()).get().getMapping().getItems().size(); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/PolicyTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/PolicyTest.java index a7945266f05..6bc668be894 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/PolicyTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/PolicyTest.java @@ -45,7 +45,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class PolicyTest extends AbstractClientAppTest { +class PolicyTest extends AbstractClientAppTest { @Autowired private OIDCRPClientAppDAO oidcRelyingPartyDAO; @@ -60,7 +60,7 @@ public class PolicyTest extends AbstractClientAppTest { private ImplementationDAO implementationDAO; @Test - public void authPolicyCanBeNull() { + void authPolicyCanBeNull() { Realm realm = realmSearchDAO.findByFullPath("/odd").orElseThrow(); // Create new client app and assign policy @@ -76,7 +76,7 @@ public void authPolicyCanBeNull() { } @Test - public void removePolicyFromRealm() { + void removePolicyFromRealm() { AuthPolicy authPolicy = buildAndSaveAuthPolicy(); AccessPolicy accessPolicy = buildAndSaveAccessPolicy(); AttrReleasePolicy attrPolicy = buildAndSaveAttrRelPolicy(); @@ -106,7 +106,7 @@ public void removePolicyFromRealm() { } @Test - public void removePolicyFromApps() { + void removePolicyFromApps() { // Create new policy AccessPolicy accessPolicy = buildAndSaveAccessPolicy(); AuthPolicy authPolicy = buildAndSaveAuthPolicy(); @@ -139,7 +139,7 @@ public void removePolicyFromApps() { } @Test - public void addAndRemoveAccountPolicyRule() { + void addAndRemoveAccountPolicyRule() { Implementation implementation = entityFactory.newEntity(Implementation.class); implementation.setKey(UUID.randomUUID().toString()); implementation.setEngine(ImplementationEngine.JAVA); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/RealmSearchTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/RealmSearchTest.java index 0e1c0d75b2e..835ff662b47 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/RealmSearchTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/RealmSearchTest.java @@ -47,7 +47,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class RealmSearchTest extends AbstractTest { +class RealmSearchTest extends AbstractTest { @Autowired private AnyTypeClassDAO anyTypeClassDAO; @@ -101,7 +101,7 @@ void prepare() { } @Test - public void byName() { + void byName() { AnyCond name = new AnyCond(AttrCond.Type.EQ); name.setSchema("name"); name.setExpression("two"); @@ -115,7 +115,7 @@ public void byName() { } @Test - public void byAttr() { + void byAttr() { AttrCond attrEq = new AttrCond(AttrCond.Type.EQ); attrEq.setSchema("aLong"); attrEq.setExpression("42"); @@ -170,7 +170,7 @@ public void byAttr() { } @Test - public void byNameAndAttr() { + void byNameAndAttr() { AnyCond name = new AnyCond(AttrCond.Type.EQ); name.setSchema("name"); name.setExpression("two"); @@ -189,7 +189,7 @@ public void byNameAndAttr() { } @Test - public void and() { + void and() { AttrCond cond1 = new AttrCond(AttrCond.Type.EQ); cond1.setSchema("aLong"); cond1.setExpression("42"); @@ -204,7 +204,7 @@ public void and() { } @Test - public void auxClass() { + void auxClass() { AuxClassCond auxClassCond = new AuxClassCond(); auxClassCond.setAuxClass("other"); @@ -218,7 +218,7 @@ public void auxClass() { } @Test - public void resource() { + void resource() { ResourceCond resourceCond = new ResourceCond(); resourceCond.setResource("resource-ldap-orgunit"); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/RealmTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/RealmTest.java index 941889f3cbb..d71b011eb70 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/RealmTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/RealmTest.java @@ -42,7 +42,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class RealmTest extends AbstractTest { +class RealmTest extends AbstractTest { @Autowired private AnyTypeClassDAO anyTypeClassDAO; @@ -66,7 +66,7 @@ public class RealmTest extends AbstractTest { private PlainAttrValidationManager validator; @Test - public void plainAttrs() { + void plainAttrs() { Realm realm = realmSearchDAO.findByFullPath("/odd").orElseThrow(); assertTrue(realm.getAnyTypeClasses().isEmpty()); assertTrue(realm.getPlainAttrs().isEmpty()); @@ -91,7 +91,7 @@ public void plainAttrs() { } @Test - public void delete() { + void delete() { Realm realm = realmSearchDAO.findByFullPath("/odd").orElseThrow(); // need to remove this group in order to remove the realm, which is otherwise empty @@ -111,7 +111,7 @@ public void delete() { } @Test - public void addAndRemoveLogicActions() { + void addAndRemoveLogicActions() { Implementation implementation = entityFactory.newEntity(Implementation.class); implementation.setKey(UUID.randomUUID().toString()); implementation.setEngine(ImplementationEngine.JAVA); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ReportTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ReportTest.java index fe9bc07cf4c..dd1f047ba09 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ReportTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ReportTest.java @@ -38,7 +38,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class ReportTest extends AbstractTest { +class ReportTest extends AbstractTest { @Autowired private ReportDAO reportDAO; @@ -50,7 +50,7 @@ public class ReportTest extends AbstractTest { private ImplementationDAO implementationDAO; @Test - public void find() { + void find() { Report report = reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").orElseThrow(); assertNotNull(report.getExecs()); @@ -59,7 +59,7 @@ public void find() { } @Test - public void saveWithExistingName() { + void saveWithExistingName() { assertThrows(DataIntegrityViolationException.class, () -> { Report report = reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").orElseThrow(); @@ -77,7 +77,7 @@ public void saveWithExistingName() { } @Test - public void save() { + void save() { Report report = reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").orElseThrow(); assertEquals(1, report.getExecs().size()); @@ -96,7 +96,7 @@ public void save() { } @Test - public void deleteReport() { + void deleteReport() { reportDAO.deleteById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b"); assertTrue(reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").isEmpty()); @@ -104,7 +104,7 @@ public void deleteReport() { } @Test - public void deleteReportExecution() { + void deleteReportExecution() { ReportExec execution = reportExecDAO.findById("c13f39c5-0d35-4bff-ba79-3cd5de940369").orElseThrow(); int executionNumber = execution.getReport().getExecs().size(); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ResourceTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ResourceTest.java index cb21cfaec92..1f9dd018512 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ResourceTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ResourceTest.java @@ -56,7 +56,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class ResourceTest extends AbstractTest { +class ResourceTest extends AbstractTest { @Autowired private ExternalResourceDAO resourceDAO; @@ -77,7 +77,7 @@ public class ResourceTest extends AbstractTest { private ImplementationDAO implementationDAO; @Test - public void accountPolicy() { + void accountPolicy() { ExternalResource resource = resourceDAO.findById("resource-testdb").orElseThrow(); assertEquals( policyDAO.findById("20ab5a8c-4b0c-432c-b957-f7fb9784d9f7").orElseThrow(), @@ -85,14 +85,14 @@ public void accountPolicy() { } @Test - public void findByConnInstance() { + void findByConnInstance() { List resources = resourceDAO.findByConnInstance("88a7a819-dab5-46b4-9b90-0b9769eabdb8"); assertEquals(6, resources.size()); assertTrue(resources.contains(resourceDAO.findById("ws-target-resource-1").orElseThrow())); } @Test - public void findByProvisionSorter() { + void findByProvisionSorter() { Implementation impl = entityFactory.newEntity(Implementation.class); impl.setType(IdMImplementationType.PROVISION_SORTER); impl.setEngine(ImplementationEngine.GROOVY); @@ -109,7 +109,7 @@ public void findByProvisionSorter() { } @Test - public void findByPropagationActionsContaining() { + void findByPropagationActionsContaining() { Implementation impl = implementationDAO.findById("GenerateRandomPasswordPropagationActions").orElseThrow(); assertEquals( @@ -119,7 +119,7 @@ public void findByPropagationActionsContaining() { } @Test - public void createWithPasswordPolicy() { + void createWithPasswordPolicy() { final String resourceName = "resourceWithPasswordPolicy"; PasswordPolicy policy = policyDAO.findById("986d1236-3ac5-4a19-810c-5ab21d79cba1", PasswordPolicy.class). @@ -145,7 +145,7 @@ public void createWithPasswordPolicy() { } @Test - public void save() { + void save() { ExternalResource resource = entityFactory.newEntity(ExternalResource.class); resource.setKey("ws-target-resource-save"); @@ -217,7 +217,7 @@ public void save() { } @Test - public void delete() { + void delete() { ExternalResource resource = resourceDAO.findById("resource-testdb").orElseThrow(); // ------------------------------------- @@ -264,7 +264,7 @@ public void delete() { } @Test - public void addAndRemovePropagationActions() { + void addAndRemovePropagationActions() { Implementation implementation = entityFactory.newEntity(Implementation.class); implementation.setKey(UUID.randomUUID().toString()); implementation.setEngine(ImplementationEngine.JAVA); @@ -291,7 +291,7 @@ public void addAndRemovePropagationActions() { } @Test - public void issue243() { + void issue243() { ExternalResource csv = resourceDAO.findById("resource-csv").orElseThrow(); int origMapItems = csv.getProvisionByAnyType( diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/RoleTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/RoleTest.java index 9a68705bf62..95dccc0b93c 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/RoleTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/RoleTest.java @@ -39,7 +39,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class RoleTest extends AbstractTest { +class RoleTest extends AbstractTest { @Autowired private RoleDAO roleDAO; @@ -57,7 +57,7 @@ public class RoleTest extends AbstractTest { private DelegationDAO delegationDAO; @Test - public void delete() { + void delete() { // 0. create role Role role = entityFactory.newEntity(Role.class); role.setKey("new"); @@ -87,7 +87,7 @@ public void delete() { } @Test - public void deleteCascadeOnDelegations() { + void deleteCascadeOnDelegations() { User bellini = userDAO.findByUsername("bellini").orElseThrow(); User rossini = userDAO.findByUsername("rossini").orElseThrow(); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/SecurityQuestionTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/SecurityQuestionTest.java index f6cab07ec4d..c1fd658218e 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/SecurityQuestionTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/SecurityQuestionTest.java @@ -29,7 +29,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class SecurityQuestionTest extends AbstractTest { +class SecurityQuestionTest extends AbstractTest { @Autowired private SecurityQuestionDAO securityQuestionDAO; @@ -38,7 +38,7 @@ public class SecurityQuestionTest extends AbstractTest { private UserDAO userDAO; @Test - public void test() { + void test() { User user = userDAO.findByUsername("bellini").orElseThrow(); assertNull(user.getSecurityQuestion()); assertNull(user.getSecurityAnswer()); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/TaskTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/TaskTest.java index 0af1f3f1989..5f0137e597a 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/TaskTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/TaskTest.java @@ -72,7 +72,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class TaskTest extends AbstractTest { +class TaskTest extends AbstractTest { @Autowired private TaskDAO taskDAO; @@ -93,7 +93,7 @@ public class TaskTest extends AbstractTest { private TaskUtilsFactory taskUtilsFactory; @Test - public void read() { + void read() { Task task = taskDAO.findById(TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); assertNotNull(task); @@ -103,7 +103,7 @@ public void read() { } @Test - public void readMultipleOrderBy() { + void readMultipleOrderBy() { List orderByClauses = new ArrayList<>(); orderByClauses.add(new Sort.Order(Sort.DEFAULT_DIRECTION, "start")); orderByClauses.add(new Sort.Order(Sort.DEFAULT_DIRECTION, "latestExecStatus")); @@ -114,7 +114,7 @@ public void readMultipleOrderBy() { } @Test - public void findAllByResource() { + void findAllByResource() { List tasks = taskDAO.findAll( TaskType.PROPAGATION, resourceDAO.findById("ws-target-resource-2").orElseThrow(), @@ -126,7 +126,7 @@ public void findAllByResource() { } @Test - public void savePropagationTask() { + void savePropagationTask() { ExternalResource resource = resourceDAO.findById("ws-target-resource-1").orElseThrow(); PropagationTask task = entityFactory.newEntity(PropagationTask.class); @@ -153,7 +153,7 @@ public void savePropagationTask() { } @Test - public void addPropagationTaskExec() { + void addPropagationTaskExec() { PropagationTask task = (PropagationTask) taskDAO.findById( TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); @@ -174,7 +174,7 @@ public void addPropagationTaskExec() { } @Test - public void addPullTaskExec() { + void addPullTaskExec() { PullTask task = (PullTask) taskDAO.findById( TaskType.PULL, "c41b9b71-9bfa-4f90-89f2-84787def4c5c").orElseThrow(); @@ -195,7 +195,7 @@ public void addPullTaskExec() { } @Test - public void addPushTaskExec() { + void addPushTaskExec() { PushTask task = (PushTask) taskDAO.findById( TaskType.PUSH, "af558be4-9d2f-4359-bf85-a554e6e90be1").orElseThrow(); @@ -216,7 +216,7 @@ public void addPushTaskExec() { } @Test - public void deleteTask() { + void deleteTask() { taskDAO.delete(TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c"); assertTrue(taskDAO.findById("1e697572-b896-484c-ae7f-0c8f63fcbc6c").isEmpty()); @@ -224,7 +224,7 @@ public void deleteTask() { } @Test - public void deleteTaskExec() { + void deleteTaskExec() { @SuppressWarnings("unchecked") TaskExec execution = (TaskExec) taskExecDAO.findById( TaskType.PROPAGATION, "e58ca1c7-178a-4012-8a71-8aa14eaf0655").orElseThrow(); @@ -240,7 +240,7 @@ public void deleteTaskExec() { } @Test - public void savePullTask() { + void savePullTask() { PullTask task = entityFactory.newEntity(PullTask.class); task.setName("savePullTask"); task.setDescription("PullTask description"); @@ -289,7 +289,7 @@ public void savePullTask() { } @Test - public void addAndRemoveInboundActions() { + void addAndRemoveInboundActions() { Implementation implementation = entityFactory.newEntity(Implementation.class); implementation.setKey(UUID.randomUUID().toString()); implementation.setEngine(ImplementationEngine.JAVA); @@ -317,7 +317,7 @@ public void addAndRemoveInboundActions() { } @Test - public void saveLiveSyncTask() { + void saveLiveSyncTask() { LiveSyncTask task = entityFactory.newEntity(LiveSyncTask.class); task.setName("saveLiveSyncTask"); task.setDescription("LiveSyncTask description"); @@ -338,7 +338,7 @@ public void saveLiveSyncTask() { } @Test - public void addAndRemoveReconFilterBuilder() { + void addAndRemoveReconFilterBuilder() { Implementation implementation = entityFactory.newEntity(Implementation.class); implementation.setKey(UUID.randomUUID().toString()); implementation.setEngine(ImplementationEngine.JAVA); @@ -366,7 +366,7 @@ public void addAndRemoveReconFilterBuilder() { @Rollback(false) @Test - public void macroTaskCommandsOrdering() { + void macroTaskCommandsOrdering() { MacroTask task = entityFactory.newEntity(MacroTask.class); task.setName("macro"); task.setJobDelegate(implementationDAO.findById("MacroJobDelegate").orElseThrow()); @@ -408,7 +408,7 @@ public void macroTaskCommandsOrdering() { } @Test - public void saveMacroTaskSameCommandMultipleOccurrences() { + void saveMacroTaskSameCommandMultipleOccurrences() { MacroTask task = entityFactory.newEntity(MacroTask.class); task.setRealm(realmDAO.getRoot()); task.setJobDelegate(implementationDAO.findById("MacroJobDelegate").orElseThrow()); @@ -458,7 +458,7 @@ public void saveMacroTaskSameCommandMultipleOccurrences() { } @Test - public void issueSYNCOPE144() { + void issueSYNCOPE144() { ExternalResource resource = resourceDAO.findById("ws-target-resource-1").orElseThrow(); assertNotNull(resource); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/UserTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/UserTest.java index 5c8fed9aed9..5bc96fef7ef 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/UserTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/UserTest.java @@ -61,7 +61,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class UserTest extends AbstractTest { +class UserTest extends AbstractTest { @Autowired private RelationshipTypeDAO relationshipTypeDAO; @@ -106,7 +106,7 @@ public class UserTest extends AbstractTest { private Neo4jTemplate neo4jTemplate; @Test - public void delete() { + void delete() { List memberships = groupDAO.findUMemberships( groupDAO.findByName("managingDirector").orElseThrow(), Pageable.unpaged()); assertFalse(memberships.isEmpty()); @@ -122,7 +122,7 @@ public void delete() { } @Test - public void ships() { + void ships() { User user = userDAO.findByUsername("bellini").orElseThrow(); assertEquals(1, user.getMemberships().size()); assertEquals("bf825fe1-7320-4a54-bd64-143b5c18ab97", user.getMemberships().getFirst().getRightEnd().getKey()); @@ -185,7 +185,7 @@ private LinkedAccount newLinkedAccount(final String connObjectKeyValue) { } @Test - public void findLinkedAccount() { + void findLinkedAccount() { LinkedAccount account = newLinkedAccount("findLinkedAccount"); assertNotNull(account.getKey()); assertEquals(1, account.getPlainAttrs().size()); @@ -205,7 +205,7 @@ public void findLinkedAccount() { } @Test - public void deleteLinkedAccountResourceCascade() { + void deleteLinkedAccountResourceCascade() { LinkedAccount account = newLinkedAccount("deleteLinkedAccountResourceCascade"); assertNotNull(account.getKey()); @@ -218,7 +218,7 @@ public void deleteLinkedAccountResourceCascade() { } @Test - public void deleteCascadeOnDelegations() { + void deleteCascadeOnDelegations() { User bellini = userDAO.findByUsername("bellini").orElseThrow(); User rossini = userDAO.findByUsername("rossini").orElseThrow(); @@ -242,7 +242,7 @@ public void deleteCascadeOnDelegations() { } @Test - public void auxClasses() { + void auxClasses() { User user = entityFactory.newEntity(User.class); user.setRealm(realmDAO.getRoot()); @@ -268,7 +268,7 @@ public void auxClasses() { * Search by derived attribute. */ @Test - public void issueSYNCOPE800() { + void issueSYNCOPE800() { // create derived attribute (literal as prefix) DerSchema prefix = entityFactory.newEntity(DerSchema.class); prefix.setKey("kprefix"); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/XMLContentExporterTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/XMLContentExporterTest.java index 74f84642b48..f475b16bead 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/XMLContentExporterTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/XMLContentExporterTest.java @@ -33,7 +33,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class XMLContentExporterTest extends AbstractTest { +class XMLContentExporterTest extends AbstractTest { @Autowired private ContentExporter exporter; @@ -53,7 +53,7 @@ private static void checkRealms(final String exported) { * @throws Exception exception thrown when dealing with IO. */ @Test - public void issueSYNCOPE1128() throws Exception { + void issueSYNCOPE1128() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); exporter.export(SyncopeConstants.MASTER_DOMAIN, 100, baos); @@ -65,7 +65,7 @@ public void issueSYNCOPE1128() throws Exception { } @Test - public void exportElements() throws Exception { + void exportElements() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); exporter.export(SyncopeConstants.MASTER_DOMAIN, 100, baos, "AccountPolicy", "Realm"); diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/AbstractTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/AbstractTest.java index f246b7c583e..08e4f1c6575 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/AbstractTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/AbstractTest.java @@ -49,7 +49,7 @@ protected JexlTools jexlTools() { } @BeforeEach - public void init() { + void init() { MockitoAnnotations.openMocks(this); } } diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java index ca1dcc4e325..135f7833fc2 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java @@ -50,7 +50,7 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; -public class IntAttrNameParserTest extends AbstractTest { +class IntAttrNameParserTest extends AbstractTest { private static final Map> ANY_FIELDS = Map.of( AnyTypeKind.USER, List.of("key", "username", "uManager", "gManager"), @@ -77,7 +77,7 @@ public class IntAttrNameParserTest extends AbstractTest { private IntAttrNameParser intAttrNameParser; @BeforeEach - public void initMocks() throws NoSuchFieldException { + void initMocks() throws NoSuchFieldException { lenient().when(anyUtilsFactory.getInstance(any(AnyTypeKind.class))).thenAnswer(ic -> { when(anyUtils.anyTypeKind()).thenReturn(ic.getArgument(0)); return anyUtils; @@ -135,7 +135,7 @@ public void initMocks() throws NoSuchFieldException { } @Test - public void ownFields() throws ParseException { + void ownFields() throws ParseException { IntAttrName intAttrName = intAttrNameParser.parse("key", AnyTypeKind.USER); assertNotNull(intAttrName); assertNotNull(intAttrName.getField()); @@ -189,7 +189,7 @@ public void ownFields() throws ParseException { } @Test - public void ownSchema() throws ParseException { + void ownSchema() throws ParseException { IntAttrName intAttrName = intAttrNameParser.parse("email", AnyTypeKind.USER); assertNotNull(intAttrName); assertNull(intAttrName.getField()); @@ -216,7 +216,7 @@ public void ownSchema() throws ParseException { } @Test - public void externalGroup() throws ParseException { + void externalGroup() throws ParseException { IntAttrName intAttrName = intAttrNameParser.parse("groups[readers].cn", AnyTypeKind.USER); assertNotNull(intAttrName); assertNull(intAttrName.getField()); @@ -231,7 +231,7 @@ public void externalGroup() throws ParseException { } @Test - public void externalUser() throws ParseException { + void externalUser() throws ParseException { IntAttrName intAttrName = intAttrNameParser.parse("users[bellini].firstname", AnyTypeKind.USER); assertNotNull(intAttrName); assertNull(intAttrName.getField()); @@ -246,7 +246,7 @@ public void externalUser() throws ParseException { } @Test - public void externalAnyObject() throws ParseException { + void externalAnyObject() throws ParseException { IntAttrName intAttrName = intAttrNameParser.parse("anyObjects[hp].name", AnyTypeKind.USER); assertNotNull(intAttrName); assertEquals("name", intAttrName.getField()); @@ -259,7 +259,7 @@ public void externalAnyObject() throws ParseException { } @Test - public void membership() throws ParseException { + void membership() throws ParseException { IntAttrName intAttrName = intAttrNameParser.parse("memberships[top].cn", AnyTypeKind.USER); assertNotNull(intAttrName); assertNull(intAttrName.getField()); @@ -274,7 +274,7 @@ public void membership() throws ParseException { } @Test - public void relationship() throws ParseException { + void relationship() throws ParseException { IntAttrName intAttrName = intAttrNameParser.parse("relationships[inclusion][hp].location", AnyTypeKind.USER); assertNotNull(intAttrName); assertNull(intAttrName.getField()); @@ -289,7 +289,7 @@ public void relationship() throws ParseException { } @Test - public void invalid() { + void invalid() { try { intAttrNameParser.parse("memberships.cn", AnyTypeKind.USER); fail("This should not happen"); @@ -299,7 +299,7 @@ public void invalid() { } @Test - public void realm() throws ParseException { + void realm() throws ParseException { IntAttrName intAttrName = intAttrNameParser.parse("key"); assertNotNull(intAttrName); assertNotNull(intAttrName.getField()); @@ -343,7 +343,7 @@ public void realm() throws ParseException { } @Test - public void issueSYNCOPE1894() throws ParseException { + void issueSYNCOPE1894() throws ParseException { IntAttrName intAttrName = intAttrNameParser.parse("user.valueWithDot", AnyTypeKind.USER); assertNotNull(intAttrName); assertNull(intAttrName.getField()); diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/PropagationByResourceTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/PropagationByResourceTest.java index fbe38200ede..1150377cf1b 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/PropagationByResourceTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/PropagationByResourceTest.java @@ -36,14 +36,14 @@ import org.mockito.Mock; import org.springframework.test.util.ReflectionTestUtils; -public class PropagationByResourceTest extends AbstractTest { +class PropagationByResourceTest extends AbstractTest { private static final String KEY = "testKey"; private final PropagationByResource propagationByResource = new PropagationByResource<>(); @Test - public void voidMethods( + void voidMethods( final @Mock Set toBeCreated, final @Mock Set toBeUpdated, final @Mock Set toBeDeleted, @@ -86,7 +86,7 @@ public void voidMethods( } @Test - public void add() { + void add() { assertTrue(propagationByResource.add(ResourceOperation.CREATE, KEY)); assertTrue(propagationByResource.add(ResourceOperation.UPDATE, KEY)); assertTrue(propagationByResource.add(ResourceOperation.DELETE, KEY)); @@ -94,7 +94,7 @@ public void add() { } @Test - public void addAll() { + void addAll() { List keys = new ArrayList<>(); keys.add("testKey1"); keys.add("testKey2"); @@ -106,7 +106,7 @@ public void addAll() { } @Test - public void remove() { + void remove() { assertFalse(propagationByResource.remove(ResourceOperation.CREATE, KEY)); assertFalse(propagationByResource.remove(ResourceOperation.UPDATE, KEY)); assertFalse(propagationByResource.remove(ResourceOperation.DELETE, KEY)); @@ -114,7 +114,7 @@ public void remove() { } @Test - public void removeAll() { + void removeAll() { Set keys = new HashSet<>(); String key1 = "key1"; String key2 = "key2"; @@ -134,7 +134,7 @@ public void removeAll() { } @Test - public void retainAll() { + void retainAll() { Set keys = new HashSet<>(); String key1 = "key1"; String key2 = "key2"; @@ -151,7 +151,7 @@ public void retainAll() { } @Test - public void removeAndRetainAll() { + void removeAndRetainAll() { List keys = new ArrayList<>(); keys.add("testKey1"); keys.add("testKey2"); @@ -161,7 +161,7 @@ public void removeAndRetainAll() { } @Test - public void contains() { + void contains() { assertFalse(propagationByResource.contains(ResourceOperation.CREATE, KEY)); assertFalse(propagationByResource.contains(ResourceOperation.UPDATE, KEY)); assertFalse(propagationByResource.contains(ResourceOperation.DELETE, KEY)); @@ -176,7 +176,7 @@ public void contains() { } @Test - public void get() { + void get() { Set matchingList = new HashSet<>(); matchingList.add(KEY); @@ -189,12 +189,12 @@ public void get() { } @Test - public void asMap() { + void asMap() { assertEquals(Map.of(), propagationByResource.asMap()); } @Test - public void set() { + void set() { Set keys = new HashSet<>(); keys.add("testKey1"); keys.add("testKey2"); @@ -210,7 +210,7 @@ public void set() { } @Test - public void byLinkedAccount() { + void byLinkedAccount() { PropagationByResource> propByLinkedAccount = new PropagationByResource<>(); propByLinkedAccount.add(ResourceOperation.CREATE, Pair.of("resource1", "connObjectKey1")); propByLinkedAccount.add(ResourceOperation.CREATE, Pair.of("resource2", "connObjectKey2")); diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/UserWorkflowResultTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/UserWorkflowResultTest.java index 1dcb8c167e4..f9422d28640 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/UserWorkflowResultTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/UserWorkflowResultTest.java @@ -28,10 +28,10 @@ import org.apache.commons.lang3.tuple.Pair; import org.junit.jupiter.api.Test; -public class UserWorkflowResultTest extends AbstractTest { +class UserWorkflowResultTest extends AbstractTest { @Test - public void test() { + void test() { PropagationByResource propByRes = new PropagationByResource<>(); PropagationByResource> propByLinkedAccount = new PropagationByResource<>(); UserWorkflowResult userWorkflowResult; diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/WorkflowResultTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/WorkflowResultTest.java index 429a55412ec..6f12a0d8481 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/WorkflowResultTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/WorkflowResultTest.java @@ -27,10 +27,10 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; -public class WorkflowResultTest extends AbstractTest { +class WorkflowResultTest extends AbstractTest { @Test - public void test(final @Mock PropagationByResource propByRes) { + void test(final @Mock PropagationByResource propByRes) { String result = "result"; Set performedTasks = new HashSet<>(); performedTasks.add("TEST"); diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/event/AfterHandlingEventTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/event/AfterHandlingEventTest.java index a121353747d..53f9e1a2e69 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/event/AfterHandlingEventTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/event/AfterHandlingEventTest.java @@ -26,10 +26,10 @@ import org.apache.syncope.core.provisioning.api.AbstractTest; import org.junit.jupiter.api.Test; -public class AfterHandlingEventTest extends AbstractTest { +class AfterHandlingEventTest extends AbstractTest { @Test - public void test() { + void test() { String who = "testUser"; OpEvent.CategoryType type = OpEvent.CategoryType.CUSTOM; String category = SyncopeConstants.REALM_ANYTYPE.toLowerCase(); diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlToolsTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlToolsTest.java index e337bcd096b..913101ee185 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlToolsTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlToolsTest.java @@ -139,7 +139,7 @@ void builderPlainAttrs(final @Mock Collection attrs) { } @Test - public void addDerAttrsToContext( + void addDerAttrsToContext( final @Mock DerAttrHandler derAttrHandler, final @Mock Any any) { diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MailTemplateTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MailTemplateTest.java index 9451bb2bdf8..ec20fa193bf 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MailTemplateTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MailTemplateTest.java @@ -36,7 +36,7 @@ import org.apache.syncope.core.provisioning.api.AbstractTest; import org.junit.jupiter.api.Test; -public class MailTemplateTest extends AbstractTest { +class MailTemplateTest extends AbstractTest { private static final String CONFIRM_PASSWORD_RESET_TEMPLATE = "" @@ -65,13 +65,13 @@ public class MailTemplateTest extends AbstractTest { + " \n $$ }\n "; @Test - public void confirmPasswordReset() throws IOException { + void confirmPasswordReset() throws IOException { String htmlBody = jexlTools().evaluateTemplate(CONFIRM_PASSWORD_RESET_TEMPLATE, new MapContext()); assertNotNull(htmlBody); } @Test - public void requestPasswordReset() throws IOException { + void requestPasswordReset() throws IOException { Map ctx = new HashMap<>(); String username = "test" + UUID.randomUUID(); @@ -97,7 +97,7 @@ public void requestPasswordReset() throws IOException { } @Test - public void optin() throws IOException { + void optin() throws IOException { Map ctx = new HashMap<>(); String username = "test" + UUID.randomUUID(); diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MappingTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MappingTest.java index fbb7129c1a1..a2cbc6c868f 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MappingTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MappingTest.java @@ -31,10 +31,10 @@ import org.apache.syncope.core.provisioning.api.AbstractTest; import org.junit.jupiter.api.Test; -public class MappingTest extends AbstractTest { +class MappingTest extends AbstractTest { @Test - public void anyConnObjectLink() { + void anyConnObjectLink() { Realm realm = mock(Realm.class); when(realm.getFullPath()).thenReturn("/even"); @@ -53,7 +53,7 @@ public void anyConnObjectLink() { } @Test - public void realmConnObjectLink() { + void realmConnObjectLink() { Realm realm = mock(Realm.class); when(realm.getFullPath()).thenReturn("/even/two"); assertNotNull(realm); @@ -72,7 +72,7 @@ public void realmConnObjectLink() { } @Test - public void datetime() { + void datetime() { OffsetDateTime now = OffsetDateTime.now(); JexlContext jexlContext = new MapContext(); diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/job/JobNamerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/job/JobNamerTest.java index 75adbd9d0c5..26430956e17 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/job/JobNamerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/job/JobNamerTest.java @@ -29,12 +29,12 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; -public class JobNamerTest extends AbstractTest { +class JobNamerTest extends AbstractTest { private String name; @Test - public void getTaskKeyFromJobName() { + void getTaskKeyFromJobName() { name = "testName"; assertTrue(JobNamer.getTaskKeyFromJobName(name).isEmpty()); @@ -44,7 +44,7 @@ public void getTaskKeyFromJobName() { } @Test - public void getReportKeyFromJobName() { + void getReportKeyFromJobName() { name = "testName"; assertTrue(JobNamer.getTaskKeyFromJobName(name).isEmpty()); @@ -54,14 +54,14 @@ public void getReportKeyFromJobName() { } @Test - public void getJobKey(final @Mock Task task) { + void getJobKey(final @Mock Task task) { String uuid = UUID.randomUUID().toString(); when(task.getKey()).thenReturn(uuid); assertEquals("taskJob" + task.getKey(), JobNamer.getJobName(task)); } @Test - public void getJobKey(final @Mock Report report) { + void getJobKey(final @Mock Report report) { String uuid = UUID.randomUUID().toString(); when(report.getKey()).thenReturn(uuid); assertEquals("reportJob" + report.getKey(), JobNamer.getJobName(report)); diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfileTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfileTest.java index a82b16ec3ba..d39282c6821 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfileTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfileTest.java @@ -31,10 +31,10 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; -public class ProvisioningProfileTest extends AbstractTest { +class ProvisioningProfileTest extends AbstractTest { @Test - public void test( + void test( final @Mock Connector connector, final @Mock PushTask pushTask) { diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializerTest.java index 95a3f901ea7..71df3437758 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializerTest.java @@ -35,7 +35,7 @@ import tools.jackson.databind.JsonNode; import tools.jackson.databind.node.ObjectNode; -public class AttributeDeserializerTest extends AbstractTest { +class AttributeDeserializerTest extends AbstractTest { @Mock private JsonParser jp; @@ -57,7 +57,7 @@ public class AttributeDeserializerTest extends AbstractTest { private String name; @BeforeEach - public void initTest() throws IOException { + void initTest() throws IOException { name = Name.NAME; when(jp.readValueAsTree()).thenReturn(tree); when(tree.get("name")).thenReturn(node2); @@ -66,7 +66,7 @@ public void initTest() throws IOException { } @Test - public void deserializeIsNull() throws IOException { + void deserializeIsNull() throws IOException { when(node2.asString()).thenReturn(name); when(node.isNull()).thenReturn(Boolean.TRUE); Attribute attr = deserializer.deserialize(jp, ct); @@ -76,7 +76,7 @@ public void deserializeIsNull() throws IOException { } @Test - public void deserializeIsBoolean() throws IOException { + void deserializeIsBoolean() throws IOException { when(node2.asString()).thenReturn(name); when(node.isBoolean()).thenReturn(Boolean.TRUE); when(node.asBoolean()).thenReturn(Boolean.TRUE); @@ -86,7 +86,7 @@ public void deserializeIsBoolean() throws IOException { } @Test - public void deserializeIsDouble() throws IOException { + void deserializeIsDouble() throws IOException { Double number = 9000.1; name = "__TEST__"; when(node2.asString()).thenReturn(name); @@ -98,7 +98,7 @@ public void deserializeIsDouble() throws IOException { } @Test - public void deserializeIsLong() throws IOException { + void deserializeIsLong() throws IOException { Long number = 9000L; name = "__UID__"; when(node2.asString()).thenReturn(name); @@ -110,7 +110,7 @@ public void deserializeIsLong() throws IOException { } @Test - public void deserializeIsInt() throws IOException { + void deserializeIsInt() throws IOException { Integer number = 9000; when(node2.asString()).thenReturn(name); when(node.isInt()).thenReturn(Boolean.TRUE); @@ -121,7 +121,7 @@ public void deserializeIsInt() throws IOException { } @Test - public void deserializeIsText() throws IOException { + void deserializeIsText() throws IOException { String text = "test"; when(node2.asString()).thenReturn(name); when(node.asString()).thenReturn(text); diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializerTest.java index 6d90de8d5bf..9bccbc77032 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializerTest.java @@ -37,10 +37,10 @@ import tools.jackson.core.JsonGenerator; import tools.jackson.databind.SerializationContext; -public class AttributeSerializerTest extends AbstractTest { +class AttributeSerializerTest extends AbstractTest { @Test - public void serialize( + void serialize( final @Mock Attribute source, final @Mock JsonGenerator jgen, final @Mock SerializationContext ctx) { diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/ConnObjectIdentificationDeserializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/ConnObjectIdentificationDeserializerTest.java index 6dd867ce1e4..768fae6c046 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/ConnObjectIdentificationDeserializerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/ConnObjectIdentificationDeserializerTest.java @@ -28,10 +28,10 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -public class ConnObjectIdentificationDeserializerTest extends AbstractTest { +class ConnObjectIdentificationDeserializerTest extends AbstractTest { @Test - public void deserialize() throws IOException { + void deserialize() throws IOException { ConnectorObjectIdentification source = new ConnectorObjectIdentification(ObjectClass.ACCOUNT, Set.of(AttributeBuilder.build(Uid.NAME, "someuid"))); diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/ConnObjectIdentificationSerializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/ConnObjectIdentificationSerializerTest.java index ea06377d354..ad3d59a3cf6 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/ConnObjectIdentificationSerializerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/ConnObjectIdentificationSerializerTest.java @@ -33,10 +33,10 @@ import tools.jackson.core.JsonGenerator; import tools.jackson.databind.SerializationContext; -public class ConnObjectIdentificationSerializerTest extends AbstractTest { +class ConnObjectIdentificationSerializerTest extends AbstractTest { @Test - public void serialize( + void serialize( final @Mock JsonGenerator jgen, final @Mock SerializationContext ctx) { diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializerTest.java index a217a7e766a..b60dd42aecf 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringDeserializerTest.java @@ -38,7 +38,7 @@ import tools.jackson.databind.node.JsonNodeFactory; import tools.jackson.databind.node.ObjectNode; -public class GuardedStringDeserializerTest extends AbstractTest { +class GuardedStringDeserializerTest extends AbstractTest { private static final String READONLY = "readOnly"; @@ -60,7 +60,7 @@ public class GuardedStringDeserializerTest extends AbstractTest { private JsonNode node; @Test - public void deserialize() { + void deserialize() { Map kids = new HashMap<>(); kids.put(READONLY, node); kids.put(DISPOSED, node); diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializerTest.java index 477fbe4b67e..fa26a7876d7 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/GuardedStringSerializerTest.java @@ -30,7 +30,7 @@ import tools.jackson.core.JsonGenerator; import tools.jackson.databind.SerializationContext; -public class GuardedStringSerializerTest extends AbstractTest { +class GuardedStringSerializerTest extends AbstractTest { private static final String READONLY = "readOnly"; @@ -43,7 +43,7 @@ public class GuardedStringSerializerTest extends AbstractTest { private final GuardedStringSerializer serializer = new GuardedStringSerializer(); @Test - public void serialize( + void serialize( final @Mock JsonGenerator jgen, final @Mock SerializationContext ctx) throws IOException { diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelperTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelperTest.java index d6827baa9c0..821a1449965 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelperTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelperTest.java @@ -31,31 +31,31 @@ import org.mockito.Mock; import tools.jackson.core.type.TypeReference; -public class POJOHelperTest extends AbstractTest { +class POJOHelperTest extends AbstractTest { @Test - public void serialize() { + void serialize() { Object object = 9001; assertEquals(String.valueOf(object), POJOHelper.serialize(object)); } @Test - public void deserializeWithClassReference() { + void deserializeWithClassReference() { String serialized = "false"; assertEquals(Boolean.valueOf(serialized), POJOHelper.deserialize(serialized, Object.class)); } @Test - public void deserializeWithTypeReference(final @Mock TypeReference reference) { + void deserializeWithTypeReference(final @Mock TypeReference reference) { String serialized = "false"; assertNull(POJOHelper.deserialize(serialized, reference)); } @Test - public void propagationData() { + void propagationData() { PropagationData original = new PropagationData(Set.of(AttributeBuilder.build("title", "title1"))); original.setAttributeDeltas(Set.of(AttributeDeltaBuilder.build("title", List.of("title2"), List.of("title1")))); diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/PropagationTaskInfoSerializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/PropagationTaskInfoSerializerTest.java index e04edbda596..c58bdf69d70 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/PropagationTaskInfoSerializerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/PropagationTaskInfoSerializerTest.java @@ -37,10 +37,10 @@ import tools.jackson.core.JsonGenerator; import tools.jackson.databind.SerializationContext; -public class PropagationTaskInfoSerializerTest extends AbstractTest { +class PropagationTaskInfoSerializerTest extends AbstractTest { @Test - public void propagationTaskInfoSerializer( + void propagationTaskInfoSerializer( final @Mock JsonGenerator jgen, final @Mock SerializationContext ctx) { diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializerTest.java index 7c79abf70c8..7b4616e3ab0 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenDeserializerTest.java @@ -35,7 +35,7 @@ import tools.jackson.databind.JsonNode; import tools.jackson.databind.node.ObjectNode; -public class SyncTokenDeserializerTest extends AbstractTest { +class SyncTokenDeserializerTest extends AbstractTest { @Mock private JsonParser jp; @@ -52,14 +52,14 @@ public class SyncTokenDeserializerTest extends AbstractTest { private final SyncTokenDeserializer deserializer = new SyncTokenDeserializer(); @BeforeEach - public void initTest() throws IOException { + void initTest() throws IOException { when(jp.readValueAsTree()).thenReturn(tree); when(tree.has("value")).thenReturn(Boolean.TRUE); when(tree.get("value")).thenReturn(node); } @Test - public void deserializeIsBoolean() throws IOException { + void deserializeIsBoolean() throws IOException { Boolean value = Boolean.TRUE; when(node.isBoolean()).thenReturn(value); when(node.asBoolean()).thenReturn(value); @@ -67,7 +67,7 @@ public void deserializeIsBoolean() throws IOException { } @Test - public void deserializeIsDouble() throws IOException { + void deserializeIsDouble() throws IOException { Double value = 9000.1; when(node.isDouble()).thenReturn(Boolean.TRUE); when(node.asDouble()).thenReturn(value); @@ -75,7 +75,7 @@ public void deserializeIsDouble() throws IOException { } @Test - public void deserializeIsLong() throws IOException { + void deserializeIsLong() throws IOException { Long value = 9000L; when(node.isLong()).thenReturn(Boolean.TRUE); when(node.asLong()).thenReturn(value); @@ -83,7 +83,7 @@ public void deserializeIsLong() throws IOException { } @Test - public void deserializeIsInt() throws IOException { + void deserializeIsInt() throws IOException { Integer value = 9000; when(node.isInt()).thenReturn(Boolean.TRUE); when(node.asInt()).thenReturn(value); @@ -91,7 +91,7 @@ public void deserializeIsInt() throws IOException { } @Test - public void deserializeIsString() throws IOException { + void deserializeIsString() throws IOException { String value = "testValue"; when(node.asString()).thenReturn(value); assertEquals(value, deserializer.deserialize(jp, ct).getValue()); diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenSerializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenSerializerTest.java index 1085494c661..a39648e20ec 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenSerializerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/SyncTokenSerializerTest.java @@ -30,10 +30,10 @@ import tools.jackson.core.JsonGenerator; import tools.jackson.databind.SerializationContext; -public class SyncTokenSerializerTest extends AbstractTest { +class SyncTokenSerializerTest extends AbstractTest { @Test - public void syncTokenSerializer( + void syncTokenSerializer( final @Mock JsonGenerator jgen, final @Mock SerializationContext ctx) throws IOException { diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningProperties.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningProperties.java index a690f99849a..858fe71fa37 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningProperties.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningProperties.java @@ -31,6 +31,8 @@ public class ProvisioningProperties { private final ExecutorProperties scheduling = new ExecutorProperties(); + private final ResultHandlerDispatcherProperties resultHandlerDispatcher = new ResultHandlerDispatcherProperties(); + private final List connIdLocation = new ArrayList<>(); public ExecutorProperties getAsyncConnectorFacadeExecutor() { @@ -45,6 +47,10 @@ public ExecutorProperties getScheduling() { return scheduling; } + public ResultHandlerDispatcherProperties getResultHandlerDispatcher() { + return resultHandlerDispatcher; + } + public List getConnIdLocation() { return connIdLocation; } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ResultHandlerDispatcherProperties.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ResultHandlerDispatcherProperties.java new file mode 100644 index 00000000000..6a2160005b5 --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ResultHandlerDispatcherProperties.java @@ -0,0 +1,50 @@ +/* + * 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.core.provisioning.java; + +import org.apache.syncope.core.provisioning.java.pushpull.SyncopeResultHandlerDispatcher; + +public class ResultHandlerDispatcherProperties { + + /** + * Maximum time to wait for a free slot before rejecting a new handler task. + */ + private int submitTimeoutSeconds = SyncopeResultHandlerDispatcher.DEFAULT_SUBMIT_TIMEOUT_SECONDS; + + /** + * Maximum overall time to wait for the submitted handler tasks to complete. + */ + private int completionTimeoutSeconds = SyncopeResultHandlerDispatcher.DEFAULT_COMPLETION_TIMEOUT_SECONDS; + + public int getSubmitTimeoutSeconds() { + return submitTimeoutSeconds; + } + + public void setSubmitTimeoutSeconds(final int submitTimeoutSeconds) { + this.submitTimeoutSeconds = submitTimeoutSeconds; + } + + public int getCompletionTimeoutSeconds() { + return completionTimeoutSeconds; + } + + public void setCompletionTimeoutSeconds(final int completionTimeoutSeconds) { + this.completionTimeoutSeconds = completionTimeoutSeconds; + } +} diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java index bd61bdb24cc..deb47c84e02 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java @@ -42,6 +42,7 @@ import org.apache.syncope.core.provisioning.api.ProvisionSorter; import org.apache.syncope.core.provisioning.api.job.JobExecutionContext; import org.apache.syncope.core.provisioning.api.job.JobExecutionException; +import org.apache.syncope.core.provisioning.java.ProvisioningProperties; import org.apache.syncope.core.provisioning.java.job.AbstractSchedTaskJobDelegate; import org.apache.syncope.core.provisioning.java.job.TaskJob; import org.apache.syncope.core.spring.implementation.ImplementationManager; @@ -74,10 +75,30 @@ public abstract class AbstractProvisioningJobDelegate perContextProvisionSorter = Optional.empty(); protected Connector connector; + /** + * Applies the configured timeouts to the given dispatcher, so that concurrent handling cannot wait forever + * for a free slot, nor forever for the submitted handler tasks to complete. + * + * @param + * @param dispatcher the dispatcher to configure + * @return the given dispatcher + */ + protected > RHD configure(final RHD dispatcher) { + dispatcher.setSubmitTimeoutSeconds( + provisioningProperties.getResultHandlerDispatcher().getSubmitTimeoutSeconds()); + dispatcher.setCompletionTimeoutSeconds( + provisioningProperties.getResultHandlerDispatcher().getCompletionTimeoutSeconds()); + + return dispatcher; + } + @Override protected void init( final TaskType taskType, diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullExecutor.java index 62f812db7a2..8199c06623f 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullExecutor.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullExecutor.java @@ -75,8 +75,8 @@ abstract class AbstractPullExecutor> protected PullResultHandlerDispatcher dispatcher; protected PullResultHandlerDispatcher buildDispatcher() { - return ctx.getBeanFactory().createBean(PullResultHandlerDispatcher.class). - init(profile, this); + return configure(ctx.getBeanFactory().createBean(PullResultHandlerDispatcher.class). + init(profile, this)); } protected RealmPullResultHandler buildRealmHandler() { diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java index e5ad81f7ccf..ff949c1e5a8 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java @@ -212,8 +212,8 @@ public void stop() { } protected PushResultHandlerDispatcher buildDispatcher() { - return ctx.getBeanFactory().createBean(PushResultHandlerDispatcher.class). - init(profile, this); + return configure(ctx.getBeanFactory().createBean(PushResultHandlerDispatcher.class). + init(profile, this)); } @Override diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SyncopeResultHandlerDispatcher.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SyncopeResultHandlerDispatcher.java index 87b57b93b67..8c5db90a0d4 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SyncopeResultHandlerDispatcher.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SyncopeResultHandlerDispatcher.java @@ -23,12 +23,18 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.CancellationException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Supplier; import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask; +import org.apache.syncope.core.provisioning.api.job.JobExecutionException; import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningActions; import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile; import org.apache.syncope.core.provisioning.api.pushpull.SyncopeResultHandler; @@ -49,20 +55,54 @@ public abstract class SyncopeResultHandlerDispatcher< private static final String PLACEHOLDER_PWD = "PLACEHOLDER_PWD"; + /** + * Default value for {@link #submitTimeoutSeconds}. + */ + public static final int DEFAULT_SUBMIT_TIMEOUT_SECONDS = 300; + + /** + * Default value for {@link #completionTimeoutSeconds}. + */ + public static final int DEFAULT_COMPLETION_TIMEOUT_SECONDS = 600; + protected Optional tpte; + /** + * Only set in concurrent mode: one permit per configured pool size, so that the caller of + * {@link #submit(Runnable)} is blocked while no handler task can be started - but never for longer than + * {@link #submitTimeoutSeconds}. + */ + protected Semaphore permits; + + /** + * Maximum time to wait for a free slot before rejecting a new handler task. + */ + protected int submitTimeoutSeconds = DEFAULT_SUBMIT_TIMEOUT_SECONDS; + + /** + * Maximum overall time to wait, on {@link #shutdown()}, for the submitted handler tasks to complete. + */ + protected int completionTimeoutSeconds = DEFAULT_COMPLETION_TIMEOUT_SECONDS; + protected final Map> suppliers = new ConcurrentHashMap<>(); protected final Map handlers = new ConcurrentHashMap<>(); protected final List> futures = new ArrayList<>(); + /** + * Number of handler tasks rejected because no free slot became available within {@link #submitTimeoutSeconds}. + */ + protected int rejectedSubmissions; + protected void init(final ProvisioningProfile profile) { if (profile.getTask().getConcurrentSettings() == null) { tpte = Optional.empty(); } else { + int poolSize = profile.getTask().getConcurrentSettings().getPoolSize(); + VirtualThreadPoolTaskExecutor t = new VirtualThreadPoolTaskExecutor(); - t.setPoolSize(profile.getTask().getConcurrentSettings().getPoolSize()); + t.setPoolSize(poolSize); t.setWaitForTasksToCompleteOnShutdown(true); t.setThreadNamePrefix("provisioningTask-" + profile.getTask().getKey() + "-"); t.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); @@ -82,6 +122,8 @@ protected void init(final ProvisioningProfile profile) { t.initialize(); + permits = new Semaphore(poolSize); + tpte = Optional.of(t); } } @@ -94,23 +136,119 @@ public RA nonConcurrentHandler(final String key) { return handlers.computeIfAbsent(key, k -> suppliers.get(k).get()); } + public void setSubmitTimeoutSeconds(final int submitTimeoutSeconds) { + this.submitTimeoutSeconds = submitTimeoutSeconds; + } + + public void setCompletionTimeoutSeconds(final int completionTimeoutSeconds) { + this.completionTimeoutSeconds = completionTimeoutSeconds; + } + protected void submit(final Runnable runnable) { - tpte.ifPresent(executor -> futures.add(executor.submit(runnable))); + if (tpte.isEmpty()) { + return; + } + + if (!acquirePermit()) { + rejectedSubmissions++; + + throw new RejectedExecutionException("No free slot for handler task after " + submitTimeoutSeconds + 's'); + } + + try { + futures.add(tpte.get().submit(() -> { + try { + runnable.run(); + } finally { + releasePermit(); + } + })); + } catch (RuntimeException e) { + releasePermit(); + + throw e; + } + } + + private boolean acquirePermit() { + if (permits == null) { + return true; + } + + try { + if (permits.tryAcquire(submitTimeoutSeconds, TimeUnit.SECONDS)) { + return true; + } + + LOG.error("Timeout after {}s while waiting for a free slot to submit a new handler task", + submitTimeoutSeconds); + return false; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + + LOG.error("Interrupted while waiting for a free slot to submit a new handler task", e); + return false; + } + } + + private void releasePermit() { + Optional.ofNullable(permits).ifPresent(Semaphore::release); } public void stop() { handlers.values().forEach(SyncopeResultHandler::stop); } - protected void shutdown() { - for (Future f : this.futures) { + /** + * Waits for the submitted handler tasks to complete, for no longer than {@link #completionTimeoutSeconds} overall; + * any task still running at that point is cancelled. + * + * @throws JobExecutionException if not all handler tasks could be submitted and completed + */ + protected void shutdown() throws JobExecutionException { + awaitCompletion(); + + List> notCompleted = futures.stream().filter(future -> !future.isDone()).toList(); + notCompleted.forEach(future -> future.cancel(true)); + + tpte.ifPresent(VirtualThreadPoolTaskExecutor::shutdown); + + if (!notCompleted.isEmpty() || rejectedSubmissions > 0) { + throw new JobExecutionException("Concurrent handling did not complete: " + + notCompleted.size() + " out of " + futures.size() + " handler task(s) still running after " + + completionTimeoutSeconds + 's' + + (rejectedSubmissions == 0 + ? "" + : ", " + rejectedSubmissions + " handler task(s) not submitted within " + + submitTimeoutSeconds + 's')); + } + } + + private void awaitCompletion() { + long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(completionTimeoutSeconds); + + for (Future future : futures) { + long remaining = deadline - System.nanoTime(); + if (remaining <= 0) { + LOG.error("Timeout after {}s while waiting for handler tasks to complete", completionTimeoutSeconds); + return; + } + try { - f.get(); - } catch (ExecutionException | InterruptedException e) { + future.get(remaining, TimeUnit.NANOSECONDS); + } catch (TimeoutException e) { + LOG.error("Timeout after {}s while waiting for handler tasks to complete", completionTimeoutSeconds, e); + return; + } catch (CancellationException e) { + LOG.debug("Handler task was cancelled", e); + } catch (ExecutionException e) { LOG.error("Unexpected error when waiting for completion", e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + + LOG.error("Interrupted while waiting for completion", e); + return; } } - - tpte.ifPresent(VirtualThreadPoolTaskExecutor::shutdown); } } diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java index 0b66606c993..f5c57040dbe 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java @@ -19,6 +19,7 @@ package org.apache.syncope.core.provisioning.java; import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.MockitoAnnotations.openMocks; import io.zonky.test.db.postgres.embedded.EmbeddedPostgres; import jakarta.persistence.EntityManager; @@ -31,7 +32,6 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.MockitoAnnotations; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; @@ -80,7 +80,7 @@ public static void init() { protected EntityManager entityManager; @BeforeEach - public void initMocks() { - MockitoAnnotations.openMocks(this); + void initMocks() { + openMocks(this); } } diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ConnectorManagerTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ConnectorManagerTest.java index 22652cd6b72..f904dd59550 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ConnectorManagerTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ConnectorManagerTest.java @@ -31,7 +31,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class ConnectorManagerTest extends AbstractTest { +class ConnectorManagerTest extends AbstractTest { @Autowired private ConnIdBundleManager connIdBundleManager; @@ -45,7 +45,7 @@ public class ConnectorManagerTest extends AbstractTest { private DefaultConnectorManager connManager; @BeforeEach - public void before() { + void before() { connManager = new DefaultConnectorManager(connIdBundleManager, resourceDAO, null, null, ctx); // Remove any other connector instance bean set up by standard ConnectorManager.load() @@ -53,7 +53,7 @@ public void before() { } @Test - public void load() { + void load() { connManager.load(); // only consider local connector bundles diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DefaultMappingManagerTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DefaultMappingManagerTest.java index e9f99bce553..db697593741 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DefaultMappingManagerTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DefaultMappingManagerTest.java @@ -44,7 +44,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class DefaultMappingManagerTest extends AbstractTest { +class DefaultMappingManagerTest extends AbstractTest { @Autowired private MappingManager mappingManager; @@ -59,7 +59,7 @@ public class DefaultMappingManagerTest extends AbstractTest { private EntityFactory entityFactory; @Test - public void prepareAttrsForUser() { + void prepareAttrsForUser() { User bellini = userDAO.findByUsername("bellini").orElseThrow(); ExternalResource ldap = resourceDAO.findById("resource-ldap").orElseThrow(); Provision provision = ldap.getProvisionByAnyType(AnyTypeKind.USER.name()).orElseThrow(); @@ -137,7 +137,7 @@ public void prepareAttrsForUser() { } @Test - public void prepareAttrsForLinkedAccount() { + void prepareAttrsForLinkedAccount() { User vivaldi = userDAO.findByUsername("vivaldi").orElseThrow(); ExternalResource ldap = resourceDAO.findById("resource-ldap").orElseThrow(); Provision provision = ldap.getProvisionByAnyType(AnyTypeKind.USER.name()).orElseThrow(); diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DerAttrTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DerAttrTest.java index 4a32618c835..4f57fb71ac3 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DerAttrTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DerAttrTest.java @@ -33,7 +33,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class DerAttrTest extends AbstractTest { +class DerAttrTest extends AbstractTest { @Autowired private DerSchemaDAO derSchemaDAO; @@ -45,7 +45,7 @@ public class DerAttrTest extends AbstractTest { private DerAttrHandler derAttrHandler; @Test - public void derAttrFromSpecialAttrs() { + void derAttrFromSpecialAttrs() { DerSchema info = derSchemaDAO.findById("info").orElseThrow(); assertEquals("username + ' - ' + creationDate + '[' + failedLogins + ']'", info.getExpression()); diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AbstractDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AbstractDataBinderTest.java new file mode 100644 index 00000000000..29603f46373 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AbstractDataBinderTest.java @@ -0,0 +1,71 @@ +/* + * 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.core.provisioning.java.data; + +import java.util.Set; +import java.util.stream.Stream; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.AMEntitlement; +import org.apache.syncope.common.lib.types.AnyEntitlement; +import org.apache.syncope.common.lib.types.IdMEntitlement; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.provisioning.java.AbstractTest; +import org.apache.syncope.core.spring.security.SyncopeAuthenticationDetails; +import org.apache.syncope.core.spring.security.SyncopeGrantedAuthority; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.transaction.annotation.Transactional; + +/** + * Base class for data binder tests: runs each test in a transaction (so that changes are rolled back) and + * makes available an authenticated administrator with all entitlements. + */ +@Transactional(rollbackFor = Throwable.class) +public abstract class AbstractDataBinderTest extends AbstractTest { + + @BeforeAll + public static void setAuthContext() { + var authorities = Stream.concat( + Stream.of( + IdRepoEntitlement.values(), + IdMEntitlement.values(), + AMEntitlement.values()). + flatMap(Set::stream), + Stream.of("PRINTER"). + flatMap(anyType -> Stream.of(AnyEntitlement.values()). + map(entitlement -> entitlement.getFor(anyType)))). + map(entitlement -> (GrantedAuthority) new SyncopeGrantedAuthority( + entitlement, SyncopeConstants.ROOT_REALM)). + toList(); + + UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken( + new org.springframework.security.core.userdetails.User( + "admin", "FAKE_PASSWORD", authorities), "FAKE_PASSWORD", authorities); + auth.setDetails(new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, null)); + SecurityContextHolder.getContext().setAuthentication(auth); + } + + @AfterAll + public static void unsetAuthContext() { + SecurityContextHolder.clearContext(); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderTest.java new file mode 100644 index 00000000000..7dac9dc0437 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AccessTokenDataBinderTest.java @@ -0,0 +1,131 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.OffsetDateTime; +import java.util.Map; +import java.util.Optional; +import org.apache.syncope.common.lib.to.AccessTokenTO; +import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; +import org.apache.syncope.core.persistence.api.entity.AccessToken; +import org.apache.syncope.core.provisioning.api.data.AccessTokenDataBinder; +import org.apache.syncope.core.provisioning.api.data.AccessTokenDataBinder.AccessTokenInfo; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class AccessTokenDataBinderTest extends AbstractDataBinderTest { + + private static final String OWNER = "verdi"; + + @Autowired + private AccessTokenDataBinder binder; + + @Autowired + private AccessTokenDAO accessTokenDAO; + + @Test + void create() { + AccessTokenInfo info = binder.create( + Optional.of("access-token-key"), OWNER, Map.of("claim", "claimValue"), "AUTH1", false); + + assertNotNull(info.jwt()); + assertTrue(info.expiration().isAfter(OffsetDateTime.now())); + + AccessToken accessToken = accessTokenDAO.findByOwner(OWNER).orElseThrow(); + assertEquals("access-token-key", accessToken.getKey()); + assertEquals(info.jwt(), accessToken.getBody()); + assertEquals(OWNER, accessToken.getOwner()); + assertEquals("AUTH1", accessToken.getAuthorities()); + + AccessTokenTO accessTokenTO = binder.getAccessTokenTO(accessToken); + assertEquals(accessToken.getKey(), accessTokenTO.getKey()); + assertEquals(accessToken.getExpirationTime(), accessTokenTO.getExpirationTime()); + assertEquals(OWNER, accessTokenTO.getOwner()); + } + + @Test + void createWithoutReplace() { + AccessTokenInfo first = binder.create(Optional.empty(), OWNER, Map.of(), "AUTH1", false); + AccessTokenInfo second = binder.create(Optional.empty(), OWNER, Map.of(), "AUTH2", false); + + assertEquals(first.jwt(), second.jwt()); + assertEquals("AUTH1", accessTokenDAO.findByOwner(OWNER).orElseThrow().getAuthorities()); + + AccessToken existing = accessTokenDAO.findByOwner(OWNER).orElseThrow(); + String key = existing.getKey(); + existing.setExpirationTime(OffsetDateTime.now().minusDays(1)); + accessTokenDAO.save(existing); + entityManager.flush(); + + AccessTokenInfo refreshed = binder.create(Optional.empty(), OWNER, Map.of(), "AUTH2", false); + AccessToken afterRefresh = accessTokenDAO.findByOwner(OWNER).orElseThrow(); + assertEquals(key, afterRefresh.getKey()); + assertEquals(refreshed.jwt(), afterRefresh.getBody()); + assertTrue(afterRefresh.getExpirationTime().isAfter(OffsetDateTime.now())); + assertEquals("AUTH2", afterRefresh.getAuthorities()); + } + + @Test + void createWithReplace() { + binder.create(Optional.empty(), OWNER, Map.of(), "AUTH1", false); + AccessToken before = accessTokenDAO.findByOwner(OWNER).orElseThrow(); + String key = before.getKey(); + + AccessTokenInfo replaced = binder.create(Optional.empty(), OWNER, Map.of(), "AUTH2", true); + + assertNotNull(replaced.jwt()); + AccessToken accessToken = accessTokenDAO.findByOwner(OWNER).orElseThrow(); + assertEquals(key, accessToken.getKey()); + assertEquals(replaced.jwt(), accessToken.getBody()); + assertEquals("AUTH2", accessToken.getAuthorities()); + } + + @Test + void createForAdmin() { + AccessTokenInfo info = binder.create(Optional.empty(), "admin", Map.of(), "AUTH", false); + + assertNotNull(info.jwt()); + AccessToken accessToken = accessTokenDAO.findByOwner("admin").orElseThrow(); + assertNull(accessToken.getAuthorities()); + } + + @Test + void update() { + binder.create(Optional.empty(), OWNER, Map.of(), "AUTH1", false); + AccessToken accessToken = accessTokenDAO.findByOwner(OWNER).orElseThrow(); + String key = accessToken.getKey(); + + AccessTokenInfo updated = binder.update(accessToken, "AUTH2"); + + assertTrue(updated.expiration().isAfter(OffsetDateTime.now())); + + AccessToken actual = accessTokenDAO.findByOwner(OWNER).orElseThrow(); + assertEquals(key, actual.getKey()); + assertEquals(updated.jwt(), actual.getBody()); + assertEquals(updated.expiration(), actual.getExpirationTime()); + assertEquals("AUTH2", actual.getAuthorities()); + assertFalse(actual.getBody().isEmpty()); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderTest.java new file mode 100644 index 00000000000..4bd4f384de6 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderTest.java @@ -0,0 +1,283 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.apache.syncope.common.lib.Attr; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.request.AnyObjectCR; +import org.apache.syncope.common.lib.request.AnyObjectUR; +import org.apache.syncope.common.lib.request.AttrPatch; +import org.apache.syncope.common.lib.request.MembershipUR; +import org.apache.syncope.common.lib.request.RelationshipUR; +import org.apache.syncope.common.lib.request.StringPatchItem; +import org.apache.syncope.common.lib.request.StringReplacePatchItem; +import org.apache.syncope.common.lib.to.AnyObjectTO; +import org.apache.syncope.common.lib.to.MembershipTO; +import org.apache.syncope.common.lib.to.RelationshipTO; +import org.apache.syncope.common.lib.types.PatchOperation; +import org.apache.syncope.core.persistence.api.EncryptorManager; +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.provisioning.api.PropagationByResource; +import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class AnyObjectDataBinderTest extends AbstractDataBinderTest { + + private static final String PRINTER_KEY = "fc6dbc3a-6c07-4965-8781-921e7401a4a5"; + + private static final String OTHER_PRINTER_KEY = "8559d14d-58c2-46eb-a2d4-a7d35161e8f8"; + + private static final String THIRD_PRINTER_KEY = "9e1d130c-d6a3-48b1-98b3-182477ed0688"; + + private static final String VERDI_KEY = "74cd8ece-715a-44a4-a736-e17b46c4e7e6"; + + private static final String GROUP_KEY = "f779c0d4-633b-4be5-8f57-32eb478a3ca5"; + + @Autowired + private AnyObjectDataBinder binder; + + @Autowired + private AnyObjectDAO anyObjectDAO; + + @Autowired + private RelationshipTypeDAO relationshipTypeDAO; + + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + @Autowired + private EncryptorManager encryptorManager; + + @Autowired + private EntityFactory entityFactory; + + @Test + void get() { + AnyObjectTO printer = binder.getAnyObjectTO(PRINTER_KEY); + assertEquals(PRINTER_KEY, printer.getKey()); + assertEquals("HP LJ 1300n", printer.getName()); + assertEquals("PRINTER", printer.getType()); + assertEquals(SyncopeConstants.ROOT_REALM, printer.getRealm()); + assertEquals("Canon MFC8030", printer.getPlainAttr("model").orElseThrow().getValues().getFirst()); + + AnyObjectTO noDetails = binder.getAnyObjectTO(anyObjectDAO.findById(PRINTER_KEY).orElseThrow(), false); + assertEquals(PRINTER_KEY, noDetails.getKey()); + } + + @Test + void createFull() { + AnyObjectCR anyObjectCR = new AnyObjectCR.Builder("/even/two", "PRINTER", "fullPrinter"). + uManager(VERDI_KEY). + auxClasses("other", "csv", "not-existing"). + plainAttr(new Attr.Builder("model").value("Full model").build()). + plainAttr(new Attr.Builder("location").value("3rd floor").build()). + plainAttr(new Attr.Builder("ctype").value("G").build()). + plainAttr(new Attr.Builder("aLong").value("42").build()). + plainAttr(new Attr.Builder("cool").value("true").build()). + plainAttr(new Attr.Builder("gender").value("M").build()). + plainAttr(new Attr.Builder("loginDate").values(List.of("2020-01-01", "2021-02-02")).build()). + plainAttr(new Attr.Builder("uselessReadonly").value("ignored").build()). + plainAttr(new Attr.Builder("not-existing").value("ignored").build()). + resource("ws-target-resource-1"). + resource("not-existing"). + relationship(new RelationshipTO.Builder("inclusion"). + otherEnd(OTHER_PRINTER_KEY). + build()). + membership(new MembershipTO.Builder(GROUP_KEY). + plainAttr(new Attr.Builder("obscure").value("membership secret").build()). + build()). + build(); + + AnyObject anyObject = entityFactory.newEntity(AnyObject.class); + binder.create(anyObject, anyObjectCR); + anyObject = anyObjectDAO.save(anyObject); + entityManager.flush(); + + AnyObject actual = anyObjectDAO.findById(anyObject.getKey()).orElseThrow(); + assertEquals("fullPrinter", actual.getName()); + assertEquals("PRINTER", actual.getType().getKey()); + assertEquals("/even/two", actual.getRealm().getFullPath()); + assertEquals(VERDI_KEY, actual.getuManager().getKey()); + assertNull(actual.getgManager()); + + assertEquals(2, actual.getAuxClasses().size()); + assertTrue(actual.getAuxClasses().stream().anyMatch(auxClass -> "other".equals(auxClass.getKey()))); + assertTrue(actual.getAuxClasses().stream().anyMatch(auxClass -> "csv".equals(auxClass.getKey()))); + + assertEquals("Full model", actual.getPlainAttr("model").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals("3rd floor", actual.getPlainAttr("location").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals("G", actual.getPlainAttr("ctype").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals("42", actual.getPlainAttr("aLong").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals("true", actual.getPlainAttr("cool").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals("M", actual.getPlainAttr("gender").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals(2, actual.getPlainAttr("loginDate").orElseThrow().getValuesAsStrings().size()); + assertTrue(actual.getPlainAttr("uselessReadonly").isEmpty()); + assertTrue(actual.getPlainAttr("not-existing").isEmpty()); + + assertEquals(1, actual.getResources().size()); + assertEquals("ws-target-resource-1", actual.getResources().getFirst().getKey()); + + assertNotNull(actual.getRelationship( + relationshipTypeDAO.findById("inclusion").orElseThrow(), OTHER_PRINTER_KEY).orElseThrow()); + + assertNotNull(actual.getMembership(GROUP_KEY).orElseThrow()); + assertTrue(encryptorManager.getInstance().verify( + "membership secret", + plainSchemaDAO.findById("obscure").orElseThrow().getCipherAlgorithm(), + actual.getPlainAttr("obscure", actual.getMembership(GROUP_KEY).orElseThrow()). + orElseThrow().getValuesAsStrings().getFirst())); + } + + @Test + void updateFull() { + AnyObjectUR anyObjectUR = new AnyObjectUR.Builder(PRINTER_KEY). + name(new StringReplacePatchItem.Builder().value("HP LJ 1300n updated").build()). + realm(new StringReplacePatchItem.Builder().value("/even/two").build()). + auxClass(new StringPatchItem.Builder().value("other").operation(PatchOperation.ADD_REPLACE).build()). + auxClass(new StringPatchItem.Builder().value("csv").operation(PatchOperation.ADD_REPLACE).build()). + auxClass(new StringPatchItem.Builder().value("not-existing"). + operation(PatchOperation.DELETE). + build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("model").value("Updated model").build()).build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("location").build()). + operation(PatchOperation.DELETE). + build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("ctype").value("F").build()).build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("aLong").value("42").build()).build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("cool").value("false").build()).build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("gender").value("F").build()).build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("not-existing").value("ignored").build()).build()). + resource(new StringPatchItem.Builder(). + value("ws-target-resource-2"). + operation(PatchOperation.ADD_REPLACE). + build()). + resource(new StringPatchItem.Builder(). + value("not-existing"). + operation(PatchOperation.ADD_REPLACE). + build()). + build(); + + PropagationByResource propByRes = binder.update( + anyObjectDAO.findById(PRINTER_KEY).orElseThrow(), anyObjectUR); + assertNotNull(propByRes); + entityManager.flush(); + + AnyObject actual = anyObjectDAO.findById(PRINTER_KEY).orElseThrow(); + assertEquals("HP LJ 1300n updated", actual.getName()); + assertEquals("/even/two", actual.getRealm().getFullPath()); + assertTrue(actual.getAuxClasses().stream().anyMatch(auxClass -> "other".equals(auxClass.getKey()))); + assertTrue(actual.getAuxClasses().stream().anyMatch(auxClass -> "csv".equals(auxClass.getKey()))); + assertEquals("Updated model", actual.getPlainAttr("model").orElseThrow().getValuesAsStrings().getFirst()); + assertTrue(actual.getPlainAttr("location").isEmpty()); + assertEquals("F", actual.getPlainAttr("ctype").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals("42", actual.getPlainAttr("aLong").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals("false", actual.getPlainAttr("cool").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals("F", actual.getPlainAttr("gender").orElseThrow().getValuesAsStrings().getFirst()); + assertTrue(actual.getResources().stream(). + anyMatch(resource -> "ws-target-resource-2".equals(resource.getKey()))); + + AnyObjectTO updated = binder.getAnyObjectTO(actual, true); + assertEquals("HP LJ 1300n updated", updated.getName()); + assertEquals("/even/two", updated.getRealm()); + } + + @Test + void updateMembershipsAndRelationships() { + AnyObjectUR addMembership = new AnyObjectUR.Builder(PRINTER_KEY). + relationship(new RelationshipUR.Builder("inclusion"). + otherEnd(THIRD_PRINTER_KEY). + build()). + relationship(new RelationshipUR.Builder("inclusion"). + otherEnd(OTHER_PRINTER_KEY). + operation(PatchOperation.DELETE). + build()). + membership(new MembershipUR.Builder(GROUP_KEY). + plainAttr(new Attr.Builder("obscure").value("membership secret").build()). + build()). + build(); + + binder.update(anyObjectDAO.findById(PRINTER_KEY).orElseThrow(), addMembership); + entityManager.flush(); + + AnyObject updated = anyObjectDAO.findById(PRINTER_KEY).orElseThrow(); + assertNotNull(updated.getRelationship( + relationshipTypeDAO.findById("inclusion").orElseThrow(), THIRD_PRINTER_KEY).orElseThrow()); + assertTrue(updated.getRelationship( + relationshipTypeDAO.findById("inclusion").orElseThrow(), OTHER_PRINTER_KEY).isEmpty()); + assertNotNull(updated.getMembership(GROUP_KEY).orElseThrow()); + + AnyObjectUR removeMembership = new AnyObjectUR.Builder(PRINTER_KEY). + membership(new MembershipUR.Builder(GROUP_KEY). + operation(PatchOperation.DELETE). + build()). + build(); + binder.update(anyObjectDAO.findById(PRINTER_KEY).orElseThrow(), removeMembership); + entityManager.flush(); + + AnyObject cleared = anyObjectDAO.findById(PRINTER_KEY).orElseThrow(); + assertTrue(cleared.getMembership(GROUP_KEY).isEmpty()); + } + + @Test + void updateManagers() { + AnyObjectUR setUserManager = new AnyObjectUR.Builder(PRINTER_KEY). + uManager(new StringReplacePatchItem.Builder().value(VERDI_KEY).build()). + build(); + binder.update(anyObjectDAO.findById(PRINTER_KEY).orElseThrow(), setUserManager); + entityManager.flush(); + + AnyObject managedByUser = anyObjectDAO.findById(PRINTER_KEY).orElseThrow(); + assertEquals(VERDI_KEY, managedByUser.getuManager().getKey()); + assertNull(managedByUser.getgManager()); + + AnyObjectUR setGroupManager = new AnyObjectUR.Builder(PRINTER_KEY). + uManager(new StringReplacePatchItem.Builder().value(null).build()). + gManager(new StringReplacePatchItem.Builder().value(GROUP_KEY).build()). + build(); + binder.update(anyObjectDAO.findById(PRINTER_KEY).orElseThrow(), setGroupManager); + entityManager.flush(); + + AnyObject managedByGroup = anyObjectDAO.findById(PRINTER_KEY).orElseThrow(); + assertNull(managedByGroup.getuManager()); + assertEquals(GROUP_KEY, managedByGroup.getgManager().getKey()); + } + + @Test + void invalidCreate() { + assertThrows(SyncopeClientException.class, () -> binder.create( + entityFactory.newEntity(AnyObject.class), + new AnyObjectCR.Builder("/even/two", "not-existing", "test").build())); + assertThrows(SyncopeClientException.class, () -> binder.create( + entityFactory.newEntity(AnyObject.class), + new AnyObjectCR.Builder("/even/three", "PRINTER", "test").build())); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AnyTypeClassDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AnyTypeClassDataBinderTest.java new file mode 100644 index 00000000000..f19558dac83 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AnyTypeClassDataBinderTest.java @@ -0,0 +1,93 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.apache.syncope.common.lib.to.AnyTypeClassTO; +import org.apache.syncope.common.lib.to.PlainSchemaTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.provisioning.api.data.AnyTypeClassDataBinder; +import org.apache.syncope.core.provisioning.api.data.SchemaDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class AnyTypeClassDataBinderTest extends AbstractDataBinderTest { + + @Autowired + private AnyTypeClassDataBinder binder; + + @Autowired + private AnyTypeClassDAO anyTypeClassDAO; + + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + @Autowired + private SchemaDataBinder schemaDataBinder; + + @Test + void get() { + AnyTypeClassTO user = binder.getAnyTypeClassTO(anyTypeClassDAO.findById("minimal user").orElseThrow()); + assertEquals("minimal user", user.getKey()); + assertTrue(user.getPlainSchemas().containsAll(List.of("fullname", "userId", "firstname", "surname", "email"))); + assertTrue(user.getDerSchemas().containsAll(List.of("cn", "info"))); + assertTrue(user.getInUseByTypes().contains(AnyTypeKind.USER.name())); + } + + @Test + void createUpdate() { + PlainSchemaTO schemaTO = new PlainSchemaTO(); + schemaTO.setKey("testSchema"); + schemaTO.setType(AttrSchemaType.String); + schemaTO.setMultivalue(false); + schemaTO.setUniqueConstraint(false); + schemaTO.setReadonly(false); + plainSchemaDAO.save(schemaDataBinder.create(schemaTO)); + + AnyTypeClassTO anyTypeClassTO = new AnyTypeClassTO(); + anyTypeClassTO.setKey("testClass"); + anyTypeClassTO.getPlainSchemas().add("testSchema"); + anyTypeClassTO.getPlainSchemas().add("ctype"); + anyTypeClassTO.getPlainSchemas().add("not-existing"); + anyTypeClassTO.getDerSchemas().add("not-existing"); + + AnyTypeClass anyTypeClass = binder.create(anyTypeClassTO); + anyTypeClass = anyTypeClassDAO.save(anyTypeClass); + + AnyTypeClassTO created = binder.getAnyTypeClassTO(anyTypeClass); + assertEquals("testClass", created.getKey()); + assertEquals(List.of("testSchema"), created.getPlainSchemas()); + assertTrue(created.getDerSchemas().isEmpty()); + assertEquals(anyTypeClass, plainSchemaDAO.findById("testSchema").orElseThrow().getAnyTypeClass()); + + created.getPlainSchemas().clear(); + binder.update(anyTypeClass, created); + + assertTrue(binder.getAnyTypeClassTO(anyTypeClass).getPlainSchemas().isEmpty()); + assertNull(plainSchemaDAO.findById("testSchema").orElseThrow().getAnyTypeClass()); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderTest.java new file mode 100644 index 00000000000..636b0a1bb7b --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderTest.java @@ -0,0 +1,91 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.AnyTypeTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.provisioning.api.data.AnyTypeDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class AnyTypeDataBinderTest extends AbstractDataBinderTest { + + @Autowired + private AnyTypeDataBinder binder; + + @Autowired + private AnyTypeDAO anyTypeDAO; + + @Test + void get() { + AnyTypeTO user = binder.getAnyTypeTO(anyTypeDAO.findById(AnyTypeKind.USER.name()).orElseThrow()); + assertEquals(AnyTypeKind.USER.name(), user.getKey()); + assertEquals(AnyTypeKind.USER, user.getKind()); + assertTrue(user.getClasses().containsAll(List.of("minimal user", "other"))); + + AnyTypeTO printer = binder.getAnyTypeTO(anyTypeDAO.findById("PRINTER").orElseThrow()); + assertEquals("PRINTER", printer.getKey()); + assertEquals(AnyTypeKind.ANY_OBJECT, printer.getKind()); + assertTrue(printer.getClasses().contains("minimal printer")); + } + + @Test + void createUpdateDelete() { + AnyTypeTO anyTypeTO = new AnyTypeTO(); + anyTypeTO.setKey("TEST_TYPE"); + anyTypeTO.setKind(AnyTypeKind.ANY_OBJECT); + anyTypeTO.getClasses().add("minimal printer"); + anyTypeTO.getClasses().add("invalid-class"); + + AnyType anyType = binder.create(anyTypeTO); + anyType = anyTypeDAO.save(anyType); + + AnyTypeTO created = binder.getAnyTypeTO(anyType); + assertEquals("TEST_TYPE", created.getKey()); + assertEquals(List.of("minimal printer"), created.getClasses()); + + created.getClasses().clear(); + created.getClasses().add("csv"); + binder.update(anyType, created); + assertEquals(List.of("csv"), binder.getAnyTypeTO(anyType).getClasses()); + + AnyTypeTO deleted = binder.delete(anyType); + assertEquals("TEST_TYPE", deleted.getKey()); + assertEquals(List.of("csv"), deleted.getClasses()); + } + + @Test + void kindCannotChange() { + AnyType user = anyTypeDAO.findById(AnyTypeKind.USER.name()).orElseThrow(); + + AnyTypeTO anyTypeTO = new AnyTypeTO(); + anyTypeTO.setKey(AnyTypeKind.USER.name()); + anyTypeTO.setKind(AnyTypeKind.GROUP); + + assertThrows(SyncopeClientException.class, () -> binder.update(user, anyTypeTO)); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AttrRepoDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AttrRepoDataBinderTest.java new file mode 100644 index 00000000000..cd67a9d98bd --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AttrRepoDataBinderTest.java @@ -0,0 +1,109 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.AttrRepoTO; +import org.apache.syncope.common.lib.to.Item; +import org.apache.syncope.common.lib.types.AttrRepoState; +import org.apache.syncope.common.lib.types.MappingPurpose; +import org.apache.syncope.core.persistence.api.dao.AttrRepoDAO; +import org.apache.syncope.core.persistence.api.entity.am.AttrRepo; +import org.apache.syncope.core.provisioning.api.data.AttrRepoDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class AttrRepoDataBinderTest extends AbstractDataBinderTest { + + @Autowired + private AttrRepoDataBinder binder; + + @Autowired + private AttrRepoDAO attrRepoDAO; + + @Test + void get() { + AttrRepoTO attrRepo = binder.getAttrRepoTO( + attrRepoDAO.findById("DefaultStubAttrRepo").orElseThrow()); + assertEquals("DefaultStubAttrRepo", attrRepo.getKey()); + assertEquals("Stub attr repo", attrRepo.getDescription()); + assertEquals(AttrRepoState.ACTIVE, attrRepo.getState()); + assertEquals(1, attrRepo.getItems().size()); + assertEquals("attr1", attrRepo.getItems().getFirst().getIntAttrName()); + assertEquals(MappingPurpose.NONE, attrRepo.getItems().getFirst().getPurpose()); + } + + @Test + void createUpdate() { + AttrRepoTO attrRepoTO = new AttrRepoTO(); + attrRepoTO.setKey("testAttrRepo"); + attrRepoTO.setDescription("a test attr repo"); + attrRepoTO.setState(AttrRepoState.STANDBY); + attrRepoTO.setOrder(5); + attrRepoTO.setConf(binder.getAttrRepoTO( + attrRepoDAO.findById("DefaultStubAttrRepo").orElseThrow()).getConf()); + + Item item = new Item(); + item.setIntAttrName("email"); + item.setExtAttrName("mail"); + item.setMandatoryCondition("false"); + attrRepoTO.getItems().add(item); + + AttrRepo attrRepo = attrRepoDAO.save(binder.create(attrRepoTO)); + + AttrRepoTO created = binder.getAttrRepoTO(attrRepo); + assertEquals("testAttrRepo", created.getKey()); + assertEquals("a test attr repo", created.getDescription()); + assertEquals(AttrRepoState.STANDBY, created.getState()); + assertEquals(5, created.getOrder()); + assertEquals(1, created.getItems().size()); + + created.setDescription("an updated test attr repo"); + created.getItems().getFirst().setExtAttrName("email"); + binder.update(attrRepo, created); + + AttrRepoTO updated = binder.getAttrRepoTO(attrRepo); + assertEquals("an updated test attr repo", updated.getDescription()); + assertEquals("email", updated.getItems().getFirst().getExtAttrName()); + } + + @Test + void invalidItems() { + AttrRepoTO noIntAttrName = new AttrRepoTO(); + noIntAttrName.setKey("testAttrRepo"); + noIntAttrName.getItems().add(new Item()); + assertThrows(SyncopeClientException.class, () -> binder.create(noIntAttrName)); + + AttrRepoTO invalidMandatoryCondition = new AttrRepoTO(); + invalidMandatoryCondition.setKey("testAttrRepo"); + Item item = new Item(); + item.setIntAttrName("email"); + item.setMandatoryCondition("1 +"); + invalidMandatoryCondition.getItems().add(item); + assertThrows(SyncopeClientException.class, () -> binder.create(invalidMandatoryCondition)); + + AttrRepoTO nullItem = new AttrRepoTO(); + nullItem.setKey("testAttrRepo"); + nullItem.getItems().add(null); + assertThrows(SyncopeClientException.class, () -> binder.create(nullItem)); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AuditDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AuditDataBinderTest.java new file mode 100644 index 00000000000..96d386edb03 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AuditDataBinderTest.java @@ -0,0 +1,53 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.syncope.common.lib.to.AuditConfTO; +import org.apache.syncope.core.persistence.api.entity.AuditConf; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.provisioning.api.data.AuditDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class AuditDataBinderTest extends AbstractDataBinderTest { + + @Autowired + private AuditDataBinder binder; + + @Autowired + private EntityFactory entityFactory; + + @Test + void getAuditConfTO() { + AuditConf auditConf = entityFactory.newEntity(AuditConf.class); + auditConf.setKey("test"); + auditConf.setActive(true); + + AuditConfTO auditConfTO = binder.getAuditConfTO(auditConf); + assertEquals("test", auditConfTO.getKey()); + assertTrue(auditConfTO.isActive()); + + auditConf.setActive(false); + assertFalse(binder.getAuditConfTO(auditConf).isActive()); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AuthModuleDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AuthModuleDataBinderTest.java new file mode 100644 index 00000000000..21090b06859 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AuthModuleDataBinderTest.java @@ -0,0 +1,109 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.AuthModuleTO; +import org.apache.syncope.common.lib.to.Item; +import org.apache.syncope.common.lib.types.AuthModuleState; +import org.apache.syncope.common.lib.types.MappingPurpose; +import org.apache.syncope.core.persistence.api.dao.AuthModuleDAO; +import org.apache.syncope.core.persistence.api.entity.am.AuthModule; +import org.apache.syncope.core.provisioning.api.data.AuthModuleDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class AuthModuleDataBinderTest extends AbstractDataBinderTest { + + @Autowired + private AuthModuleDataBinder binder; + + @Autowired + private AuthModuleDAO authModuleDAO; + + @Test + void get() { + AuthModuleTO authModule = binder.getAuthModuleTO( + authModuleDAO.findById("DefaultLDAPAuthModule").orElseThrow()); + assertEquals("DefaultLDAPAuthModule", authModule.getKey()); + assertEquals("LDAP auth module", authModule.getDescription()); + assertEquals(AuthModuleState.ACTIVE, authModule.getState()); + assertEquals(4, authModule.getItems().size()); + assertTrue(authModule.getItems().stream(). + anyMatch(item -> "mail".equals(item.getIntAttrName()) + && "email".equals(item.getExtAttrName()) + && MappingPurpose.NONE == item.getPurpose())); + } + + @Test + void createUpdate() { + AuthModuleTO authModuleTO = new AuthModuleTO(); + authModuleTO.setKey("testAuthModule"); + authModuleTO.setDescription("a test auth module"); + authModuleTO.setState(AuthModuleState.STANDBY); + authModuleTO.setOrder(3); + authModuleTO.setConf(binder.getAuthModuleTO( + authModuleDAO.findById("DefaultJDBCAuthModule").orElseThrow()).getConf()); + + Item item = new Item(); + item.setIntAttrName("username"); + item.setExtAttrName("uid"); + item.setConnObjectKey(true); + item.setMandatoryCondition("true"); + authModuleTO.getItems().add(item); + + AuthModule authModule = authModuleDAO.save(binder.create(authModuleTO)); + + AuthModuleTO created = binder.getAuthModuleTO(authModule); + assertEquals("testAuthModule", created.getKey()); + assertEquals("a test auth module", created.getDescription()); + assertEquals(AuthModuleState.STANDBY, created.getState()); + assertEquals(3, created.getOrder()); + assertEquals(1, created.getItems().size()); + assertTrue(created.getItems().getFirst().isConnObjectKey()); + + created.setDescription("an updated test auth module"); + created.getItems().getFirst().setExtAttrName("username"); + binder.update(authModule, created); + + AuthModuleTO updated = binder.getAuthModuleTO(authModule); + assertEquals("an updated test auth module", updated.getDescription()); + assertEquals("username", updated.getItems().getFirst().getExtAttrName()); + } + + @Test + void invalidItems() { + AuthModuleTO noIntAttrName = new AuthModuleTO(); + noIntAttrName.setKey("testAuthModule"); + noIntAttrName.getItems().add(new Item()); + assertThrows(SyncopeClientException.class, () -> binder.create(noIntAttrName)); + + AuthModuleTO invalidMandatoryCondition = new AuthModuleTO(); + invalidMandatoryCondition.setKey("testAuthModule"); + Item item = new Item(); + item.setIntAttrName("username"); + item.setMandatoryCondition("1 +"); + invalidMandatoryCondition.getItems().add(item); + assertThrows(SyncopeClientException.class, () -> binder.create(invalidMandatoryCondition)); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AuthProfileDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AuthProfileDataBinderTest.java new file mode 100644 index 00000000000..498b90306d6 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/AuthProfileDataBinderTest.java @@ -0,0 +1,105 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.LocalDateTime; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import org.apache.syncope.common.lib.to.AuthProfileTO; +import org.apache.syncope.common.lib.wa.GoogleMfaAuthAccount; +import org.apache.syncope.common.lib.wa.GoogleMfaAuthToken; +import org.apache.syncope.common.lib.wa.ImpersonationAccount; +import org.apache.syncope.common.lib.wa.MfaTrustedDevice; +import org.apache.syncope.common.lib.wa.WAConsentDecision; +import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential; +import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO; +import org.apache.syncope.core.persistence.api.entity.am.AuthProfile; +import org.apache.syncope.core.provisioning.api.data.AuthProfileDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class AuthProfileDataBinderTest extends AbstractDataBinderTest { + + @Autowired + private AuthProfileDataBinder binder; + + @Autowired + private AuthProfileDAO authProfileDAO; + + @Test + void createGetUpdate() { + AuthProfileTO authProfileTO = new AuthProfileTO(); + authProfileTO.setOwner("rossini"); + authProfileTO.getImpersonationAccounts().add( + new ImpersonationAccount.Builder().impersonated("verdi").build()); + authProfileTO.getGoogleMfaAuthTokens().add( + new GoogleMfaAuthToken.Builder().token(123456).issueDate(LocalDateTime.now()).build()); + authProfileTO.getGoogleMfaAuthAccounts().add(new GoogleMfaAuthAccount.Builder(). + id(1L).name("account").username("rossini").secretKey("secret"). + validationCode(1).source("test").registrationDate(ZonedDateTime.now()).build()); + + MfaTrustedDevice device = new MfaTrustedDevice(); + device.setId(1L); + device.setName("device"); + device.setDeviceFingerprint("fingerprint"); + device.setRecordKey("recordKey"); + device.setRecordDate(ZonedDateTime.now()); + device.setExpirationDate(ZonedDateTime.now().plusDays(1)); + authProfileTO.getMfaTrustedDevices().add(device); + + authProfileTO.getWebAuthnDeviceCredentials().add( + new WebAuthnDeviceCredential.Builder().identifier("identifier").json("{}").build()); + authProfileTO.getConsentDecisions().add( + new WAConsentDecision.Builder(1L, "rossini", "service", LocalDateTime.now()). + reminder(10L).reminderTimeUnit(ChronoUnit.DAYS).attributes("{}").build()); + + AuthProfile authProfile = authProfileDAO.save(binder.create(authProfileTO)); + entityManager.flush(); + + AuthProfileTO actual = binder.getAuthProfileTO( + authProfileDAO.findById(authProfile.getKey()).orElseThrow()); + assertNotNull(actual.getKey()); + assertEquals("rossini", actual.getOwner()); + assertEquals("verdi", actual.getImpersonationAccounts().getFirst().getImpersonated()); + assertEquals(123456, actual.getGoogleMfaAuthTokens().getFirst().getOtp()); + assertEquals("rossini", actual.getGoogleMfaAuthAccounts().getFirst().getUsername()); + assertEquals("device", actual.getMfaTrustedDevices().getFirst().getName()); + assertEquals("identifier", actual.getWebAuthnDeviceCredentials().getFirst().getIdentifier()); + assertEquals("service", actual.getConsentDecisions().getFirst().getService()); + + // clear everything + binder.update(authProfile, new AuthProfileTO()); + authProfileDAO.save(authProfile); + entityManager.flush(); + + AuthProfileTO cleared = binder.getAuthProfileTO( + authProfileDAO.findById(authProfile.getKey()).orElseThrow()); + assertEquals("rossini", cleared.getOwner()); + assertTrue(cleared.getImpersonationAccounts().isEmpty()); + assertTrue(cleared.getGoogleMfaAuthTokens().isEmpty()); + assertTrue(cleared.getGoogleMfaAuthAccounts().isEmpty()); + assertTrue(cleared.getMfaTrustedDevices().isEmpty()); + assertTrue(cleared.getWebAuthnDeviceCredentials().isEmpty()); + assertTrue(cleared.getConsentDecisions().isEmpty()); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderTest.java new file mode 100644 index 00000000000..6a648a7b4b7 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderTest.java @@ -0,0 +1,495 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; + +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Set; +import org.apache.syncope.common.lib.Attr; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.clientapps.DefaultUsernameAttributeProviderConf; +import org.apache.syncope.common.lib.policy.DefaultTicketExpirationPolicyConf; +import org.apache.syncope.common.lib.to.CASSPClientAppTO; +import org.apache.syncope.common.lib.to.ClientAppTO; +import org.apache.syncope.common.lib.to.OIDCRPClientAppTO; +import org.apache.syncope.common.lib.to.SAML2SPClientAppTO; +import org.apache.syncope.common.lib.types.LogoutType; +import org.apache.syncope.common.lib.types.MetadataCriteriaDirection; +import org.apache.syncope.common.lib.types.OIDCApplicationType; +import org.apache.syncope.common.lib.types.OIDCClientAuthenticationMethod; +import org.apache.syncope.common.lib.types.OIDCGrantType; +import org.apache.syncope.common.lib.types.OIDCResponseType; +import org.apache.syncope.common.lib.types.OIDCSubjectType; +import org.apache.syncope.common.lib.types.OIDCTokenEncryptionAlg; +import org.apache.syncope.common.lib.types.OIDCTokenEncryptionEncoding; +import org.apache.syncope.common.lib.types.OIDCTokenSigningAlg; +import org.apache.syncope.common.lib.types.SAML2BindingType; +import org.apache.syncope.common.lib.types.SAML2SPNameId; +import org.apache.syncope.common.lib.types.SigningCredentialType; +import org.apache.syncope.common.lib.types.XmlSecAlgorithm; +import org.apache.syncope.core.persistence.api.dao.CASSPClientAppDAO; +import org.apache.syncope.core.persistence.api.dao.OIDCOpEntityDAO; +import org.apache.syncope.core.persistence.api.dao.OIDCRPClientAppDAO; +import org.apache.syncope.core.persistence.api.dao.PolicyDAO; +import org.apache.syncope.core.persistence.api.dao.SAML2SPClientAppDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.am.CASSPClientApp; +import org.apache.syncope.core.persistence.api.entity.am.ClientApp; +import org.apache.syncope.core.persistence.api.entity.am.OIDCOpEntity; +import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp; +import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp; +import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; +import org.apache.syncope.core.provisioning.api.data.ClientAppDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class ClientAppDataBinderTest extends AbstractDataBinderTest { + + private static final String AUTH_POLICY_KEY = "b912a0d4-a890-416f-9ab8-84ab077eb028"; + + private static final String ACCESS_POLICY_KEY = "419935c7-deb3-40b3-8a9a-683037e523a2"; + + private static final String ATTR_RELEASE_POLICY_KEY = "319935c7-deb3-40b3-8a9a-683037e523a2"; + + private static final String ACCOUNT_POLICY_KEY = "20ab5a8c-4b0c-432c-b957-f7fb9784d9f7"; + + @Autowired + private ClientAppDataBinder binder; + + @Autowired + private SAML2SPClientAppDAO saml2SPClientAppDAO; + + @Autowired + private OIDCRPClientAppDAO oidcrpClientAppDAO; + + @Autowired + private CASSPClientAppDAO casspClientAppDAO; + + @Autowired + private OIDCOpEntityDAO oidcOpEntityDAO; + + @Autowired + private PolicyDAO policyDAO; + + @Autowired + private EntityFactory entityFactory; + + protected String ticketExpirationPolicy() { + TicketExpirationPolicy policy = entityFactory.newEntity(TicketExpirationPolicy.class); + policy.setName("test ticket expiration policy"); + DefaultTicketExpirationPolicyConf conf = new DefaultTicketExpirationPolicyConf(); + DefaultTicketExpirationPolicyConf.TGTConf tgtConf = new DefaultTicketExpirationPolicyConf.TGTConf(); + tgtConf.setMaxTimeToLiveInSeconds(600); + conf.setTgtConf(tgtConf); + policy.setConf(conf); + return policyDAO.save(policy).getKey(); + } + + protected SAML2SPClientAppTO saml2spTO(final String name, final long clientAppId) { + SAML2SPClientAppTO clientAppTO = new SAML2SPClientAppTO(); + clientAppTO.setName(name); + clientAppTO.setClientAppId(clientAppId); + clientAppTO.setEntityId(name + "-entity-id"); + clientAppTO.setMetadataLocation("https://idp.example.com/" + name + "/metadata"); + clientAppTO.setMetadataSignatureLocation("https://idp.example.com/" + name + "/signature"); + clientAppTO.setMetadataCriteriaPattern(".*"); + clientAppTO.setMetadataCriteriaDirection(MetadataCriteriaDirection.INCLUDE); + clientAppTO.setSubjectLocality("localhost"); + clientAppTO.setSigningCredentialType(SigningCredentialType.X509); + clientAppTO.setLogoutResponseBinding(SAML2BindingType.POST); + clientAppTO.setSignAssertions(true); + clientAppTO.setSignResponses(true); + clientAppTO.setEncryptionOptional(true); + clientAppTO.setEncryptAssertions(true); + clientAppTO.setRequireSignedRoot(true); + clientAppTO.setLogoutResponseEnabled(true); + clientAppTO.setEncryptAttributes(true); + clientAppTO.setSkipGeneratingAssertionNameId(true); + clientAppTO.setSkipGeneratingSubjectConfirmationInResponseTo(true); + clientAppTO.setSkipGeneratingResponseInResponseTo(true); + clientAppTO.setSkipGeneratingSubjectConfirmationNotOnOrAfter(true); + clientAppTO.setSkipGeneratingSubjectConfirmationRecipient(true); + clientAppTO.setSkipGeneratingSubjectConfirmationAddress(true); + clientAppTO.setSkipGeneratingSubjectConfirmationNotBefore(true); + clientAppTO.setSkipGeneratingSubjectConfirmationNameId(true); + clientAppTO.setSkipGeneratingNameIdQualifiers(true); + clientAppTO.setSkipGeneratingTransientNameId(true); + clientAppTO.setSkipValidatingAuthnRequest(true); + clientAppTO.setSkipGeneratingServiceProviderNameIdQualifier(true); + clientAppTO.setSkipGeneratingAuthenticatingAuthority(true); + clientAppTO.setSkipGeneratingNameIdQualifier(true); + clientAppTO.setSkipGeneratingSessionNotOnOrAfter(true); + clientAppTO.setValidateMetadataCertificates(true); + clientAppTO.setRequiredAuthenticationContextClass("urn:test:context"); + clientAppTO.setRequiredNameIdFormat(SAML2SPNameId.EMAIL_ADDRESS); + clientAppTO.setSkewAllowance("30"); + clientAppTO.setValidityUntil("2030-01-01"); + clientAppTO.setNameIdQualifier("nameIdQualifier"); + clientAppTO.setServiceProviderNameIdQualifier("serviceProviderNameIdQualifier"); + clientAppTO.getAssertionAudiences().add("audience1"); + clientAppTO.getAssertionAudiences().add("audience2"); + + clientAppTO.getSigningSignatureAlgorithms().add(XmlSecAlgorithm.AES_128); + clientAppTO.getSigningSignatureReferenceDigestMethods().add(XmlSecAlgorithm.AES_192); + clientAppTO.getEncryptionKeyAlgorithms().add(XmlSecAlgorithm.AES_256); + clientAppTO.getEncryptionDataAlgorithms().add(XmlSecAlgorithm.TRIPLEDES); + clientAppTO.getSigningSignatureBlackListedAlgorithms().add(XmlSecAlgorithm.DIFFIE_HELLMAN); + clientAppTO.getEncryptionBlackListedAlgorithms().add(XmlSecAlgorithm.SEED_128); + + return clientAppTO; + } + + protected OIDCRPClientAppTO oidcrpTO(final String name, final long clientAppId) { + OIDCRPClientAppTO clientAppTO = new OIDCRPClientAppTO(); + clientAppTO.setName(name); + clientAppTO.setClientAppId(clientAppId); + clientAppTO.setClientId(name + "-client-id"); + clientAppTO.setClientSecret("clientSecret"); + clientAppTO.setClientSecretExpiration(1234567L); + clientAppTO.setIdTokenIssuer("https://issuer.example.com"); + clientAppTO.setSignIdToken(true); + clientAppTO.setIdTokenSigningAlg(OIDCTokenSigningAlg.RS256); + clientAppTO.setEncryptIdToken(true); + clientAppTO.setIdTokenEncryptionAlg(OIDCTokenEncryptionAlg.RSA_OAEP); + clientAppTO.setIdTokenEncryptionEncoding(OIDCTokenEncryptionEncoding.A128GCM); + clientAppTO.setUserInfoSigningAlg(OIDCTokenSigningAlg.RS384); + clientAppTO.setUserInfoEncryptedResponseAlg(OIDCTokenEncryptionAlg.ECDH_ES); + clientAppTO.setUserInfoEncryptedResponseEncoding(OIDCTokenEncryptionEncoding.A256GCM); + clientAppTO.setJwtAccessToken(true); + clientAppTO.setBypassApprovalPrompt(false); + clientAppTO.setGenerateRefreshToken(false); + clientAppTO.setSubjectType(OIDCSubjectType.PAIRWISE); + clientAppTO.setApplicationType(OIDCApplicationType.NATIVE); + clientAppTO.getRedirectUris().add("https://client.example.com/callback"); + clientAppTO.getSupportedGrantTypes().add(OIDCGrantType.authorization_code); + clientAppTO.getSupportedGrantTypes().add(OIDCGrantType.refresh_token); + clientAppTO.getSupportedResponseTypes().add(OIDCResponseType.CODE); + clientAppTO.getScopes().add("openid"); + clientAppTO.getScopes().add("email"); + clientAppTO.setLogoutUri("https://client.example.com/logout"); + clientAppTO.setJwks("{}"); + clientAppTO.setJwksUri("https://client.example.com/jwks"); + clientAppTO.setTokenEndpointAuthenticationMethod(OIDCClientAuthenticationMethod.client_secret_basic); + clientAppTO.setAccessTokenMaxActiveTokens(5L); + clientAppTO.setAccessTokenMaxTimeToLive("PT1H"); + clientAppTO.setAccessTokenTimeToKill("PT30M"); + clientAppTO.setRefreshTokenMaxActiveTokens(3L); + clientAppTO.setRefreshTokenTimeToKill("PT2H"); + clientAppTO.setDeviceTokenTimeToKill("PT10M"); + clientAppTO.setJwtAccessTokenSigningAlg(OIDCTokenSigningAlg.RS512); + clientAppTO.setRenewRefreshToken(true); + clientAppTO.setIdTokenTimeToKill("PT1H"); + clientAppTO.setAuthorizationCodeTimeToLive("PT5M"); + clientAppTO.getAudience().add("audience1"); + clientAppTO.getAudience().add("audience2"); + return clientAppTO; + } + + @Test + void createGetUpdateSAML2SP() { + SAML2SPClientAppTO clientAppTO = saml2spTO("saml2sp", 1L); + clientAppTO.setRealm("/even/two"); + clientAppTO.setEvaluationOrder(2); + clientAppTO.setDescription("description"); + clientAppTO.setLogo("logo"); + clientAppTO.setTheme("theme"); + clientAppTO.setInformationUrl("https://example.com/info"); + clientAppTO.setPrivacyUrl("https://example.com/privacy"); + DefaultUsernameAttributeProviderConf usernameConf = new DefaultUsernameAttributeProviderConf(); + clientAppTO.setUsernameAttributeProviderConf(usernameConf); + clientAppTO.setAuthPolicy(AUTH_POLICY_KEY); + clientAppTO.setAccessPolicy(ACCESS_POLICY_KEY); + clientAppTO.setAttrReleasePolicy(ATTR_RELEASE_POLICY_KEY); + String ticketExpirationPolicy = ticketExpirationPolicy(); + clientAppTO.setTicketExpirationPolicy(ticketExpirationPolicy); + clientAppTO.getProperties().add(new Attr.Builder("key").value("value").build()); + clientAppTO.setLogoutType(LogoutType.FRONT_CHANNEL); + clientAppTO.setIdp("test-idp"); + + SAML2SPClientApp created = saml2SPClientAppDAO.save(binder.create(clientAppTO)); + entityManager.flush(); + + SAML2SPClientAppTO actual = binder.getClientAppTO( + saml2SPClientAppDAO.findById(created.getKey()).orElseThrow()); + assertEquals(created.getKey(), actual.getKey()); + assertEquals("/even/two", actual.getRealm()); + assertEquals("saml2sp", actual.getName()); + assertEquals(1L, actual.getClientAppId()); + assertEquals(2, actual.getEvaluationOrder()); + assertEquals("description", actual.getDescription()); + assertEquals("logo", actual.getLogo()); + assertEquals("theme", actual.getTheme()); + assertEquals("https://example.com/info", actual.getInformationUrl()); + assertEquals("https://example.com/privacy", actual.getPrivacyUrl()); + assertEquals(usernameConf, actual.getUsernameAttributeProviderConf()); + assertEquals(AUTH_POLICY_KEY, actual.getAuthPolicy()); + assertEquals(ACCESS_POLICY_KEY, actual.getAccessPolicy()); + assertEquals(ATTR_RELEASE_POLICY_KEY, actual.getAttrReleasePolicy()); + assertEquals(ticketExpirationPolicy, actual.getTicketExpirationPolicy()); + assertEquals(1, actual.getProperties().size()); + assertEquals(LogoutType.FRONT_CHANNEL, actual.getLogoutType()); + + assertEquals("test-idp", actual.getIdp()); + assertEquals("saml2sp-entity-id", actual.getEntityId()); + assertEquals("https://idp.example.com/saml2sp/metadata", actual.getMetadataLocation()); + assertEquals("https://idp.example.com/saml2sp/signature", actual.getMetadataSignatureLocation()); + assertEquals(".*", actual.getMetadataCriteriaPattern()); + assertEquals("localhost", actual.getSubjectLocality()); + assertEquals(MetadataCriteriaDirection.INCLUDE, actual.getMetadataCriteriaDirection()); + assertEquals(SigningCredentialType.X509, actual.getSigningCredentialType()); + assertEquals(SAML2BindingType.POST, actual.getLogoutResponseBinding()); + assertTrue(actual.isSignAssertions()); + assertTrue(actual.isSignResponses()); + assertTrue(actual.isEncryptionOptional()); + assertTrue(actual.isEncryptAssertions()); + assertTrue(actual.isRequireSignedRoot()); + assertTrue(actual.isLogoutResponseEnabled()); + assertTrue(actual.isEncryptAttributes()); + assertTrue(actual.isSkipGeneratingAssertionNameId()); + assertTrue(actual.isSkipGeneratingSubjectConfirmationInResponseTo()); + assertTrue(actual.isSkipGeneratingResponseInResponseTo()); + assertTrue(actual.isSkipGeneratingSubjectConfirmationNotOnOrAfter()); + assertTrue(actual.isSkipGeneratingSubjectConfirmationRecipient()); + assertTrue(actual.isSkipGeneratingSubjectConfirmationAddress()); + assertTrue(actual.isSkipGeneratingSubjectConfirmationNotBefore()); + assertTrue(actual.isSkipGeneratingSubjectConfirmationNameId()); + assertTrue(actual.isSkipGeneratingNameIdQualifiers()); + assertTrue(actual.isSkipGeneratingTransientNameId()); + assertTrue(actual.isSkipValidatingAuthnRequest()); + assertTrue(actual.isSkipGeneratingServiceProviderNameIdQualifier()); + assertTrue(actual.isSkipGeneratingAuthenticatingAuthority()); + assertTrue(actual.isSkipGeneratingNameIdQualifier()); + assertTrue(actual.isSkipGeneratingSessionNotOnOrAfter()); + assertTrue(actual.isValidateMetadataCertificates()); + assertEquals("urn:test:context", actual.getRequiredAuthenticationContextClass()); + assertEquals(SAML2SPNameId.EMAIL_ADDRESS, actual.getRequiredNameIdFormat()); + assertEquals("30", actual.getSkewAllowance()); + assertEquals("2030-01-01", actual.getValidityUntil()); + assertEquals("nameIdQualifier", actual.getNameIdQualifier()); + assertEquals("serviceProviderNameIdQualifier", actual.getServiceProviderNameIdQualifier()); + assertEquals(Set.of("audience1", "audience2"), Set.copyOf(actual.getAssertionAudiences())); + assertEquals(List.of(XmlSecAlgorithm.AES_128), actual.getSigningSignatureAlgorithms()); + assertEquals(List.of(XmlSecAlgorithm.AES_192), actual.getSigningSignatureReferenceDigestMethods()); + assertEquals(List.of(XmlSecAlgorithm.AES_256), actual.getEncryptionKeyAlgorithms()); + assertEquals(List.of(XmlSecAlgorithm.TRIPLEDES), actual.getEncryptionDataAlgorithms()); + assertEquals(List.of(XmlSecAlgorithm.DIFFIE_HELLMAN), actual.getSigningSignatureBlackListedAlgorithms()); + assertEquals(List.of(XmlSecAlgorithm.SEED_128), actual.getEncryptionBlackListedAlgorithms()); + + actual.setDescription("updated"); + actual.setAuthPolicy(null); + actual.setLogoutType(LogoutType.NONE); + actual.setIdp("Syncope"); + binder.update(created, actual); + saml2SPClientAppDAO.save(created); + entityManager.flush(); + + SAML2SPClientAppTO updated = binder.getClientAppTO( + saml2SPClientAppDAO.findById(created.getKey()).orElseThrow()); + assertEquals(created.getKey(), updated.getKey()); + assertEquals("updated", updated.getDescription()); + assertNull(updated.getAuthPolicy()); + assertEquals(LogoutType.NONE, updated.getLogoutType()); + // default owner is not overwritten + assertEquals("test-idp", updated.getIdp()); + } + + @Test + void invalidPolicies() { + SAML2SPClientAppTO auth = saml2spTO("invalid-auth", 10L); + auth.setAuthPolicy(ACCOUNT_POLICY_KEY); + assertThrows(SyncopeClientException.class, () -> binder.create(auth)); + + SAML2SPClientAppTO access = saml2spTO("invalid-access", 11L); + access.setAccessPolicy(ACCOUNT_POLICY_KEY); + assertThrows(SyncopeClientException.class, () -> binder.create(access)); + + SAML2SPClientAppTO attrRelease = saml2spTO("invalid-attr-release", 12L); + attrRelease.setAttrReleasePolicy(ACCOUNT_POLICY_KEY); + assertThrows(SyncopeClientException.class, () -> binder.create(attrRelease)); + + SAML2SPClientAppTO ticket = saml2spTO("invalid-ticket", 13L); + ticket.setTicketExpirationPolicy(ACCOUNT_POLICY_KEY); + assertThrows(SyncopeClientException.class, () -> binder.create(ticket)); + } + + @Test + void createGetUpdateOIDCRP() { + OIDCOpEntity oidcOpEntity = entityFactory.newEntity(OIDCOpEntity.class); + oidcOpEntity.setJWKS("jwks".getBytes(StandardCharsets.UTF_8)); + oidcOpEntity.getCustomScopes().put("custom-scope", Set.of("custom")); + oidcOpEntityDAO.save(oidcOpEntity); + entityManager.flush(); + + OIDCRPClientAppTO clientAppTO = oidcrpTO("oidcrp", 2L); + clientAppTO.getScopes().add("custom-scope"); + clientAppTO.setRealm("/even"); + + OIDCRPClientApp created = oidcrpClientAppDAO.save(binder.create(clientAppTO)); + entityManager.flush(); + + OIDCRPClientAppTO actual = binder.getClientAppTO( + oidcrpClientAppDAO.findById(created.getKey()).orElseThrow()); + assertEquals(created.getKey(), actual.getKey()); + assertEquals("/even", actual.getRealm()); + assertEquals("oidcrp", actual.getName()); + assertEquals(2L, actual.getClientAppId()); + assertEquals("oidcrp-client-id", actual.getClientId()); + assertEquals("clientSecret", actual.getClientSecret()); + assertEquals(1234567L, actual.getClientSecretExpiration()); + assertEquals("https://issuer.example.com", actual.getIdTokenIssuer()); + assertTrue(actual.isSignIdToken()); + assertEquals(OIDCTokenSigningAlg.RS256, actual.getIdTokenSigningAlg()); + assertTrue(actual.isEncryptIdToken()); + assertEquals(OIDCTokenEncryptionAlg.RSA_OAEP, actual.getIdTokenEncryptionAlg()); + assertEquals(OIDCTokenEncryptionEncoding.A128GCM, actual.getIdTokenEncryptionEncoding()); + assertEquals(OIDCTokenSigningAlg.RS384, actual.getUserInfoSigningAlg()); + assertEquals(OIDCTokenEncryptionAlg.ECDH_ES, actual.getUserInfoEncryptedResponseAlg()); + assertEquals(OIDCTokenEncryptionEncoding.A256GCM, actual.getUserInfoEncryptedResponseEncoding()); + assertTrue(actual.isJwtAccessToken()); + assertFalse(actual.isBypassApprovalPrompt()); + assertFalse(actual.isGenerateRefreshToken()); + assertEquals(OIDCSubjectType.PAIRWISE, actual.getSubjectType()); + assertEquals(OIDCApplicationType.NATIVE, actual.getApplicationType()); + assertEquals(List.of("https://client.example.com/callback"), actual.getRedirectUris()); + assertEquals( + Set.of(OIDCGrantType.authorization_code, OIDCGrantType.refresh_token), + Set.copyOf(actual.getSupportedGrantTypes())); + assertEquals(List.of(OIDCResponseType.CODE), actual.getSupportedResponseTypes()); + assertEquals(Set.of("openid", "email", "custom-scope"), Set.copyOf(actual.getScopes())); + assertEquals("https://client.example.com/logout", actual.getLogoutUri()); + assertEquals("{}", actual.getJwks()); + assertEquals("https://client.example.com/jwks", actual.getJwksUri()); + assertEquals(OIDCClientAuthenticationMethod.client_secret_basic, + actual.getTokenEndpointAuthenticationMethod()); + assertEquals(5L, actual.getAccessTokenMaxActiveTokens()); + assertEquals("PT1H", actual.getAccessTokenMaxTimeToLive()); + assertEquals("PT30M", actual.getAccessTokenTimeToKill()); + assertEquals(3L, actual.getRefreshTokenMaxActiveTokens()); + assertEquals("PT2H", actual.getRefreshTokenTimeToKill()); + assertEquals("PT10M", actual.getDeviceTokenTimeToKill()); + assertEquals(OIDCTokenSigningAlg.RS512, actual.getJwtAccessTokenSigningAlg()); + assertTrue(actual.getRenewRefreshToken()); + assertEquals("PT1H", actual.getIdTokenTimeToKill()); + assertEquals("PT5M", actual.getAuthorizationCodeTimeToLive()); + assertEquals(Set.of("audience1", "audience2"), Set.copyOf(actual.getAudience())); + + actual.getScopes().clear(); + actual.getScopes().add("profile"); + actual.setClientSecret(null); + actual.setRealm(null); + binder.update(created, actual); + oidcrpClientAppDAO.save(created); + entityManager.flush(); + + OIDCRPClientAppTO updated = binder.getClientAppTO( + oidcrpClientAppDAO.findById(created.getKey()).orElseThrow()); + assertEquals(Set.of("profile"), Set.copyOf(updated.getScopes())); + assertNull(updated.getClientSecret()); + assertNull(updated.getRealm()); + } + + @Test + void undefinedOIDCScope() { + OIDCRPClientAppTO clientAppTO = oidcrpTO("invalid-scope", 3L); + clientAppTO.getScopes().add("not-a-scope"); + + assertThrows(SyncopeClientException.class, () -> binder.create(clientAppTO)); + } + + @Test + void createGetUpdateCASSP() { + CASSPClientAppTO clientAppTO = new CASSPClientAppTO(); + clientAppTO.setName("cassp"); + clientAppTO.setClientAppId(4L); + clientAppTO.setServiceId("https://service.example.com"); + clientAppTO.setRealm("/even/two"); + clientAppTO.setAuthPolicy(AUTH_POLICY_KEY); + + CASSPClientApp created = casspClientAppDAO.save(binder.create(clientAppTO)); + entityManager.flush(); + + CASSPClientAppTO actual = binder.getClientAppTO( + casspClientAppDAO.findById(created.getKey()).orElseThrow()); + assertEquals(created.getKey(), actual.getKey()); + assertEquals("cassp", actual.getName()); + assertEquals(4L, actual.getClientAppId()); + assertEquals("https://service.example.com", actual.getServiceId()); + assertEquals("/even/two", actual.getRealm()); + assertEquals(AUTH_POLICY_KEY, actual.getAuthPolicy()); + + actual.setServiceId("https://service2.example.com"); + binder.update(created, actual); + casspClientAppDAO.save(created); + entityManager.flush(); + + CASSPClientAppTO updated = binder.getClientAppTO( + casspClientAppDAO.findById(created.getKey()).orElseThrow()); + assertEquals("https://service2.example.com", updated.getServiceId()); + } + + @Test + void unsupportedClientApp() { + ClientAppTO unsupportedTO = new ClientAppTO() { + + private static final long serialVersionUID = 1L; + + @Override + public String getDiscriminator() { + return "unsupported"; + } + }; + + assertThrows(IllegalArgumentException.class, () -> binder.create(unsupportedTO)); + + ClientApp clientApp = mock(ClientApp.class); + assertThrows(IllegalArgumentException.class, () -> binder.update(clientApp, unsupportedTO)); + assertThrows(IllegalArgumentException.class, () -> binder.getClientAppTO(clientApp)); + } + + @Test + void saml2spWithoutIdp() { + SAML2SPClientAppTO clientAppTO = saml2spTO("saml2sp-no-idp", 5L); + clientAppTO.setIdp(null); + + SAML2SPClientApp created = saml2SPClientAppDAO.save(binder.create(clientAppTO)); + entityManager.flush(); + + SAML2SPClientAppTO actual = binder.getClientAppTO( + saml2SPClientAppDAO.findById(created.getKey()).orElseThrow()); + assertNull(actual.getIdp()); + assertNotNull(actual.getKey()); + + // default owner is not stored + actual.setIdp("Syncope"); + binder.update(created, actual); + saml2SPClientAppDAO.save(created); + entityManager.flush(); + + assertNull(binder.getClientAppTO( + saml2SPClientAppDAO.findById(created.getKey()).orElseThrow()).getIdp()); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderTest.java index 9f7cae08ecd..a0a7648be6a 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderTest.java @@ -18,20 +18,31 @@ */ package org.apache.syncope.core.provisioning.java.data; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Set; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.ConnInstanceTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.ConnConfPropSchema; +import org.apache.syncope.common.lib.types.ConnConfProperty; +import org.apache.syncope.common.lib.types.ConnPoolConf; +import org.apache.syncope.common.lib.types.ConnectorCapability; import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO; +import org.apache.syncope.core.persistence.api.entity.ConnInstance; import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder; -import org.apache.syncope.core.provisioning.java.AbstractTest; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.transaction.annotation.Transactional; -@Transactional -public class ConnInstanceDataBinderTest extends AbstractTest { +class ConnInstanceDataBinderTest extends AbstractDataBinderTest { + + private static final String CONNECTOR_KEY = "88a7a819-dab5-46b4-9b90-0b9769eabdb8"; @Autowired private ConnInstanceDataBinder binder; @@ -39,18 +50,39 @@ public class ConnInstanceDataBinderTest extends AbstractTest { @Autowired private ConnInstanceDAO connInstanceDAO; + protected ConnConfProperty confProperty( + final String name, final Object value, final boolean confidential) { + + ConnConfPropSchema schema = new ConnConfPropSchema(); + schema.setName(name); + schema.setDisplayName(name); + schema.setHelpMessage(name + " help"); + schema.setType(value.getClass().getName()); + schema.setRequired(true); + schema.setOrder(1); + schema.setConfidential(confidential); + + ConnConfProperty property = new ConnConfProperty(); + property.setSchema(schema); + property.setOverridable(true); + property.getValues().add(value); + return property; + } + @Test - public void working() { + void working() { ConnInstanceTO connInstance = binder.getConnInstanceTO( - connInstanceDAO.findById("88a7a819-dab5-46b4-9b90-0b9769eabdb8").orElseThrow()); + connInstanceDAO.findById(CONNECTOR_KEY).orElseThrow()); assertNotNull(connInstance); assertFalse(connInstance.isErrored()); assertNotNull(connInstance.getLocation()); assertFalse(connInstance.getConf().isEmpty()); + assertEquals("ConnInstance100", connInstance.getDisplayName()); + assertEquals(SyncopeConstants.ROOT_REALM, connInstance.getAdminRealm()); } @Test - public void errored() { + void errored() { ConnInstanceTO connInstance = binder.getConnInstanceTO( connInstanceDAO.findById("413bf072-678a-41d3-9d20-8c453b3a39d1").orElseThrow()); assertNotNull(connInstance); @@ -58,4 +90,123 @@ public void errored() { assertNotNull(connInstance.getLocation()); assertTrue(connInstance.getConf().isEmpty()); } + + @Test + void create() { + ConnPoolConf poolConf = new ConnPoolConf(); + poolConf.setMaxObjects(10); + poolConf.setMaxIdle(5); + poolConf.setMinIdle(2); + poolConf.setMaxWait(1000L); + poolConf.setMinEvictableIdleTimeMillis(2000L); + + ConnInstanceTO connInstanceTO = new ConnInstanceTO(); + connInstanceTO.setLocation("connid://localhost"); + connInstanceTO.setBundleName("net.tirasa.connid.bundles.soap"); + connInstanceTO.setConnectorName("net.tirasa.connid.bundles.soap.WebServiceConnector"); + connInstanceTO.setVersion("1.5.0"); + connInstanceTO.setDisplayName("Test connector"); + connInstanceTO.setAdminRealm("/even"); + connInstanceTO.setConnRequestTimeout(30); + connInstanceTO.setPoolConf(poolConf); + connInstanceTO.getCapabilities().addAll( + Set.of(ConnectorCapability.CREATE, ConnectorCapability.UPDATE, ConnectorCapability.SEARCH)); + connInstanceTO.getConf().add(confProperty("endpoint", "http://localhost:8080/soap", false)); + connInstanceTO.getConf().add(confProperty("servicename", "Provisioning", false)); + + ConnInstance connInstance = binder.create(connInstanceTO); + connInstance = connInstanceDAO.save(connInstance); + entityManager.flush(); + + ConnInstanceTO actual = binder.getConnInstanceTO( + connInstanceDAO.findById(connInstance.getKey()).orElseThrow()); + assertEquals("connid://localhost", actual.getLocation()); + assertEquals("net.tirasa.connid.bundles.soap", actual.getBundleName()); + assertEquals("net.tirasa.connid.bundles.soap.WebServiceConnector", actual.getConnectorName()); + assertEquals("1.5.0", actual.getVersion()); + assertEquals("Test connector", actual.getDisplayName()); + assertEquals("/even", actual.getAdminRealm()); + assertEquals(30, actual.getConnRequestTimeout()); + assertEquals(Set.of(ConnectorCapability.CREATE, ConnectorCapability.UPDATE, + ConnectorCapability.SEARCH), actual.getCapabilities()); + assertEquals(2, actual.getConf().size()); + assertEquals("http://localhost:8080/soap", + actual.getConf().getFirst().getValues().getFirst()); + assertNotNull(actual.getPoolConf()); + assertEquals(10, actual.getPoolConf().getMaxObjects()); + assertEquals(5, actual.getPoolConf().getMaxIdle()); + assertEquals(2, actual.getPoolConf().getMinIdle()); + assertEquals(1000L, actual.getPoolConf().getMaxWait()); + assertEquals(2000L, actual.getPoolConf().getMinEvictableIdleTimeMillis()); + } + + @Test + void update() { + ConnInstance variant = connInstanceDAO.findById(CONNECTOR_KEY).orElseThrow(); + ConnInstanceTO connInstanceTO = binder.getConnInstanceTO(variant); + + ConnPoolConf poolConf = new ConnPoolConf(); + poolConf.setMaxObjects(20); + connInstanceTO.setPoolConf(poolConf); + + connInstanceTO.setLocation("connid://updated"); + connInstanceTO.setBundleName("net.tirasa.connid.bundles.db"); + connInstanceTO.setConnectorName("net.tirasa.connid.bundles.db.table.DatabaseTableConnector"); + connInstanceTO.setVersion("2.5.0"); + connInstanceTO.setDisplayName("Updated connector"); + connInstanceTO.setAdminRealm("/even/two"); + connInstanceTO.setConnRequestTimeout(60); + connInstanceTO.getCapabilities().clear(); + connInstanceTO.getCapabilities().addAll(Set.of(ConnectorCapability.AUTHENTICATE)); + connInstanceTO.getConf().clear(); + connInstanceTO.getConf().add(confProperty(AnyTypeKind.USER.name(), "sa", false)); + connInstanceTO.getConf().add(confProperty("keyColumn", "id", false)); + + binder.update(connInstanceTO); + entityManager.flush(); + + ConnInstanceTO actual = binder.getConnInstanceTO( + connInstanceDAO.findById(CONNECTOR_KEY).orElseThrow()); + assertEquals("connid://updated", actual.getLocation()); + assertEquals("net.tirasa.connid.bundles.db", actual.getBundleName()); + assertEquals("net.tirasa.connid.bundles.db.table.DatabaseTableConnector", actual.getConnectorName()); + assertEquals("2.5.0", actual.getVersion()); + assertEquals("Updated connector", actual.getDisplayName()); + assertEquals("/even/two", actual.getAdminRealm()); + assertEquals(60, actual.getConnRequestTimeout()); + assertEquals(Set.of(ConnectorCapability.AUTHENTICATE), actual.getCapabilities()); + assertEquals(2, actual.getConf().size()); + assertEquals("sa", actual.getConf().getFirst().getValues().getFirst()); + assertNotNull(actual.getPoolConf()); + assertEquals(20, actual.getPoolConf().getMaxObjects()); + + // setting no pool conf removes it + actual.setPoolConf(null); + binder.update(actual); + entityManager.flush(); + + ConnInstanceTO withoutPool = binder.getConnInstanceTO( + connInstanceDAO.findById(CONNECTOR_KEY).orElseThrow()); + assertNull(withoutPool.getPoolConf()); + } + + @Test + void invalid() { + ConnInstanceTO missing = new ConnInstanceTO(); + assertThrows(SyncopeClientException.class, () -> binder.create(missing)); + + ConnInstanceTO invalidRealm = new ConnInstanceTO(); + invalidRealm.setLocation("connid://localhost"); + invalidRealm.setBundleName("net.tirasa.connid.bundles.soap"); + invalidRealm.setConnectorName("net.tirasa.connid.bundles.soap.WebServiceConnector"); + invalidRealm.setVersion("1.5.0"); + invalidRealm.setAdminRealm("/even/notexisting"); + invalidRealm.getConf().add(confProperty("endpoint", "http://localhost:8080/soap", false)); + assertThrows(SyncopeClientException.class, () -> binder.create(invalidRealm)); + + ConnInstanceTO valid = binder.getConnInstanceTO( + connInstanceDAO.findById(CONNECTOR_KEY).orElseThrow()); + valid.setAdminRealm("/even/notexisting"); + assertThrows(SyncopeClientException.class, () -> binder.update(valid)); + } } diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/DelegationDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/DelegationDataBinderTest.java new file mode 100644 index 00000000000..b687e2b4319 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/DelegationDataBinderTest.java @@ -0,0 +1,103 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.OffsetDateTime; +import java.util.List; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.DelegationTO; +import org.apache.syncope.core.persistence.api.dao.DelegationDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.entity.Delegation; +import org.apache.syncope.core.provisioning.api.data.DelegationDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class DelegationDataBinderTest extends AbstractDataBinderTest { + + private static final String DELEGATING = "1417acbe-cbf6-4277-9372-e75e04f97000"; + + private static final String DELEGATED = "74cd8ece-715a-44a4-a736-e17b46c4e7e6"; + + @Autowired + private DelegationDataBinder binder; + + @Autowired + private DelegationDAO delegationDAO; + + @Test + void createUpdate() { + DelegationTO delegationTO = new DelegationTO(); + delegationTO.setDelegating(DELEGATING); + delegationTO.setDelegated(DELEGATED); + delegationTO.setStart(OffsetDateTime.now()); + delegationTO.setEnd(OffsetDateTime.now().plusDays(1)); + delegationTO.getRoles().add("Other"); + + Delegation delegation = delegationDAO.save(binder.create(delegationTO)); + + DelegationTO created = binder.getDelegationTO(delegation); + assertEquals(DELEGATING, created.getDelegating()); + assertEquals(DELEGATED, created.getDelegated()); + assertEquals(List.of("Other"), List.copyOf(created.getRoles())); + assertEquals(delegationTO.getStart(), created.getStart()); + assertEquals(delegationTO.getEnd(), created.getEnd()); + + created.setEnd(OffsetDateTime.now().plusDays(2)); + created.setStart(OffsetDateTime.now().minusDays(1)); + binder.update(delegation, created); + + DelegationTO updated = binder.getDelegationTO(delegation); + assertEquals(created.getStart(), updated.getStart()); + assertEquals(created.getEnd(), updated.getEnd()); + assertTrue(updated.getRoles().contains("Other")); + } + + @Test + void invalidRoles() { + DelegationTO notOwned = new DelegationTO(); + notOwned.setDelegating(DELEGATING); + notOwned.setDelegated(DELEGATED); + notOwned.getRoles().add("User reviewer"); + assertThrows(SyncopeClientException.class, () -> binder.create(notOwned)); + + DelegationTO notExisting = new DelegationTO(); + notExisting.setDelegating(DELEGATING); + notExisting.setDelegated(DELEGATED); + notExisting.getRoles().add("not-existing"); + assertThrows(SyncopeClientException.class, () -> binder.create(notExisting)); + } + + @Test + void invalidUsers() { + DelegationTO noDelegating = new DelegationTO(); + noDelegating.setDelegating("not-existing"); + noDelegating.setDelegated(DELEGATED); + assertThrows(NotFoundException.class, () -> binder.create(noDelegating)); + + DelegationTO noDelegated = new DelegationTO(); + noDelegated.setDelegating(DELEGATING); + noDelegated.setDelegated("not-existing"); + assertThrows(NotFoundException.class, () -> binder.create(noDelegated)); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/FIQLQueryDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/FIQLQueryDataBinderTest.java new file mode 100644 index 00000000000..54bbee54d39 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/FIQLQueryDataBinderTest.java @@ -0,0 +1,148 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.to.FIQLQueryTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.dao.FIQLQueryDAO; +import org.apache.syncope.core.persistence.api.entity.FIQLQuery; +import org.apache.syncope.core.provisioning.api.data.FIQLQueryDataBinder; +import org.apache.syncope.core.spring.security.SyncopeAuthenticationDetails; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.User; + +class FIQLQueryDataBinderTest extends AbstractDataBinderTest { + + private static final String OWNER = "rossini"; + + @Autowired + private FIQLQueryDataBinder binder; + + @Autowired + private FIQLQueryDAO fiqlQueryDAO; + + protected void authenticateAs(final String username) { + var authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities(); + + UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken( + new User(username, "FAKE_PASSWORD", authorities), "FAKE_PASSWORD", authorities); + auth.setDetails(new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, null)); + SecurityContextHolder.getContext().setAuthentication(auth); + } + + protected void authenticateAsAdmin() { + authenticateAs("admin"); + } + + @Test + void createGet() { + authenticateAs(OWNER); + try { + FIQLQueryTO fiqlQueryTO = new FIQLQueryTO(); + fiqlQueryTO.setName("my query"); + fiqlQueryTO.setTarget(AnyTypeKind.USER.name()); + fiqlQueryTO.setFiql("username==rossini"); + + FIQLQuery fiqlQuery = binder.create(fiqlQueryTO); + fiqlQuery = fiqlQueryDAO.save(fiqlQuery); + entityManager.flush(); + + assertNotNull(fiqlQuery.getKey()); + assertEquals(OWNER, fiqlQuery.getOwner().getUsername()); + + FIQLQueryTO actual = binder.getFIQLQueryTO( + fiqlQueryDAO.findById(fiqlQuery.getKey()).orElseThrow()); + assertEquals(fiqlQuery.getKey(), actual.getKey()); + assertEquals("my query", actual.getName()); + assertEquals(AnyTypeKind.USER.name(), actual.getTarget()); + assertEquals("username==rossini", actual.getFiql()); + } finally { + authenticateAsAdmin(); + } + } + + @Test + void createBlankFiqlInvalid() { + authenticateAs(OWNER); + try { + FIQLQueryTO fiqlQueryTO = new FIQLQueryTO(); + fiqlQueryTO.setName("blank query"); + fiqlQueryTO.setTarget(AnyTypeKind.GROUP.name()); + fiqlQueryTO.setFiql(" "); + + assertThrows(SyncopeClientException.class, () -> binder.create(fiqlQueryTO)); + } finally { + authenticateAsAdmin(); + } + } + + @Test + void update() { + authenticateAs(OWNER); + try { + FIQLQueryTO fiqlQueryTO = new FIQLQueryTO(); + fiqlQueryTO.setName("to update"); + fiqlQueryTO.setTarget(AnyTypeKind.USER.name()); + fiqlQueryTO.setFiql("username==verdi"); + + FIQLQuery fiqlQuery = binder.create(fiqlQueryTO); + + FIQLQueryTO update = new FIQLQueryTO(); + update.setName("updated query"); + update.setTarget("PRINTER"); + update.setFiql("model==Canon"); + + binder.update(fiqlQuery, update); + fiqlQuery = fiqlQueryDAO.save(fiqlQuery); + entityManager.flush(); + + FIQLQueryTO actual = binder.getFIQLQueryTO( + fiqlQueryDAO.findById(fiqlQuery.getKey()).orElseThrow()); + assertEquals("updated query", actual.getName()); + assertEquals("PRINTER", actual.getTarget()); + assertEquals("model==Canon", actual.getFiql()); + } finally { + authenticateAsAdmin(); + } + } + + @Test + void invalidFiql() { + authenticateAs(OWNER); + try { + FIQLQueryTO fiqlQueryTO = new FIQLQueryTO(); + fiqlQueryTO.setName("invalid query"); + fiqlQueryTO.setTarget(AnyTypeKind.USER.name()); + fiqlQueryTO.setFiql("username==(("); + + assertThrows(SyncopeClientException.class, () -> binder.create(fiqlQueryTO)); + } finally { + authenticateAsAdmin(); + } + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderTest.java new file mode 100644 index 00000000000..02e52f60e6f --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderTest.java @@ -0,0 +1,247 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.apache.syncope.common.lib.Attr; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.request.AttrPatch; +import org.apache.syncope.common.lib.request.GroupCR; +import org.apache.syncope.common.lib.request.GroupUR; +import org.apache.syncope.common.lib.request.StringPatchItem; +import org.apache.syncope.common.lib.request.StringReplacePatchItem; +import org.apache.syncope.common.lib.to.GroupTO; +import org.apache.syncope.common.lib.to.TypeExtensionTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.PatchOperation; +import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.provisioning.api.PropagationByResource; +import org.apache.syncope.core.provisioning.api.data.GroupDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class GroupDataBinderTest extends AbstractDataBinderTest { + + private static final String CITIZEN_KEY = "29f96485-729e-4d31-88a1-6fc60e4677f3"; + + private static final String VERDI_KEY = "74cd8ece-715a-44a4-a736-e17b46c4e7e6"; + + @Autowired + private GroupDataBinder binder; + + @Autowired + private GroupDAO groupDAO; + + @Autowired + private EntityFactory entityFactory; + + @Test + void get() { + GroupTO group = binder.getGroupTO(CITIZEN_KEY); + assertEquals(CITIZEN_KEY, group.getKey()); + assertEquals("citizen", group.getName()); + assertEquals(SyncopeConstants.ROOT_REALM, group.getRealm()); + + GroupTO root = binder.getGroupTO("37d15e4c-cdc1-460b-a591-8505c8133806"); + assertEquals("root", root.getName()); + assertEquals("niceIcon", root.getPlainAttr("icon").orElseThrow().getValues().getFirst()); + } + + @Test + void createFull() { + TypeExtensionTO userTypeExtension = new TypeExtensionTO(); + userTypeExtension.setAnyType(AnyTypeKind.USER.name()); + userTypeExtension.getAuxClasses().addAll(List.of("other", "not-existing")); + + TypeExtensionTO printerTypeExtension = new TypeExtensionTO(); + printerTypeExtension.setAnyType("PRINTER"); + printerTypeExtension.getAuxClasses().addAll(List.of("csv", "not-existing")); + + GroupCR groupCR = new GroupCR.Builder("/even", "fullGroup"). + uManager(VERDI_KEY). + auxClasses("other", "csv", "not-existing"). + plainAttr(new Attr.Builder("icon").value("fullIcon").build()). + plainAttr(new Attr.Builder("show").value("true").build()). + plainAttr(new Attr.Builder("title").values(List.of("title1", "title2")).build()). + plainAttr(new Attr.Builder("originalName").value("originalFullGroup").build()). + plainAttr(new Attr.Builder("rderived_sx").value("sx").build()). + plainAttr(new Attr.Builder("rderived_dx").value("dx").build()). + plainAttr(new Attr.Builder("ctype").value("G").build()). + plainAttr(new Attr.Builder("aLong").value("42").build()). + plainAttr(new Attr.Builder("cool").value("true").build()). + plainAttr(new Attr.Builder("gender").value("F").build()). + plainAttr(new Attr.Builder("loginDate").values(List.of("2020-01-01", "2021-02-02")).build()). + plainAttr(new Attr.Builder("uselessReadonly").value("ignored").build()). + plainAttr(new Attr.Builder("not-existing").value("ignored").build()). + resource("ws-target-resource-1"). + resource("not-existing"). + typeExtension(userTypeExtension). + typeExtension(printerTypeExtension). + build(); + + Group group = entityFactory.newEntity(Group.class); + binder.create(group, groupCR); + group = groupDAO.save(group); + entityManager.flush(); + + Group actual = groupDAO.findById(group.getKey()).orElseThrow(); + assertEquals("fullGroup", actual.getName()); + assertEquals("/even", actual.getRealm().getFullPath()); + assertEquals(VERDI_KEY, actual.getuManager().getKey()); + assertNull(actual.getgManager()); + + assertEquals(2, actual.getAuxClasses().size()); + assertTrue(actual.getAuxClasses().stream().anyMatch(auxClass -> "other".equals(auxClass.getKey()))); + assertTrue(actual.getAuxClasses().stream().anyMatch(auxClass -> "csv".equals(auxClass.getKey()))); + + assertEquals("fullIcon", actual.getPlainAttr("icon").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals("true", actual.getPlainAttr("show").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals(2, actual.getPlainAttr("title").orElseThrow().getValuesAsStrings().size()); + assertEquals("originalFullGroup", + actual.getPlainAttr("originalName").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals("G", actual.getPlainAttr("ctype").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals(2, actual.getPlainAttr("loginDate").orElseThrow().getValuesAsStrings().size()); + assertTrue(actual.getPlainAttr("uselessReadonly").isEmpty()); + assertTrue(actual.getPlainAttr("not-existing").isEmpty()); + + assertEquals(1, actual.getResources().size()); + assertEquals("ws-target-resource-1", actual.getResources().getFirst().getKey()); + + GroupTO created = binder.getGroupTO(actual, true); + assertEquals(List.of("other"), created.getTypeExtension(AnyTypeKind.USER.name()).orElseThrow().getAuxClasses()); + assertEquals(List.of("csv"), created.getTypeExtension("PRINTER").orElseThrow().getAuxClasses()); + } + + @Test + void updateFull() { + Group group = groupDAO.findById("37d15e4c-cdc1-460b-a591-8505c8133806").orElseThrow(); + + TypeExtensionTO userTypeExtension = new TypeExtensionTO(); + userTypeExtension.setAnyType(AnyTypeKind.USER.name()); + userTypeExtension.getAuxClasses().addAll(List.of("csv", "not-existing")); + + GroupUR groupUR = new GroupUR.Builder(group.getKey()). + name(new StringReplacePatchItem.Builder().value("root2").build()). + realm(new StringReplacePatchItem.Builder().value("/even/two").build()). + uManager(new StringReplacePatchItem.Builder().value(VERDI_KEY).build()). + auxClass(new StringPatchItem.Builder().value("csv").operation(PatchOperation.ADD_REPLACE).build()). + auxClass(new StringPatchItem.Builder().value("other").operation(PatchOperation.ADD_REPLACE).build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("icon").value("updatedIcon").build()).build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("originalName").build()). + operation(PatchOperation.DELETE). + build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("aLong").value("42").build()).build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("cool").value("false").build()).build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("not-existing").value("ignored").build()).build()). + resource(new StringPatchItem.Builder(). + value("ws-target-resource-1"). + operation(PatchOperation.ADD_REPLACE). + build()). + resource(new StringPatchItem.Builder(). + value("not-existing"). + operation(PatchOperation.ADD_REPLACE). + build()). + typeExtension(userTypeExtension). + build(); + + PropagationByResource propByRes = binder.update(group, groupUR); + assertNotNull(propByRes); + entityManager.flush(); + + Group actual = groupDAO.findById(group.getKey()).orElseThrow(); + assertEquals("root2", actual.getName()); + assertEquals("/even/two", actual.getRealm().getFullPath()); + assertEquals(VERDI_KEY, actual.getuManager().getKey()); + assertNull(actual.getgManager()); + assertEquals("updatedIcon", actual.getPlainAttr("icon").orElseThrow().getValuesAsStrings().getFirst()); + assertTrue(actual.getPlainAttr("originalName").isEmpty()); + assertEquals("42", actual.getPlainAttr("aLong").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals("false", actual.getPlainAttr("cool").orElseThrow().getValuesAsStrings().getFirst()); + assertTrue(actual.getResources().stream(). + anyMatch(resource -> "ws-target-resource-1".equals(resource.getKey()))); + + GroupTO updated = binder.getGroupTO(actual, true); + assertEquals("root2", updated.getName()); + assertEquals("/even/two", updated.getRealm()); + assertEquals(List.of("csv"), updated.getTypeExtension(AnyTypeKind.USER.name()).orElseThrow().getAuxClasses()); + } + + @Test + void updateAuxClassRemoval() { + Group group = groupDAO.findById("37d15e4c-cdc1-460b-a591-8505c8133806").orElseThrow(); + + GroupUR addAuxClass = new GroupUR.Builder(group.getKey()). + auxClass(new StringPatchItem.Builder(). + value("other"). + operation(PatchOperation.ADD_REPLACE). + build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("ctype").value("G").build()).build()). + build(); + binder.update(group, addAuxClass); + entityManager.flush(); + + Group withAuxClass = groupDAO.findById(group.getKey()).orElseThrow(); + assertTrue(withAuxClass.getAuxClasses().stream(). + anyMatch(auxClass -> "other".equals(auxClass.getKey()))); + assertEquals("G", withAuxClass.getPlainAttr("ctype").orElseThrow().getValuesAsStrings().getFirst()); + + GroupUR removeAuxClass = new GroupUR.Builder(group.getKey()). + auxClass(new StringPatchItem.Builder(). + value("other"). + operation(PatchOperation.DELETE). + build()). + build(); + binder.update(withAuxClass, removeAuxClass); + entityManager.flush(); + + Group actual = groupDAO.findById(group.getKey()).orElseThrow(); + assertTrue(actual.getAuxClasses().isEmpty()); + assertTrue(actual.getPlainAttr("ctype").isEmpty()); + } + + @Test + void invalidCreate() { + assertThrows(SyncopeClientException.class, () -> binder.create( + entityFactory.newEntity(Group.class), new GroupCR.Builder("/even/three", "testGroup").build())); + } + + @Test + void invalidUpdate() { + Group group = groupDAO.findById(CITIZEN_KEY).orElseThrow(); + + GroupUR groupUR = new GroupUR.Builder(group.getKey()). + realm(new StringReplacePatchItem.Builder().value("/even/three").build()). + build(); + + PropagationByResource propByRes = binder.update(group, groupUR); + assertNotNull(propByRes); + + Group actual = groupDAO.findById(CITIZEN_KEY).orElseThrow(); + assertEquals(SyncopeConstants.ROOT_REALM, actual.getRealm().getFullPath()); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderTest.java new file mode 100644 index 00000000000..a2855bd19b4 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderTest.java @@ -0,0 +1,115 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.policy.DefaultAccountRuleConf; +import org.apache.syncope.common.lib.to.ImplementationTO; +import org.apache.syncope.common.lib.types.IdRepoImplementationType; +import org.apache.syncope.common.lib.types.ImplementationEngine; +import org.apache.syncope.common.lib.types.ImplementationTypesHolder; +import org.apache.syncope.core.persistence.api.dao.ImplementationDAO; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.provisioning.api.data.ImplementationDataBinder; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class ImplementationDataBinderTest extends AbstractDataBinderTest { + + @Autowired + private ImplementationDataBinder binder; + + @Autowired + private ImplementationDAO implementationDAO; + + @BeforeAll + public static void populateImplementationTypes() { + ImplementationTypesHolder.getInstance().putAll(IdRepoImplementationType.values()); + } + + @Test + void get() { + ImplementationTO implementation = binder.getImplementationTO( + implementationDAO.findById("SampleReportJobDelegate").orElseThrow()); + assertEquals("SampleReportJobDelegate", implementation.getKey()); + assertEquals(ImplementationEngine.JAVA, implementation.getEngine()); + assertEquals(IdRepoImplementationType.REPORT_DELEGATE, implementation.getType()); + assertEquals(implementation.getBody(), implementationDAO.findById("SampleReportJobDelegate"). + orElseThrow().getBody()); + } + + @Test + void createUpdate() { + DefaultAccountRuleConf conf = new DefaultAccountRuleConf(); + conf.setMaxLength(32); + conf.setMinLength(8); + + ImplementationTO implementationTO = new ImplementationTO(); + implementationTO.setKey("testAccountRule"); + implementationTO.setEngine(ImplementationEngine.JAVA); + implementationTO.setType(IdRepoImplementationType.ACCOUNT_RULE); + implementationTO.setBody(POJOHelper.serialize(conf)); + + Implementation implementation = implementationDAO.save(binder.create(implementationTO)); + + ImplementationTO created = binder.getImplementationTO(implementation); + assertEquals("testAccountRule", created.getKey()); + assertEquals(ImplementationEngine.JAVA, created.getEngine()); + assertEquals(IdRepoImplementationType.ACCOUNT_RULE, created.getType()); + assertEquals(implementationTO.getBody(), created.getBody()); + + conf.setMaxLength(64); + created.setBody(POJOHelper.serialize(conf)); + binder.update(implementation, created); + assertEquals(created.getBody(), binder.getImplementationTO(implementation).getBody()); + } + + @Test + void invalid() { + ImplementationTO blankBody = new ImplementationTO(); + blankBody.setKey("testImplementation"); + blankBody.setEngine(ImplementationEngine.JAVA); + blankBody.setType(IdRepoImplementationType.ACCOUNT_RULE); + assertThrows(SyncopeClientException.class, () -> binder.create(blankBody)); + + ImplementationTO invalidType = new ImplementationTO(); + invalidType.setKey("testImplementation"); + invalidType.setEngine(ImplementationEngine.JAVA); + invalidType.setType("invalidType"); + invalidType.setBody("{}"); + assertThrows(SyncopeClientException.class, () -> binder.create(invalidType)); + + ImplementationTO invalidBody = new ImplementationTO(); + invalidBody.setKey("testImplementation"); + invalidBody.setEngine(ImplementationEngine.JAVA); + invalidBody.setType(IdRepoImplementationType.ACCOUNT_RULE); + invalidBody.setBody("{}"); + assertThrows(SyncopeClientException.class, () -> binder.create(invalidBody)); + + Implementation existing = implementationDAO.findById("SampleReportJobDelegate").orElseThrow(); + ImplementationTO changedType = binder.getImplementationTO(existing); + changedType.setType(IdRepoImplementationType.ACCOUNT_RULE); + assertThrows(SyncopeClientException.class, () -> binder.update(existing, changedType)); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImplTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImplTest.java new file mode 100644 index 00000000000..b05bac1afb2 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImplTest.java @@ -0,0 +1,190 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.nio.charset.StandardCharsets; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.List; +import org.apache.syncope.common.lib.Attr; +import org.apache.syncope.common.lib.to.Item; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; +import org.apache.syncope.core.provisioning.api.MappingManager.IntValues; +import org.apache.syncope.core.provisioning.api.data.JEXLItemTransformer; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; + +class JEXLItemTransformerImplTest extends AbstractDataBinderTest { + + @Autowired + private ApplicationContext applicationContext; + + private JEXLItemTransformer transformer; + + @BeforeEach + void setUp() { + transformer = applicationContext.getAutowireCapableBeanFactory(). + createBean(JEXLItemTransformerImpl.class); + } + + protected PlainAttrValue stringValue(final String value) { + PlainAttrValue plainAttrValue = new PlainAttrValue(); + plainAttrValue.setStringValue(value); + return plainAttrValue; + } + + @Test + void beforePropagationString() { + transformer.setPropagationJEXL("value + ' - transformed'"); + + PlainAttrValue value = stringValue("hello"); + IntValues actual = transformer.beforePropagation( + new Item(), null, AttrSchemaType.String, new ArrayList<>(List.of(value))); + + assertEquals(AttrSchemaType.String, actual.attrSchemaType()); + assertEquals("hello - transformed", actual.values().getFirst().getStringValue()); + assertNull(actual.values().getFirst().getLongValue()); + } + + @Test + void beforePropagationBoolean() { + transformer.setPropagationJEXL("!value"); + + PlainAttrValue value = new PlainAttrValue(); + value.setBooleanValue(true); + IntValues actual = transformer.beforePropagation( + new Item(), null, AttrSchemaType.Boolean, new ArrayList<>(List.of(value))); + + assertEquals(AttrSchemaType.Boolean, actual.attrSchemaType()); + assertFalse(actual.values().getFirst().getBooleanValue()); + assertNull(actual.values().getFirst().getStringValue()); + } + + @Test + void beforePropagationLongAndDouble() { + transformer.setPropagationJEXL("value + 1"); + + PlainAttrValue longValue = new PlainAttrValue(); + longValue.setLongValue(10L); + IntValues longActual = transformer.beforePropagation( + new Item(), null, AttrSchemaType.Long, new ArrayList<>(List.of(longValue))); + assertEquals(AttrSchemaType.Long, longActual.attrSchemaType()); + assertEquals(11L, longActual.values().getFirst().getLongValue()); + + transformer.setPropagationJEXL("value * 2"); + + PlainAttrValue doubleValue = new PlainAttrValue(); + doubleValue.setDoubleValue(1.5D); + IntValues doubleActual = transformer.beforePropagation( + new Item(), null, AttrSchemaType.Double, new ArrayList<>(List.of(doubleValue))); + assertEquals(AttrSchemaType.Double, doubleActual.attrSchemaType()); + assertEquals(3.0D, doubleActual.values().getFirst().getDoubleValue()); + } + + @Test + void beforePropagationDate() { + transformer.setPropagationJEXL("value.plusDays(1)"); + + OffsetDateTime now = OffsetDateTime.now(); + PlainAttrValue value = new PlainAttrValue(); + value.setDateValue(now); + IntValues actual = transformer.beforePropagation( + new Item(), null, AttrSchemaType.Date, new ArrayList<>(List.of(value))); + + assertEquals(AttrSchemaType.Date, actual.attrSchemaType()); + assertEquals(now.plusDays(1), actual.values().getFirst().getDateValue()); + } + + @Test + void beforePropagationBinary() { + transformer.setPropagationJEXL("value"); + + byte[] binary = "binary".getBytes(StandardCharsets.UTF_8); + PlainAttrValue value = new PlainAttrValue(); + value.setBinaryValue(binary); + IntValues actual = transformer.beforePropagation( + new Item(), null, AttrSchemaType.Binary, new ArrayList<>(List.of(value))); + + assertEquals(AttrSchemaType.Binary, actual.attrSchemaType()); + assertArrayEquals(binary, actual.values().getFirst().getBinaryValue()); + } + + @Test + void beforePropagationEmptyValues() { + transformer.setPropagationJEXL("value + '!'"); + + List values = new ArrayList<>(); + IntValues actual = transformer.beforePropagation( + new Item(), null, AttrSchemaType.String, values); + + assertEquals(AttrSchemaType.String, actual.attrSchemaType()); + assertEquals(1, actual.values().size()); + assertEquals("!", actual.values().getFirst().getStringValue()); + assertSame(values, actual.values()); + } + + @Test + void beforePropagationBlankJEXL() { + transformer.setPropagationJEXL(" "); + + PlainAttrValue value = stringValue("untouched"); + List values = new ArrayList<>(List.of(value)); + IntValues actual = transformer.beforePropagation( + new Item(), null, AttrSchemaType.String, values); + + assertEquals(AttrSchemaType.String, actual.attrSchemaType()); + assertSame(values, actual.values()); + assertEquals("untouched", actual.values().getFirst().getStringValue()); + } + + @Test + void beforePull() { + transformer.setPullJEXL("value + ' pulled'"); + + UserTO userTO = new UserTO(); + userTO.setUsername("rossini"); + userTO.getPlainAttrs().add(new Attr.Builder("firstname").value("Gioacchino").build()); + + List actual = transformer.beforePull( + new Item(), userTO, new ArrayList<>(List.of("a", "b"))); + + assertEquals(List.of("a pulled", "b pulled"), actual); + } + + @Test + void beforePullBlankJEXL() { + transformer.setPullJEXL(null); + + List values = new ArrayList<>(List.of("a", "b")); + assertSame(values, transformer.beforePull(new Item(), new UserTO(), values)); + assertNull(transformer.beforePull(new Item(), new UserTO(), null)); + assertTrue(transformer.beforePull(new Item(), new UserTO(), List.of()).isEmpty()); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderTest.java new file mode 100644 index 00000000000..689a804d344 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderTest.java @@ -0,0 +1,121 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.Map; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.NotificationTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.TraceLevel; +import org.apache.syncope.core.persistence.api.dao.NotificationDAO; +import org.apache.syncope.core.persistence.api.entity.Notification; +import org.apache.syncope.core.provisioning.api.data.NotificationDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class NotificationDataBinderTest extends AbstractDataBinderTest { + + @Autowired + private NotificationDataBinder binder; + + @Autowired + private NotificationDAO notificationDAO; + + @Test + void get() { + NotificationTO notification = binder.getNotificationTO( + notificationDAO.findById("9e2b911c-25de-4c77-bcea-b86ed9451050").orElseThrow()); + assertEquals("9e2b911c-25de-4c77-bcea-b86ed9451050", notification.getKey()); + assertEquals("test", notification.getTemplate()); + assertEquals("test@syncope.apache.org", notification.getSender()); + assertEquals("Test subject", notification.getSubject()); + assertEquals("email", notification.getRecipientAttrName()); + assertEquals("$groups==7", notification.getRecipientsFIQL()); + assertEquals(TraceLevel.FAILURES, notification.getTraceLevel()); + assertTrue(notification.isActive()); + assertEquals("fullname==*o*;fullname==*i*", notification.getAbouts().get(AnyTypeKind.USER.name())); + } + + @Test + void createUpdate() { + NotificationTO notificationTO = new NotificationTO(); + notificationTO.setTemplate("test"); + notificationTO.getEvents().add("[CUSTOM]:[]:[]:[test]:[SUCCESS]"); + notificationTO.setRecipientAttrName("email"); + notificationTO.setSender("sender@syncope.apache.org"); + notificationTO.setSubject("Test subject"); + notificationTO.setRecipientsFIQL("$groups==7"); + notificationTO.getStaticRecipients().add("static@syncope.apache.org"); + notificationTO.setTraceLevel(TraceLevel.ALL); + notificationTO.setActive(true); + notificationTO.getAbouts().put(AnyTypeKind.USER.name(), "fullname==*o*"); + notificationTO.getAbouts().put("invalid", "fullname==*o*"); + + Notification notification = notificationDAO.save(binder.create(notificationTO)); + + NotificationTO created = binder.getNotificationTO(notification); + assertEquals("test", created.getTemplate()); + assertEquals("sender@syncope.apache.org", created.getSender()); + assertEquals("email", created.getRecipientAttrName()); + assertEquals(List.of("static@syncope.apache.org"), created.getStaticRecipients()); + assertEquals(TraceLevel.ALL, created.getTraceLevel()); + assertEquals(Map.of(AnyTypeKind.USER.name(), "fullname==*o*"), created.getAbouts()); + + created.setSubject("Updated subject"); + created.getEvents().clear(); + created.getEvents().add("[CUSTOM]:[]:[]:[test2]:[SUCCESS]"); + binder.update(notification, created); + + NotificationTO updated = binder.getNotificationTO(notification); + assertEquals("Updated subject", updated.getSubject()); + assertEquals(List.of("[CUSTOM]:[]:[]:[test2]:[SUCCESS]"), updated.getEvents()); + } + + @Test + void invalidTemplateAndEvents() { + NotificationTO notificationTO = new NotificationTO(); + notificationTO.setTemplate("not-existing"); + notificationTO.setRecipientAttrName("email"); + notificationTO.setSender("sender@syncope.apache.org"); + notificationTO.setSubject("Test subject"); + assertThrows(SyncopeClientException.class, () -> binder.create(notificationTO)); + + notificationTO.setTemplate("test"); + assertThrows(SyncopeClientException.class, () -> binder.create(notificationTO)); + } + + @Test + void invalidStaticRecipientAndAttribute() { + NotificationTO notificationTO = new NotificationTO(); + notificationTO.setTemplate("test"); + notificationTO.getEvents().add("[CUSTOM]:[]:[]:[test]:[SUCCESS]"); + notificationTO.setRecipientAttrName("email"); + notificationTO.getStaticRecipients().add("not-an-email"); + assertThrows(SyncopeClientException.class, () -> binder.create(notificationTO)); + + notificationTO.getStaticRecipients().clear(); + notificationTO.setRecipientAttrName("relationships[unknown].cn"); + assertThrows(SyncopeClientException.class, () -> binder.create(notificationTO)); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/OIDCOpEntityDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/OIDCOpEntityDataBinderTest.java new file mode 100644 index 00000000000..642452eab59 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/OIDCOpEntityDataBinderTest.java @@ -0,0 +1,106 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.List; +import java.util.Set; +import org.apache.syncope.common.lib.to.OIDCOpEntityTO; +import org.apache.syncope.core.persistence.api.dao.OIDCOpEntityDAO; +import org.apache.syncope.core.persistence.api.dao.WAConfigDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.am.OIDCOpEntity; +import org.apache.syncope.core.provisioning.api.data.OIDCOpEntityDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class OIDCOpEntityDataBinderTest extends AbstractDataBinderTest { + + @Autowired + private OIDCOpEntityDataBinder binder; + + @Autowired + private OIDCOpEntityDAO oidcOpEntityDAO; + + @Autowired + private WAConfigDAO waConfigDAO; + + @Autowired + private EntityFactory entityFactory; + + @Test + void providedJWKSAndScopes() { + OIDCOpEntity entity = entityFactory.newEntity(OIDCOpEntity.class); + + OIDCOpEntityTO entityTO = new OIDCOpEntityTO(); + entityTO.setJWKS(Base64.getEncoder().encodeToString("custom-jwks".getBytes(StandardCharsets.UTF_8))); + entityTO.getCustomScopes().put("scope", Set.of("first", "second")); + + binder.update(entity, entityTO); + oidcOpEntityDAO.save(entity); + entityManager.flush(); + + OIDCOpEntityTO actual = binder.getOIDCOpEntityTO(oidcOpEntityDAO.get().orElseThrow()); + assertNotNull(actual.getKey()); + assertEquals(entityTO.getJWKS(), actual.getJWKS()); + assertEquals(Set.of("first", "second"), actual.getCustomScopes().get("scope")); + } + + @Test + void generatedJWKS() { + OIDCOpEntity entity = entityFactory.newEntity(OIDCOpEntity.class); + + OIDCOpEntityTO entityTO = new OIDCOpEntityTO(); + entityTO.getCustomScopes().put("scope", Set.of("value")); + + binder.update(entity, entityTO); + oidcOpEntityDAO.save(entity); + entityManager.flush(); + + OIDCOpEntity actual = oidcOpEntityDAO.get().orElseThrow(); + assertNotNull(actual.getJWKS()); + assertTrue(actual.getJWKS().length > 0); + assertEquals(Set.of("value"), actual.getCustomScopes().get("scope")); + + OIDCOpEntityTO actualTO = binder.getOIDCOpEntityTO(actual); + assertFalse(actualTO.getJWKS().isEmpty()); + + assertEquals(List.of("syncope"), + waConfigDAO.findById("cas.authn.oidc.jwks.core.jwks-key-id").orElseThrow().getValues()); + assertEquals(List.of("RSA"), + waConfigDAO.findById("cas.authn.oidc.jwks.core.jwks-type").orElseThrow().getValues()); + assertEquals(List.of("2048"), + waConfigDAO.findById("cas.authn.oidc.jwks.core.jwks-key-size").orElseThrow().getValues()); + } + + @Test + void generateJWKSVariants() { + assertFalse(binder.generateJWKS("ec384", "EC", 384).length == 0); + assertFalse(binder.generateJWKS("ec512", "ec", 512).length == 0); + assertFalse(binder.generateJWKS("ecDefault", "EC", 256).length == 0); + assertFalse(binder.generateJWKS("rsa", "RSA", 1024).length == 0); + assertFalse(binder.generateJWKS("rsaDefault", "unknown", 512).length == 0); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/PasswordManagementDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/PasswordManagementDataBinderTest.java new file mode 100644 index 00000000000..0c4c241ec0c --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/PasswordManagementDataBinderTest.java @@ -0,0 +1,73 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.syncope.common.lib.to.PasswordManagementTO; +import org.apache.syncope.core.persistence.api.dao.PasswordManagementDAO; +import org.apache.syncope.core.persistence.api.entity.am.PasswordManagement; +import org.apache.syncope.core.provisioning.api.data.PasswordManagementDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class PasswordManagementDataBinderTest extends AbstractDataBinderTest { + + @Autowired + private PasswordManagementDataBinder binder; + + @Autowired + private PasswordManagementDAO passwordManagementDAO; + + @Test + void get() { + PasswordManagementTO passwordManagement = binder.getPasswordManagementTO( + passwordManagementDAO.findById("DefaultSyncopePasswordManagement").orElseThrow()); + assertEquals("DefaultSyncopePasswordManagement", passwordManagement.getKey()); + assertEquals("Syncope password management module", passwordManagement.getDescription()); + assertTrue(passwordManagement.isEnabled()); + } + + @Test + void createUpdate() { + PasswordManagementTO passwordManagementTO = new PasswordManagementTO(); + passwordManagementTO.setKey("testPasswordManagement"); + passwordManagementTO.setDescription("a test password management"); + passwordManagementTO.setEnabled(false); + passwordManagementTO.setConf(binder.getPasswordManagementTO( + passwordManagementDAO.findById("DefaultSyncopePasswordManagement").orElseThrow()).getConf()); + + PasswordManagement passwordManagement = passwordManagementDAO.save(binder.create(passwordManagementTO)); + + PasswordManagementTO created = binder.getPasswordManagementTO(passwordManagement); + assertEquals("testPasswordManagement", created.getKey()); + assertEquals("a test password management", created.getDescription()); + assertFalse(created.isEnabled()); + + created.setDescription("an updated test password management"); + created.setEnabled(true); + binder.update(passwordManagement, created); + + PasswordManagementTO updated = binder.getPasswordManagementTO(passwordManagement); + assertEquals("an updated test password management", updated.getDescription()); + assertTrue(updated.isEnabled()); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderTest.java new file mode 100644 index 00000000000..e5fb9d023a1 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderTest.java @@ -0,0 +1,411 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.Map; +import org.apache.syncope.common.lib.policy.AccessPolicyTO; +import org.apache.syncope.common.lib.policy.AccountPolicyTO; +import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO; +import org.apache.syncope.common.lib.policy.AuthPolicyTO; +import org.apache.syncope.common.lib.policy.DefaultAccessPolicyConf; +import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf; +import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf; +import org.apache.syncope.common.lib.policy.DefaultTicketExpirationPolicyConf; +import org.apache.syncope.common.lib.policy.InboundPolicyTO; +import org.apache.syncope.common.lib.policy.PasswordPolicyTO; +import org.apache.syncope.common.lib.policy.PropagationPolicyTO; +import org.apache.syncope.common.lib.policy.PushPolicyTO; +import org.apache.syncope.common.lib.policy.TicketExpirationPolicyTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.BackOffStrategy; +import org.apache.syncope.common.lib.types.ConflictResolutionAction; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.PolicyDAO; +import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy; +import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.Policy; +import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; +import org.apache.syncope.core.provisioning.api.data.PolicyDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class PolicyDataBinderTest extends AbstractDataBinderTest { + + @Autowired + private PolicyDataBinder binder; + + @Autowired + private PolicyDAO policyDAO; + + @Test + void getPasswordPolicyTO() { + PasswordPolicy policy = policyDAO.findById( + "ce93fcda-dc3a-4369-a7b0-a6108c261c85", PasswordPolicy.class).orElseThrow(); + + PasswordPolicyTO passwordPolicyTO = binder.getPolicyTO(policy); + assertEquals(policy.getKey(), passwordPolicyTO.getKey()); + assertEquals("a password policy", passwordPolicyTO.getName()); + assertTrue(passwordPolicyTO.isAllowNullPassword()); + assertEquals(1, passwordPolicyTO.getHistoryLength()); + assertEquals(List.of("DefaultPasswordRuleConf1"), passwordPolicyTO.getRules()); + assertTrue(passwordPolicyTO.getUsedByRealms().contains("/even/two")); + } + + @Test + void getAccountPolicyTO() { + AccountPolicy policy = policyDAO.findById( + "20ab5a8c-4b0c-432c-b957-f7fb9784d9f7", AccountPolicy.class).orElseThrow(); + + AccountPolicyTO accountPolicyTO = binder.getPolicyTO(policy); + assertEquals("an account policy", accountPolicyTO.getName()); + assertEquals(0, accountPolicyTO.getMaxAuthenticationAttempts()); + assertEquals(List.of("DefaultAccountRuleConf1"), accountPolicyTO.getRules()); + assertTrue(accountPolicyTO.getUsedByRealms().contains("/even/two")); + } + + @Test + void getAuthAndAccessPolicyTO() { + AuthPolicy authPolicy = policyDAO.findById( + "b912a0d4-a890-416f-9ab8-84ab077eb028", AuthPolicy.class).orElseThrow(); + AuthPolicyTO authPolicyTO = binder.getPolicyTO(authPolicy); + assertEquals("DefaultAuthPolicy", authPolicyTO.getName()); + assertNotNull(authPolicyTO.getConf()); + + AccessPolicyTO accessPolicyTO = binder.getPolicyTO(policyDAO.findById( + "419935c7-deb3-40b3-8a9a-683037e523a2").orElseThrow()); + assertEquals("DefaultAccessPolicy", accessPolicyTO.getName()); + assertNotNull(accessPolicyTO.getConf()); + } + + @Test + void getAttrReleaseAndInboundPolicyTO() { + AttrReleasePolicy attrReleasePolicy = policyDAO.findById( + "319935c7-deb3-40b3-8a9a-683037e523a2", AttrReleasePolicy.class).orElseThrow(); + AttrReleasePolicyTO attrReleasePolicyTO = binder.getPolicyTO(attrReleasePolicy); + assertEquals("AllowedAttrReleasePolicy", attrReleasePolicyTO.getName()); + assertEquals(0, attrReleasePolicyTO.getOrder()); + assertNotNull(attrReleasePolicyTO.getConf()); + + InboundPolicy inboundPolicy = policyDAO.findById( + "880f8553-069b-4aed-9930-2cd53873f544", InboundPolicy.class).orElseThrow(); + InboundPolicyTO inboundPolicyTO = binder.getPolicyTO(inboundPolicy); + assertEquals("another pull policy", inboundPolicyTO.getName()); + assertEquals(ConflictResolutionAction.ALL, inboundPolicyTO.getConflictResolutionAction()); + assertEquals("TestInboundCorrelationRule", inboundPolicyTO.getCorrelationRules().get(AnyTypeKind.USER.name())); + } + + @Test + void createUpdatePasswordPolicy() { + PasswordPolicyTO passwordPolicyTO = new PasswordPolicyTO(); + passwordPolicyTO.setName("test password policy"); + passwordPolicyTO.setAllowNullPassword(true); + passwordPolicyTO.setHistoryLength(3); + passwordPolicyTO.getRules().add("DefaultPasswordRuleConf1"); + passwordPolicyTO.getRules().add("not-existing"); + + Policy policy = policyDAO.save(binder.create(passwordPolicyTO)); + + PasswordPolicyTO created = binder.getPolicyTO(policy); + assertNotNull(created.getKey()); + assertEquals("test password policy", created.getName()); + assertTrue(created.isAllowNullPassword()); + assertEquals(3, created.getHistoryLength()); + assertEquals(List.of("DefaultPasswordRuleConf1"), created.getRules()); + + created.setHistoryLength(5); + created.getRules().clear(); + created.getRules().add("DefaultPasswordRuleConf2"); + binder.update(policy, created); + + PasswordPolicyTO updated = binder.getPolicyTO(policy); + assertEquals(5, updated.getHistoryLength()); + assertEquals(List.of("DefaultPasswordRuleConf2"), updated.getRules()); + } + + @Test + void invalidInboundCorrelationRule() { + InboundPolicyTO inboundPolicyTO = new InboundPolicyTO(); + inboundPolicyTO.setName("test inbound policy"); + inboundPolicyTO.getCorrelationRules().put(AnyTypeKind.USER.name(), "not-existing"); + + assertThrows(NotFoundException.class, () -> binder.create(inboundPolicyTO)); + } + + @Test + void getPropagationAndPushPolicyTO() { + PropagationPolicy propagationPolicy = policyDAO.findById( + "89d322db-9878-420c-b49c-67be13df9a12", PropagationPolicy.class).orElseThrow(); + PropagationPolicyTO propagationPolicyTO = binder.getPolicyTO(propagationPolicy); + assertEquals("sample propagation policy", propagationPolicyTO.getName()); + assertTrue(propagationPolicyTO.isFetchAroundProvisioning()); + assertEquals(BackOffStrategy.FIXED, propagationPolicyTO.getBackOffStrategy()); + assertEquals("10000", propagationPolicyTO.getBackOffParams()); + assertEquals(5, propagationPolicyTO.getMaxAttempts()); + assertTrue(propagationPolicyTO.getUsedByResources().contains("resource-db-scripted")); + + PushPolicy pushPolicy = policyDAO.findById( + "fb6530e5-892d-4f47-a46b-180c5b6c5c83", PushPolicy.class).orElseThrow(); + PushPolicyTO pushPolicyTO = binder.getPolicyTO(pushPolicy); + assertEquals("a push policy", pushPolicyTO.getName()); + assertEquals(ConflictResolutionAction.IGNORE, pushPolicyTO.getConflictResolutionAction()); + assertEquals(Map.of(AnyTypeKind.USER.name(), "TestPushCorrelationRule"), pushPolicyTO.getCorrelationRules()); + } + + @Test + void createUpdateAccountPolicy() { + AccountPolicyTO accountPolicyTO = new AccountPolicyTO(); + accountPolicyTO.setName("test account policy"); + accountPolicyTO.setMaxAuthenticationAttempts(4); + accountPolicyTO.setPropagateSuspension(true); + accountPolicyTO.getRules().add("DefaultAccountRuleConf1"); + accountPolicyTO.getRules().add("not-existing"); + + Policy policy = policyDAO.save(binder.create(accountPolicyTO)); + + AccountPolicyTO created = binder.getPolicyTO(policy); + assertNotNull(created.getKey()); + assertEquals("test account policy", created.getName()); + assertEquals(4, created.getMaxAuthenticationAttempts()); + assertTrue(created.isPropagateSuspension()); + assertEquals(List.of("DefaultAccountRuleConf1"), created.getRules()); + + created.setMaxAuthenticationAttempts(7); + created.setPropagateSuspension(false); + created.getRules().clear(); + created.getRules().add("DefaultAccountRuleConf2"); + binder.update(policy, created); + + AccountPolicyTO updated = binder.getPolicyTO(policy); + assertEquals(7, updated.getMaxAuthenticationAttempts()); + assertFalse(updated.isPropagateSuspension()); + assertEquals(List.of("DefaultAccountRuleConf2"), updated.getRules()); + } + + @Test + void createUpdatePropagationPolicy() { + PropagationPolicyTO propagationPolicyTO = new PropagationPolicyTO(); + propagationPolicyTO.setName("test propagation policy"); + propagationPolicyTO.setFetchAroundProvisioning(false); + propagationPolicyTO.setUpdateDelta(true); + propagationPolicyTO.setBackOffStrategy(BackOffStrategy.EXPONENTIAL); + propagationPolicyTO.setBackOffParams("200;40000;3"); + propagationPolicyTO.setMaxAttempts(6); + + Policy policy = policyDAO.save(binder.create(propagationPolicyTO)); + + PropagationPolicyTO created = binder.getPolicyTO(policy); + assertNotNull(created.getKey()); + assertEquals("test propagation policy", created.getName()); + assertFalse(created.isFetchAroundProvisioning()); + assertTrue(created.isUpdateDelta()); + assertEquals(BackOffStrategy.EXPONENTIAL, created.getBackOffStrategy()); + assertEquals("200;40000;3", created.getBackOffParams()); + assertEquals(6, created.getMaxAttempts()); + + created.setFetchAroundProvisioning(true); + created.setUpdateDelta(false); + created.setBackOffStrategy(BackOffStrategy.RANDOM); + created.setMaxAttempts(2); + binder.update(policy, created); + + PropagationPolicyTO updated = binder.getPolicyTO(policy); + assertTrue(updated.isFetchAroundProvisioning()); + assertFalse(updated.isUpdateDelta()); + assertEquals(BackOffStrategy.RANDOM, updated.getBackOffStrategy()); + assertEquals(2, updated.getMaxAttempts()); + } + + @Test + void createUpdatePushPolicy() { + PushPolicyTO pushPolicyTO = new PushPolicyTO(); + pushPolicyTO.setName("test push policy"); + pushPolicyTO.setConflictResolutionAction(ConflictResolutionAction.ALL); + pushPolicyTO.getCorrelationRules().put(AnyTypeKind.USER.name(), "TestPushCorrelationRule"); + pushPolicyTO.getCorrelationRules().put("not-existing", "TestPushCorrelationRule"); + + Policy policy = policyDAO.save(binder.create(pushPolicyTO)); + + PushPolicyTO created = binder.getPolicyTO(policy); + assertNotNull(created.getKey()); + assertEquals("test push policy", created.getName()); + assertEquals(ConflictResolutionAction.ALL, created.getConflictResolutionAction()); + assertEquals(Map.of(AnyTypeKind.USER.name(), "TestPushCorrelationRule"), created.getCorrelationRules()); + + created.setConflictResolutionAction(ConflictResolutionAction.IGNORE); + created.getCorrelationRules().clear(); + binder.update(policy, created); + + PushPolicyTO updated = binder.getPolicyTO(policy); + assertEquals(ConflictResolutionAction.IGNORE, updated.getConflictResolutionAction()); + assertTrue(updated.getCorrelationRules().isEmpty()); + + PushPolicyTO invalid = new PushPolicyTO(); + invalid.setName("invalid push policy"); + invalid.getCorrelationRules().put(AnyTypeKind.USER.name(), "not-existing"); + assertThrows(NotFoundException.class, () -> binder.create(invalid)); + } + + @Test + void createUpdateAuthPolicy() { + DefaultAuthPolicyConf conf = new DefaultAuthPolicyConf(); + conf.setTryAll(true); + conf.setBypassEnabled(true); + conf.getAuthModules().add("DefaultLDAPAuthModule"); + conf.getAuthModules().add("not-existing"); + + AuthPolicyTO authPolicyTO = new AuthPolicyTO(); + authPolicyTO.setName("test auth policy"); + authPolicyTO.setConf(conf); + + Policy policy = policyDAO.save(binder.create(authPolicyTO)); + + AuthPolicyTO created = binder.getPolicyTO(policy); + assertNotNull(created.getKey()); + assertEquals("test auth policy", created.getName()); + DefaultAuthPolicyConf createdConf = (DefaultAuthPolicyConf) created.getConf(); + assertTrue(createdConf.isTryAll()); + assertTrue(createdConf.isBypassEnabled()); + assertEquals(List.of("DefaultLDAPAuthModule", "not-existing"), createdConf.getAuthModules()); + + createdConf.setTryAll(false); + createdConf.getAuthModules().clear(); + binder.update(policy, created); + + DefaultAuthPolicyConf updatedConf = (DefaultAuthPolicyConf) binder.getPolicyTO(policy).getConf(); + assertFalse(updatedConf.isTryAll()); + assertTrue(updatedConf.getAuthModules().isEmpty()); + } + + @Test + void createUpdateAccessPolicy() { + DefaultAccessPolicyConf conf = new DefaultAccessPolicyConf(); + conf.setOrder(2); + conf.setEnabled(true); + conf.setSsoEnabled(false); + conf.setCaseInsensitive(true); + conf.setRequireAllAttributes(false); + + AccessPolicyTO accessPolicyTO = new AccessPolicyTO(); + accessPolicyTO.setName("test access policy"); + accessPolicyTO.setConf(conf); + + Policy policy = policyDAO.save(binder.create(accessPolicyTO)); + + AccessPolicyTO created = binder.getPolicyTO(policy); + assertNotNull(created.getKey()); + assertEquals("test access policy", created.getName()); + DefaultAccessPolicyConf createdConf = (DefaultAccessPolicyConf) created.getConf(); + assertEquals(2, createdConf.getOrder()); + assertTrue(createdConf.isEnabled()); + assertFalse(createdConf.isSsoEnabled()); + assertTrue(createdConf.isCaseInsensitive()); + assertFalse(createdConf.isRequireAllAttributes()); + + createdConf.setSsoEnabled(true); + createdConf.setOrder(1); + binder.update(policy, created); + + DefaultAccessPolicyConf updatedConf = + (DefaultAccessPolicyConf) binder.getPolicyTO(policy).getConf(); + assertTrue(updatedConf.isSsoEnabled()); + assertEquals(1, updatedConf.getOrder()); + } + + @Test + void createUpdateAttrReleasePolicy() { + DefaultAttrReleasePolicyConf conf = new DefaultAttrReleasePolicyConf(); + conf.getAllowedAttrs().add("cn"); + conf.getAllowedAttrs().add("givenName"); + conf.getExcludedAttrs().add("secret"); + conf.setPrincipalIdAttr("uid"); + + AttrReleasePolicyTO attrReleasePolicyTO = new AttrReleasePolicyTO(); + attrReleasePolicyTO.setName("test attr release policy"); + attrReleasePolicyTO.setOrder(3); + attrReleasePolicyTO.setStatus(false); + attrReleasePolicyTO.setConf(conf); + + Policy policy = policyDAO.save(binder.create(attrReleasePolicyTO)); + + AttrReleasePolicyTO created = binder.getPolicyTO(policy); + assertNotNull(created.getKey()); + assertEquals("test attr release policy", created.getName()); + assertEquals(3, created.getOrder()); + assertFalse(created.getStatus()); + DefaultAttrReleasePolicyConf createdConf = (DefaultAttrReleasePolicyConf) created.getConf(); + assertEquals(List.of("cn", "givenName"), createdConf.getAllowedAttrs()); + assertEquals(List.of("secret"), createdConf.getExcludedAttrs()); + assertEquals("uid", createdConf.getPrincipalIdAttr()); + + created.setOrder(5); + created.setStatus(true); + createdConf.getAllowedAttrs().clear(); + binder.update(policy, created); + + AttrReleasePolicyTO updated = binder.getPolicyTO(policy); + assertEquals(5, updated.getOrder()); + assertTrue(updated.getStatus()); + assertTrue(((DefaultAttrReleasePolicyConf) updated.getConf()).getAllowedAttrs().isEmpty()); + } + + @Test + void createUpdateTicketExpirationPolicy() { + DefaultTicketExpirationPolicyConf conf = new DefaultTicketExpirationPolicyConf(); + DefaultTicketExpirationPolicyConf.TGTConf tgtConf = new DefaultTicketExpirationPolicyConf.TGTConf(); + tgtConf.setMaxTimeToLiveInSeconds(600); + conf.setTgtConf(tgtConf); + DefaultTicketExpirationPolicyConf.STConf stConf = new DefaultTicketExpirationPolicyConf.STConf(); + stConf.setMaxTimeToLiveInSeconds(300); + stConf.setNumberOfUses(2); + conf.setStConf(stConf); + + TicketExpirationPolicyTO ticketExpirationPolicyTO = new TicketExpirationPolicyTO(); + ticketExpirationPolicyTO.setName("test ticket expiration policy"); + ticketExpirationPolicyTO.setConf(conf); + + Policy policy = policyDAO.save(binder.create(ticketExpirationPolicyTO)); + + TicketExpirationPolicyTO created = binder.getPolicyTO(policy); + assertNotNull(created.getKey()); + assertEquals("test ticket expiration policy", created.getName()); + DefaultTicketExpirationPolicyConf createdConf = + (DefaultTicketExpirationPolicyConf) created.getConf(); + assertEquals(600, createdConf.getTgtConf().getMaxTimeToLiveInSeconds()); + assertEquals(300, createdConf.getStConf().getMaxTimeToLiveInSeconds()); + assertEquals(2, createdConf.getStConf().getNumberOfUses()); + + createdConf.getTgtConf().setMaxTimeToLiveInSeconds(900); + binder.update(policy, created); + + DefaultTicketExpirationPolicyConf updatedConf = (DefaultTicketExpirationPolicyConf) binder. + getPolicyTO(policy).getConf(); + assertEquals(900, updatedConf.getTgtConf().getMaxTimeToLiveInSeconds()); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderTest.java new file mode 100644 index 00000000000..abc0e41e453 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderTest.java @@ -0,0 +1,240 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.syncope.common.lib.Attr; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.to.RealmTO; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.IdRepoImplementationType; +import org.apache.syncope.common.lib.types.ImplementationEngine; +import org.apache.syncope.core.persistence.api.dao.ImplementationDAO; +import org.apache.syncope.core.persistence.api.dao.RealmDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.provisioning.api.PropagationByResource; +import org.apache.syncope.core.provisioning.api.data.RealmDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class RealmDataBinderTest extends AbstractDataBinderTest { + + private static final String ROOT_KEY = "e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"; + + private static final String EVEN_KEY = "c5b75db1-fce7-470f-b780-3b9934d82a9d"; + + private static final String TWO_KEY = "0679e069-7355-4b20-bd11-a5a0a5453c7c"; + + private static final String ACCOUNT_POLICY_KEY = "20ab5a8c-4b0c-432c-b957-f7fb9784d9f7"; + + private static final String PASSWORD_POLICY_KEY = "ce93fcda-dc3a-4369-a7b0-a6108c261c85"; + + private static final String AUTH_POLICY_KEY = "b912a0d4-a890-416f-9ab8-84ab077eb028"; + + private static final String ACCESS_POLICY_KEY = "419935c7-deb3-40b3-8a9a-683037e523a2"; + + private static final String ATTR_RELEASE_POLICY_KEY = "319935c7-deb3-40b3-8a9a-683037e523a2"; + + private static final String ORG_UNIT_RESOURCE = "resource-ldap-orgunit"; + + @Autowired + private RealmDataBinder binder; + + @Autowired + private RealmDAO realmDAO; + + @Autowired + private ImplementationDAO implementationDAO; + + @Autowired + private EntityFactory entityFactory; + + protected Attr attr(final String schema, final String value) { + return new Attr.Builder(schema).value(value).build(); + } + + @Test + void getRealmTO() { + RealmTO root = binder.getRealmTO(realmDAO.findById(ROOT_KEY).orElseThrow(), true); + assertEquals(ROOT_KEY, root.getKey()); + assertEquals(SyncopeConstants.ROOT_REALM, root.getName()); + assertEquals(SyncopeConstants.ROOT_REALM, root.getFullPath()); + assertNull(root.getParent()); + assertEquals("986d1236-3ac5-4a19-810c-5ab21d79cba1", root.getPasswordPolicy()); + + RealmTO two = binder.getRealmTO(realmDAO.findById(TWO_KEY).orElseThrow(), true); + assertEquals(TWO_KEY, two.getKey()); + assertEquals("two", two.getName()); + assertEquals("/even/two", two.getFullPath()); + assertEquals(EVEN_KEY, two.getParent()); + assertEquals(ACCOUNT_POLICY_KEY, two.getAccountPolicy()); + assertEquals(PASSWORD_POLICY_KEY, two.getPasswordPolicy()); + } + + @Test + void getRealmTONotAdmin() { + RealmTO two = binder.getRealmTO(realmDAO.findById(TWO_KEY).orElseThrow(), false); + assertEquals("/even/two", two.getFullPath()); + assertNull(two.getAccountPolicy()); + assertNull(two.getPasswordPolicy()); + } + + @Test + void createUpdate() { + Realm root = realmDAO.findById(ROOT_KEY).orElseThrow(); + + RealmTO realmTO = new RealmTO(); + realmTO.setName("test"); + + Realm created = realmDAO.save(binder.create(root, realmTO)); + + RealmTO createdTO = binder.getRealmTO(created, true); + assertNotNull(createdTO.getKey()); + assertEquals("test", createdTO.getName()); + assertEquals("/test", createdTO.getFullPath()); + assertEquals(ROOT_KEY, createdTO.getParent()); + + createdTO.setName("test2"); + createdTO.setAccountPolicy("06e2ed52-6966-44aa-a177-a0ca7434201f"); + PropagationByResource propByRes = binder.update(created, createdTO); + assertNotNull(propByRes); + realmDAO.save(created); + + RealmTO updated = binder.getRealmTO(created, true); + assertEquals("test2", updated.getName()); + assertEquals("/test2", updated.getFullPath()); + assertEquals("06e2ed52-6966-44aa-a177-a0ca7434201f", updated.getAccountPolicy()); + } + + @Test + void createFull() { + Implementation action = entityFactory.newEntity(Implementation.class); + action.setKey("TestRealmLogicActions"); + action.setEngine(ImplementationEngine.JAVA); + action.setType(IdRepoImplementationType.LOGIC_ACTIONS); + action.setBody("org.apache.syncope.core.provisioning.java.data.RealmDataBinderTest"); + implementationDAO.save(action); + + Realm root = realmDAO.findById(ROOT_KEY).orElseThrow(); + + RealmTO realmTO = new RealmTO(); + realmTO.setName("full"); + realmTO.getAnyTypeClasses().add("minimal user"); + realmTO.getAnyTypeClasses().add("not-existing"); + realmTO.getPlainAttrs().add(attr("fullname", "Full Name")); + realmTO.getPlainAttrs().add(attr("userId", "full@example.com")); + realmTO.getPlainAttrs().add(attr("firstname", "Mario")); + realmTO.getPlainAttrs().add(attr("surname", "Rossi")); + realmTO.getPlainAttrs().add(attr("not-existing", "value")); + realmTO.setAccountPolicy(ACCOUNT_POLICY_KEY); + realmTO.setPasswordPolicy(PASSWORD_POLICY_KEY); + realmTO.setAuthPolicy(AUTH_POLICY_KEY); + realmTO.setAccessPolicy(ACCESS_POLICY_KEY); + realmTO.setAttrReleasePolicy(ATTR_RELEASE_POLICY_KEY); + realmTO.getActions().add(action.getKey()); + realmTO.getActions().add("not-existing"); + + UserTO template = new UserTO(); + template.setUsername("'templated'"); + template.getPlainAttrs().add(attr("firstname", "'Mario'")); + realmTO.getTemplates().put(AnyTypeKind.USER.name(), template); + + Realm created = realmDAO.save(binder.create(root, realmTO)); + entityManager.flush(); + + RealmTO actual = binder.getRealmTO(created, true); + assertEquals("full", actual.getName()); + assertEquals("/full", actual.getFullPath()); + assertEquals(ROOT_KEY, actual.getParent()); + assertEquals(1, actual.getAnyTypeClasses().size()); + assertEquals("minimal user", actual.getAnyTypeClasses().getFirst()); + assertEquals(ACCOUNT_POLICY_KEY, actual.getAccountPolicy()); + assertEquals(PASSWORD_POLICY_KEY, actual.getPasswordPolicy()); + assertEquals(AUTH_POLICY_KEY, actual.getAuthPolicy()); + assertEquals(ACCESS_POLICY_KEY, actual.getAccessPolicy()); + assertEquals(ATTR_RELEASE_POLICY_KEY, actual.getAttrReleasePolicy()); + assertEquals(1, actual.getActions().size()); + assertEquals(action.getKey(), actual.getActions().getFirst()); + assertTrue(actual.getTemplates().containsKey(AnyTypeKind.USER.name())); + assertEquals("Full Name", actual.getPlainAttrs().stream(). + filter(a -> "fullname".equals(a.getSchema())). + findFirst().orElseThrow().getValues().getFirst()); + } + + @Test + void updateResources() { + Realm two = realmDAO.findById(TWO_KEY).orElseThrow(); + RealmTO realmTO = binder.getRealmTO(two, true); + realmTO.getResources().add(ORG_UNIT_RESOURCE); + realmTO.getResources().add("not-existing"); + + PropagationByResource propByRes = binder.update(two, realmTO); + assertNotNull(propByRes); + realmDAO.save(two); + entityManager.flush(); + + RealmTO updated = binder.getRealmTO(two, true); + assertEquals(1, updated.getResources().size()); + assertEquals(ORG_UNIT_RESOURCE, updated.getResources().getFirst()); + + // now remove the resource again + updated.getResources().clear(); + binder.update(two, updated); + realmDAO.save(two); + entityManager.flush(); + + assertTrue(binder.getRealmTO(two, true).getResources().isEmpty()); + } + + @Test + void createMissingMandatory() { + Realm root = realmDAO.findById(ROOT_KEY).orElseThrow(); + + RealmTO realmTO = new RealmTO(); + realmTO.setName("missing"); + realmTO.getAnyTypeClasses().add("minimal user"); + realmTO.getPlainAttrs().add(attr("firstname", "Mario")); + + assertThrows(SyncopeClientException.class, () -> binder.create(root, realmTO)); + } + + @Test + void createInvalidTemplate() { + Realm root = realmDAO.findById(ROOT_KEY).orElseThrow(); + + RealmTO realmTO = new RealmTO(); + realmTO.setName("invalid-template"); + + UserTO template = new UserTO(); + template.setUsername("not a valid (("); + realmTO.getTemplates().put(AnyTypeKind.USER.name(), template); + realmTO.getTemplates().put("not-existing", template); + + assertThrows(SyncopeClientException.class, () -> binder.create(root, realmTO)); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/RelationshipTypeDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/RelationshipTypeDataBinderTest.java new file mode 100644 index 00000000000..0249287475f --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/RelationshipTypeDataBinderTest.java @@ -0,0 +1,99 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.apache.syncope.common.lib.to.RelationshipTypeTO; +import org.apache.syncope.common.lib.to.TypeExtensionTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; +import org.apache.syncope.core.persistence.api.entity.RelationshipType; +import org.apache.syncope.core.provisioning.api.data.RelationshipTypeDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class RelationshipTypeDataBinderTest extends AbstractDataBinderTest { + + @Autowired + private RelationshipTypeDataBinder binder; + + @Autowired + private RelationshipTypeDAO relationshipTypeDAO; + + @Test + void get() { + RelationshipTypeTO relationshipType = binder.getRelationshipTypeTO( + relationshipTypeDAO.findById("neighborhood").orElseThrow()); + assertEquals("neighborhood", relationshipType.getKey()); + assertEquals("Models the act that an object is near another", relationshipType.getDescription()); + assertEquals(AnyTypeKind.USER.name(), relationshipType.getLeftEndAnyType()); + assertEquals("PRINTER", relationshipType.getRightEndAnyType()); + } + + @Test + void createUpdate() { + RelationshipTypeTO relationshipTypeTO = new RelationshipTypeTO(); + relationshipTypeTO.setKey("testRelationship"); + relationshipTypeTO.setDescription("a test relationship"); + relationshipTypeTO.setLeftEndAnyType(AnyTypeKind.USER.name()); + relationshipTypeTO.setRightEndAnyType("PRINTER"); + + TypeExtensionTO typeExtension = new TypeExtensionTO(); + typeExtension.setAnyType(AnyTypeKind.USER.name()); + typeExtension.getAuxClasses().addAll(List.of("other", "not-existing")); + relationshipTypeTO.getTypeExtensions().add(typeExtension); + TypeExtensionTO invalid = new TypeExtensionTO(); + invalid.setAnyType("not-existing"); + relationshipTypeTO.getTypeExtensions().add(invalid); + + RelationshipType relationshipType = relationshipTypeDAO.save(binder.create(relationshipTypeTO)); + + RelationshipTypeTO created = binder.getRelationshipTypeTO(relationshipType); + assertEquals("testRelationship", created.getKey()); + assertEquals("a test relationship", created.getDescription()); + assertEquals(1, created.getTypeExtensions().size()); + assertEquals(List.of("other"), created.getTypeExtension(AnyTypeKind.USER.name()).orElseThrow().getAuxClasses()); + + created.setDescription("an updated test relationship"); + created.getTypeExtensions().clear(); + binder.update(relationshipType, created); + + RelationshipTypeTO updated = binder.getRelationshipTypeTO(relationshipType); + assertEquals("an updated test relationship", updated.getDescription()); + assertTrue(updated.getTypeExtensions().isEmpty()); + } + + @Test + void invalidAnyTypes() { + RelationshipTypeTO relationshipTypeTO = new RelationshipTypeTO(); + relationshipTypeTO.setKey("testRelationship"); + relationshipTypeTO.setLeftEndAnyType("not-existing"); + relationshipTypeTO.setRightEndAnyType("PRINTER"); + assertThrows(NotFoundException.class, () -> binder.create(relationshipTypeTO)); + + relationshipTypeTO.setLeftEndAnyType(AnyTypeKind.USER.name()); + relationshipTypeTO.setRightEndAnyType("not-existing"); + assertThrows(NotFoundException.class, () -> binder.create(relationshipTypeTO)); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/RemediationDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/RemediationDataBinderTest.java new file mode 100644 index 00000000000..0f21fccc5a1 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/RemediationDataBinderTest.java @@ -0,0 +1,145 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.OffsetDateTime; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.request.UserCR; +import org.apache.syncope.common.lib.request.UserUR; +import org.apache.syncope.common.lib.to.RemediationTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.common.lib.types.TaskType; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.dao.RemediationDAO; +import org.apache.syncope.core.persistence.api.dao.TaskDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.Remediation; +import org.apache.syncope.core.persistence.api.entity.task.PullTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.provisioning.api.data.RemediationDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class RemediationDataBinderTest extends AbstractDataBinderTest { + + private static final String PULL_TASK_KEY = "c41b9b71-9bfa-4f90-89f2-84787def4c5c"; + + @Autowired + private RemediationDataBinder binder; + + @Autowired + private RemediationDAO remediationDAO; + + @Autowired + private AnyTypeDAO anyTypeDAO; + + @Autowired + private TaskDAO taskDAO; + + @Autowired + private EntityFactory entityFactory; + + protected Remediation newRemediation(final ResourceOperation operation) { + Remediation remediation = entityFactory.newEntity(Remediation.class); + remediation.setAnyType(anyTypeDAO.findById(AnyTypeKind.USER.name()).orElseThrow()); + remediation.setOperation(operation); + remediation.setError("error"); + remediation.setInstant(OffsetDateTime.now()); + remediation.setRemoteName("remoteName"); + + PullTask pullTask = (PullTask) taskDAO.findById(TaskType.PULL, PULL_TASK_KEY).orElseThrow(); + remediation.setPullTask(pullTask); + + return remediation; + } + + @Test + void createPayload() { + Remediation remediation = newRemediation(ResourceOperation.CREATE); + UserCR userCR = new UserCR(); + userCR.setRealm(SyncopeConstants.ROOT_REALM); + userCR.setUsername("rossini"); + userCR.setPassword("password"); + remediation.setPayload(userCR); + + remediation = remediationDAO.save(remediation); + entityManager.flush(); + + RemediationTO actual = binder.getRemediationTO( + remediationDAO.findById(remediation.getKey()).orElseThrow()); + assertEquals(remediation.getKey(), actual.getKey()); + assertEquals(AnyTypeKind.USER.name(), actual.getAnyType()); + assertEquals(ResourceOperation.CREATE, actual.getOperation()); + assertEquals("error", actual.getError()); + assertEquals("remoteName", actual.getRemoteName()); + assertEquals(PULL_TASK_KEY, actual.getPullTask()); + assertEquals("resource-csv", actual.getResource()); + assertTrue(actual.getAnyCRPayload() instanceof UserCR); + assertEquals("rossini", ((UserCR) actual.getAnyCRPayload()).getUsername()); + } + + @Test + void updatePayload() { + Remediation remediation = newRemediation(ResourceOperation.UPDATE); + UserUR userUR = new UserUR(); + userUR.setKey("1417acbe-cbf6-4277-9372-e75e04f97000"); + remediation.setPayload(userUR); + + remediation = remediationDAO.save(remediation); + entityManager.flush(); + + RemediationTO actual = binder.getRemediationTO( + remediationDAO.findById(remediation.getKey()).orElseThrow()); + assertEquals(ResourceOperation.UPDATE, actual.getOperation()); + assertTrue(actual.getAnyURPayload() instanceof UserUR); + assertEquals(userUR.getKey(), actual.getAnyURPayload().getKey()); + } + + @Test + void deletePayload() { + String key = "1417acbe-cbf6-4277-9372-e75e04f97000"; + + Remediation remediation = newRemediation(ResourceOperation.DELETE); + remediation.setPayload(key); + + remediation = remediationDAO.save(remediation); + entityManager.flush(); + + RemediationTO actual = binder.getRemediationTO( + remediationDAO.findById(remediation.getKey()).orElseThrow()); + assertEquals(ResourceOperation.DELETE, actual.getOperation()); + assertEquals(key, actual.getKeyPayload()); + } + + @Test + void noPayload() { + Remediation remediation = newRemediation(ResourceOperation.NONE); + remediation.setPayload("whatever"); + + RemediationTO actual = binder.getRemediationTO(remediation); + assertEquals(ResourceOperation.NONE, actual.getOperation()); + assertEquals(AnyTypeKind.USER.name(), actual.getAnyType()); + assertEquals(PULL_TASK_KEY, actual.getPullTask()); + assertEquals("resource-csv", actual.getResource()); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ReportDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ReportDataBinderTest.java new file mode 100644 index 00000000000..a0c909153ec --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ReportDataBinderTest.java @@ -0,0 +1,100 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.ExecTO; +import org.apache.syncope.common.lib.to.ReportTO; +import org.apache.syncope.core.persistence.api.dao.ReportDAO; +import org.apache.syncope.core.persistence.api.entity.Report; +import org.apache.syncope.core.provisioning.api.data.ReportDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class ReportDataBinderTest extends AbstractDataBinderTest { + + private static final String REPORT_KEY = "0062ea9c-924d-4ecf-9961-4492a8cc6d1b"; + + @Autowired + private ReportDataBinder binder; + + @Autowired + private ReportDAO reportDAO; + + @Test + void get() { + ReportTO report = binder.getReportTO(reportDAO.findById(REPORT_KEY).orElseThrow()); + assertEquals(REPORT_KEY, report.getKey()); + assertEquals("test", report.getName()); + assertEquals("application/pdf", report.getMimeType()); + assertEquals("pdf", report.getFileExt()); + assertEquals("SampleReportJobDelegate", report.getJobDelegate()); + assertTrue(report.isActive()); + assertEquals("SUCCESS", report.getLatestExecStatus()); + assertEquals(1, report.getExecutions().size()); + } + + @Test + void getExec() { + Report report = reportDAO.findById(REPORT_KEY).orElseThrow(); + ExecTO exec = binder.getExecTO(report.getExecs().getFirst()); + assertEquals(report.getExecs().getFirst().getKey(), exec.getKey()); + assertEquals(REPORT_KEY, exec.getRefKey()); + assertEquals(binder.buildRefDesc(report), exec.getRefDesc()); + assertEquals("SUCCESS", exec.getStatus()); + } + + @Test + void getReport() { + Report report = reportDAO.findById(REPORT_KEY).orElseThrow(); + ReportTO reportTO = binder.getReportTO(report); + + reportTO.setName("updated"); + reportTO.setMimeType("text/plain"); + reportTO.setFileExt("txt"); + reportTO.setCronExpression("0 0/10 * * * ?"); + reportTO.setActive(false); + + binder.getReport(report, reportTO); + + assertEquals("updated", report.getName()); + assertEquals("text/plain", report.getMimeType()); + assertEquals("txt", report.getFileExt()); + assertEquals("0 0/10 * * * ?", report.getCronExpression()); + assertEquals("SampleReportJobDelegate", report.getJobDelegate().getKey()); + assertFalse(report.isActive()); + } + + @Test + void invalidJobDelegate() { + Report report = reportDAO.findById(REPORT_KEY).orElseThrow(); + ReportTO reportTO = binder.getReportTO(report); + + reportTO.setJobDelegate("not-existing"); + assertThrows(SyncopeClientException.class, () -> binder.getReport(report, reportTO)); + + reportTO.setJobDelegate("EmailAddressValidator"); + assertThrows(SyncopeClientException.class, () -> binder.getReport(report, reportTO)); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java index 5a7570870ac..6e279d90846 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java @@ -19,57 +19,55 @@ package org.apache.syncope.core.provisioning.java.data; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.SyncopeClientException; import org.apache.syncope.common.lib.to.Item; import org.apache.syncope.common.lib.to.Mapping; +import org.apache.syncope.common.lib.to.OrgUnit; import org.apache.syncope.common.lib.to.Provision; import org.apache.syncope.common.lib.to.ResourceTO; import org.apache.syncope.common.lib.types.AnyTypeKind; -import org.apache.syncope.common.lib.types.IdMEntitlement; +import org.apache.syncope.common.lib.types.ConnConfPropSchema; +import org.apache.syncope.common.lib.types.ConnConfProperty; +import org.apache.syncope.common.lib.types.ConnectorCapability; +import org.apache.syncope.common.lib.types.IdMImplementationType; +import org.apache.syncope.common.lib.types.ImplementationEngine; import org.apache.syncope.common.lib.types.MappingPurpose; +import org.apache.syncope.common.lib.types.TraceLevel; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.ImplementationDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.Implementation; import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.provisioning.api.data.ResourceDataBinder; -import org.apache.syncope.core.provisioning.java.AbstractTest; -import org.apache.syncope.core.spring.security.SyncopeAuthenticationDetails; -import org.apache.syncope.core.spring.security.SyncopeGrantedAuthority; import org.identityconnectors.framework.common.objects.ObjectClass; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.transaction.annotation.Transactional; - -@Transactional -public class ResourceDataBinderTest extends AbstractTest { - - @BeforeAll - public static void setAuthContext() { - List authorities = IdMEntitlement.values().stream(). - map(entitlement -> new SyncopeGrantedAuthority(entitlement, SyncopeConstants.ROOT_REALM)). - collect(Collectors.toList()); - - UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken( - new org.springframework.security.core.userdetails.User( - "admin", "FAKE_PASSWORD", authorities), "FAKE_PASSWORD", authorities); - auth.setDetails(new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, null)); - SecurityContextHolder.getContext().setAuthentication(auth); - } - @AfterAll - public static void unsetAuthContext() { - SecurityContextHolder.getContext().setAuthentication(null); - } +class ResourceDataBinderTest extends AbstractDataBinderTest { + + private static final String CONNECTOR_KEY = "88a7a819-dab5-46b4-9b90-0b9769eabdb8"; + + private static final String PASSWORD_POLICY_KEY = "ce93fcda-dc3a-4369-a7b0-a6108c261c85"; + + private static final String ACCOUNT_POLICY_KEY = "20ab5a8c-4b0c-432c-b957-f7fb9784d9f7"; + + private static final String PROPAGATION_POLICY_KEY = "89d322db-9878-420c-b49c-67be13df9a12"; + + private static final String INBOUND_POLICY_KEY = "66691e96-285f-4464-bc19-e68384ea4c85"; + + private static final String PUSH_POLICY_KEY = "fb6530e5-892d-4f47-a46b-180c5b6c5c83"; @Autowired private ExternalResourceDAO resourceDAO; @@ -80,12 +78,49 @@ public static void unsetAuthContext() { @Autowired private PlainSchemaDAO plainSchemaDAO; + @Autowired + private ImplementationDAO implementationDAO; + + @Autowired + private EntityFactory entityFactory; + + protected Implementation provisionSorter() { + Implementation provisionSorter = entityFactory.newEntity(Implementation.class); + provisionSorter.setKey("TestProvisionSorter"); + provisionSorter.setEngine(ImplementationEngine.JAVA); + provisionSorter.setType(IdMImplementationType.PROVISION_SORTER); + provisionSorter.setBody("org.apache.syncope.core.provisioning.java.pushpull.DefaultProvisionSorter"); + return implementationDAO.save(provisionSorter); + } + + protected Item item(final String intAttrName, final String extAttrName, final MappingPurpose purpose) { + Item item = new Item(); + item.setIntAttrName(intAttrName); + item.setExtAttrName(extAttrName); + item.setPurpose(purpose); + return item; + } + + protected ConnConfProperty confProperty(final String name, final Object value) { + ConnConfPropSchema schema = new ConnConfPropSchema(); + schema.setName(name); + schema.setType(value.getClass().getName()); + schema.setRequired(true); + schema.setOrder(1); + + ConnConfProperty property = new ConnConfProperty(); + property.setSchema(schema); + property.setOverridable(true); + property.getValues().add(value); + return property; + } + @Test - public void issue42() { + void issue42() { PlainSchema userId = plainSchemaDAO.findById("userId").orElseThrow(); Set beforeUserIdMappings = resourceDAO.findAll().stream(). - map(r -> r.getProvisionByAnyType(AnyTypeKind.USER.name())). + map(r -> r.getProvisionByAnyType(org.apache.syncope.common.lib.types.AnyTypeKind.USER.name())). flatMap(Optional::stream). flatMap(p -> p.getMapping().getItems().stream()). filter(i -> userId.getKey().equals(i.getIntAttrName())). @@ -93,23 +128,20 @@ public void issue42() { ResourceTO resourceTO = new ResourceTO(); resourceTO.setKey("resource-issue42"); - resourceTO.setConnector("88a7a819-dab5-46b4-9b90-0b9769eabdb8"); + resourceTO.setConnector(CONNECTOR_KEY); resourceTO.setEnforceMandatoryCondition(true); Provision provisionTO = new Provision(); - provisionTO.setAnyType(AnyTypeKind.USER.name()); + provisionTO.setAnyType(org.apache.syncope.common.lib.types.AnyTypeKind.USER.name()); provisionTO.setObjectClass(ObjectClass.ACCOUNT_NAME); resourceTO.getProvisions().add(provisionTO); Mapping mapping = new Mapping(); provisionTO.setMapping(mapping); - Item item = new Item(); - item.setIntAttrName("userId"); - item.setExtAttrName("campo1"); + Item item = item("userId", "campo1", MappingPurpose.BOTH); item.setConnObjectKey(true); item.setMandatoryCondition("false"); - item.setPurpose(MappingPurpose.BOTH); mapping.setConnObjectKeyItem(item); ExternalResource resource = binder.create(resourceTO); @@ -118,11 +150,12 @@ public void issue42() { ExternalResource actual = resourceDAO.findById("resource-issue42").orElseThrow(); assertEquals(resource, actual); - assertEquals(1, actual.getProvisionByAnyType(AnyTypeKind.USER.name()).orElseThrow(). + assertEquals(1, actual.getProvisionByAnyType( + org.apache.syncope.common.lib.types.AnyTypeKind.USER.name()).orElseThrow(). getMapping().getItems().size()); Set afterUserIdMappings = resourceDAO.findAll().stream(). - map(r -> r.getProvisionByAnyType(AnyTypeKind.USER.name())). + map(r -> r.getProvisionByAnyType(org.apache.syncope.common.lib.types.AnyTypeKind.USER.name())). flatMap(Optional::stream). flatMap(p -> p.getMapping().getItems().stream()). filter(i -> userId.getKey().equals(i.getIntAttrName())). @@ -130,4 +163,311 @@ public void issue42() { assertEquals(beforeUserIdMappings.size(), afterUserIdMappings.size() - 1); } + + @Test + void createFull() { + Implementation provisionSorter = provisionSorter(); + + ResourceTO resourceTO = new ResourceTO(); + resourceTO.setKey("resource-full"); + resourceTO.setConnector(CONNECTOR_KEY); + resourceTO.setEnforceMandatoryCondition(true); + resourceTO.setPropagationPriority(3); + resourceTO.setCreateTraceLevel(TraceLevel.FAILURES); + resourceTO.setUpdateTraceLevel(TraceLevel.SUMMARY); + resourceTO.setDeleteTraceLevel(TraceLevel.NONE); + resourceTO.setProvisioningTraceLevel(TraceLevel.ALL); + resourceTO.setPasswordPolicy(PASSWORD_POLICY_KEY); + resourceTO.setAccountPolicy(ACCOUNT_POLICY_KEY); + resourceTO.setPropagationPolicy(PROPAGATION_POLICY_KEY); + resourceTO.setInboundPolicy(INBOUND_POLICY_KEY); + resourceTO.setPushPolicy(PUSH_POLICY_KEY); + resourceTO.setProvisionSorter(provisionSorter.getKey()); + resourceTO.setConfOverride(Optional.of(List.of(confProperty("endpoint", "http://localhost:8080/test")))); + resourceTO.setCapabilitiesOverride(Optional.of(Set.of( + ConnectorCapability.CREATE, ConnectorCapability.UPDATE, ConnectorCapability.SEARCH))); + resourceTO.getPropagationActions().add("GenerateRandomPasswordPropagationActions"); + resourceTO.getPropagationActions().add("not-existing"); + + Provision userProvision = new Provision(); + userProvision.setAnyType(AnyTypeKind.USER.name()); + userProvision.setObjectClass(ObjectClass.ACCOUNT_NAME); + userProvision.getAuxClasses().add("csv"); + userProvision.getAuxClasses().add("not-existing"); + userProvision.setIgnoreCaseMatch(true); + userProvision.setUidOnCreate("userId"); + + Mapping userMapping = new Mapping(); + userMapping.setConnObjectLink("userId"); + userProvision.setMapping(userMapping); + + Item userKey = item("username", "userId", MappingPurpose.BOTH); + userMapping.setConnObjectKeyItem(userKey); + + Item email = item("email", "email", MappingPurpose.BOTH); + userMapping.add(email); + + Item password = item("password", "__PASSWORD__", MappingPurpose.BOTH); + password.setPassword(true); + userMapping.add(password); + + Item ctype = item("ctype", "ctype", MappingPurpose.BOTH); + userMapping.add(ctype); + + Item cn = item("cn", "cn", MappingPurpose.PROPAGATION); + cn.setPropagationJEXLTransformer("surname + ', ' + firstname"); + cn.setPullJEXLTransformer("surname"); + cn.getTransformers().add("not-existing"); + userMapping.add(cn); + + resourceTO.getProvisions().add(userProvision); + + Provision printerProvision = new Provision(); + printerProvision.setAnyType("PRINTER"); + printerProvision.setObjectClass(ObjectClass.GROUP_NAME); + printerProvision.getAuxClasses().add("csv"); + printerProvision.setUidOnCreate("not-existing"); + + Mapping printerMapping = new Mapping(); + printerMapping.setConnObjectLink("model"); + printerProvision.setMapping(printerMapping); + + Item printerKey = item("model", "model", MappingPurpose.BOTH); + printerMapping.setConnObjectKeyItem(printerKey); + printerMapping.add(item("location", "location", MappingPurpose.BOTH)); + printerMapping.add(item("csvuserid", "csvuserid", MappingPurpose.PROPAGATION)); + + resourceTO.getProvisions().add(printerProvision); + + OrgUnit orgUnit = new OrgUnit(); + orgUnit.setObjectClass(ObjectClass.GROUP_NAME); + orgUnit.setConnObjectLink("cn=groups,dc=example,dc=com"); + orgUnit.setIgnoreCaseMatch(true); + + Item orgUnitKey = item("key", "cn", MappingPurpose.BOTH); + orgUnit.setConnObjectKeyItem(orgUnitKey); + orgUnit.add(item("name", "description", MappingPurpose.BOTH)); + resourceTO.setOrgUnit(orgUnit); + + ExternalResource resource = binder.create(resourceTO); + resourceDAO.save(resource); + entityManager.flush(); + + ExternalResource actual = resourceDAO.findById("resource-full").orElseThrow(); + assertEquals(CONNECTOR_KEY, actual.getConnector().getKey()); + assertTrue(actual.isEnforceMandatoryCondition()); + assertEquals(3, actual.getPropagationPriority()); + assertEquals(TraceLevel.FAILURES, actual.getCreateTraceLevel()); + assertEquals(TraceLevel.SUMMARY, actual.getUpdateTraceLevel()); + assertEquals(TraceLevel.NONE, actual.getDeleteTraceLevel()); + assertEquals(TraceLevel.ALL, actual.getProvisioningTraceLevel()); + assertEquals(PASSWORD_POLICY_KEY, actual.getPasswordPolicy().getKey()); + assertEquals(ACCOUNT_POLICY_KEY, actual.getAccountPolicy().getKey()); + assertEquals(PROPAGATION_POLICY_KEY, actual.getPropagationPolicy().getKey()); + assertEquals(INBOUND_POLICY_KEY, actual.getInboundPolicy().getKey()); + assertEquals(PUSH_POLICY_KEY, actual.getPushPolicy().getKey()); + assertEquals(provisionSorter.getKey(), actual.getProvisionSorter().getKey()); + assertEquals("endpoint", + actual.getConfOverride().orElseThrow().getFirst().getSchema().getName()); + assertEquals(Set.of( + ConnectorCapability.CREATE, ConnectorCapability.UPDATE, ConnectorCapability.SEARCH), + actual.getCapabilitiesOverride().orElseThrow()); + assertEquals(List.of("GenerateRandomPasswordPropagationActions"), + actual.getPropagationActions().stream().map(Implementation::getKey).toList()); + + Provision userProvisionActual = actual.getProvisionByAnyType(AnyTypeKind.USER.name()).orElseThrow(); + assertEquals(ObjectClass.ACCOUNT_NAME, userProvisionActual.getObjectClass()); + assertEquals(List.of("csv"), userProvisionActual.getAuxClasses()); + assertTrue(userProvisionActual.isIgnoreCaseMatch()); + assertEquals("userId", userProvisionActual.getUidOnCreate()); + assertEquals("username", userProvisionActual.getMapping().getConnObjectKeyItem(). + orElseThrow().getIntAttrName()); + assertEquals(5, userProvisionActual.getMapping().getItems().size()); + Item cnActual = userProvisionActual.getMapping().getItems().stream(). + filter(i -> "cn".equals(i.getIntAttrName())). + findFirst().orElseThrow(); + assertEquals(MappingPurpose.PROPAGATION, cnActual.getPurpose()); + assertEquals("surname + ', ' + firstname", cnActual.getPropagationJEXLTransformer()); + assertEquals("surname", cnActual.getPullJEXLTransformer()); + assertTrue(cnActual.getTransformers().isEmpty()); + + Provision printerProvisionActual = actual.getProvisionByAnyType("PRINTER").orElseThrow(); + assertEquals(ObjectClass.GROUP_NAME, printerProvisionActual.getObjectClass()); + assertEquals(List.of("csv"), printerProvisionActual.getAuxClasses()); + assertNull(printerProvisionActual.getUidOnCreate()); + assertEquals("model", printerProvisionActual.getMapping().getConnObjectKeyItem(). + orElseThrow().getIntAttrName()); + + assertNotNull(actual.getOrgUnit()); + assertEquals(ObjectClass.GROUP_NAME, actual.getOrgUnit().getObjectClass()); + assertEquals("cn=groups,dc=example,dc=com", actual.getOrgUnit().getConnObjectLink()); + assertTrue(actual.getOrgUnit().isIgnoreCaseMatch()); + assertEquals("key", actual.getOrgUnit().getConnObjectKeyItem().orElseThrow().getIntAttrName()); + + ResourceTO actualTO = binder.getResourceTO(actual); + assertEquals("resource-full", actualTO.getKey()); + assertEquals(CONNECTOR_KEY, actualTO.getConnector()); + assertEquals(2, actualTO.getProvisions().size()); + assertNotNull(actualTO.getOrgUnit()); + assertEquals(provisionSorter.getKey(), actualTO.getProvisionSorter()); + assertFalse(actualTO.getPropagationActions().isEmpty()); + } + + @Test + void updateFull() { + Implementation provisionSorter = provisionSorter(); + + ExternalResource resource = resourceDAO.findById("ws-target-resource-1").orElseThrow(); + + ResourceTO resourceTO = new ResourceTO(); + resourceTO.setKey(resource.getKey()); + resourceTO.setConnector(CONNECTOR_KEY); + resourceTO.setEnforceMandatoryCondition(true); + resourceTO.setPropagationPriority(2); + resourceTO.setCreateTraceLevel(TraceLevel.SUMMARY); + resourceTO.setUpdateTraceLevel(TraceLevel.FAILURES); + resourceTO.setDeleteTraceLevel(TraceLevel.ALL); + resourceTO.setProvisioningTraceLevel(TraceLevel.NONE); + resourceTO.setPasswordPolicy(PASSWORD_POLICY_KEY); + resourceTO.setAccountPolicy(ACCOUNT_POLICY_KEY); + resourceTO.setPropagationPolicy(PROPAGATION_POLICY_KEY); + resourceTO.setInboundPolicy(INBOUND_POLICY_KEY); + resourceTO.setPushPolicy(PUSH_POLICY_KEY); + resourceTO.setProvisionSorter(provisionSorter.getKey()); + resourceTO.setConfOverride(Optional.of(List.of(confProperty("endpoint", "http://localhost:8081/test")))); + resourceTO.setCapabilitiesOverride(Optional.of(Set.of(ConnectorCapability.UPDATE))); + resourceTO.getPropagationActions().add("GenerateRandomPasswordPropagationActions"); + + Provision userProvision = new Provision(); + userProvision.setAnyType(AnyTypeKind.USER.name()); + userProvision.setObjectClass(ObjectClass.ACCOUNT_NAME); + userProvision.getAuxClasses().add("csv"); + userProvision.setIgnoreCaseMatch(true); + userProvision.setUidOnCreate("userId"); + + Mapping userMapping = new Mapping(); + userMapping.setConnObjectLink("userId"); + userProvision.setMapping(userMapping); + userMapping.setConnObjectKeyItem(item("username", "userId", MappingPurpose.BOTH)); + userMapping.add(item("email", "email", MappingPurpose.BOTH)); + Item password = item("password", "__PASSWORD__", MappingPurpose.BOTH); + password.setPassword(true); + userMapping.add(password); + userMapping.add(item("cn", "cn", MappingPurpose.PROPAGATION)); + + resourceTO.getProvisions().add(userProvision); + + Provision printerProvision = new Provision(); + printerProvision.setAnyType("PRINTER"); + printerProvision.setObjectClass(ObjectClass.GROUP_NAME); + Mapping printerMapping = new Mapping(); + printerProvision.setMapping(printerMapping); + printerMapping.setConnObjectKeyItem(item("model", "model", MappingPurpose.BOTH)); + printerMapping.add(item("location", "location", MappingPurpose.BOTH)); + resourceTO.getProvisions().add(printerProvision); + + OrgUnit orgUnit = new OrgUnit(); + orgUnit.setObjectClass(ObjectClass.GROUP_NAME); + orgUnit.setConnObjectLink("cn=units,dc=example,dc=com"); + orgUnit.setConnObjectKeyItem(item("key", "cn", MappingPurpose.BOTH)); + resourceTO.setOrgUnit(orgUnit); + + binder.update(resource, resourceTO); + entityManager.flush(); + + ExternalResource actual = resourceDAO.findById(resource.getKey()).orElseThrow(); + assertTrue(actual.isEnforceMandatoryCondition()); + assertEquals(2, actual.getPropagationPriority()); + assertEquals(TraceLevel.SUMMARY, actual.getCreateTraceLevel()); + assertEquals(TraceLevel.FAILURES, actual.getUpdateTraceLevel()); + assertEquals(TraceLevel.ALL, actual.getDeleteTraceLevel()); + assertEquals(TraceLevel.NONE, actual.getProvisioningTraceLevel()); + assertEquals(PASSWORD_POLICY_KEY, actual.getPasswordPolicy().getKey()); + assertEquals(ACCOUNT_POLICY_KEY, actual.getAccountPolicy().getKey()); + assertEquals(PROPAGATION_POLICY_KEY, actual.getPropagationPolicy().getKey()); + assertEquals(INBOUND_POLICY_KEY, actual.getInboundPolicy().getKey()); + assertEquals(PUSH_POLICY_KEY, actual.getPushPolicy().getKey()); + assertEquals(provisionSorter.getKey(), actual.getProvisionSorter().getKey()); + assertEquals("http://localhost:8081/test", + actual.getConfOverride().orElseThrow().getFirst().getValues().getFirst()); + assertEquals(Set.of(ConnectorCapability.UPDATE), actual.getCapabilitiesOverride().orElseThrow()); + assertEquals(1, actual.getPropagationActions().size()); + + Provision userProvisionActual = actual.getProvisionByAnyType(AnyTypeKind.USER.name()).orElseThrow(); + assertEquals(List.of("csv"), userProvisionActual.getAuxClasses()); + assertEquals("userId", userProvisionActual.getUidOnCreate()); + assertEquals(4, userProvisionActual.getMapping().getItems().size()); + assertNotNull(actual.getProvisionByAnyType("PRINTER").orElseThrow()); + assertNotNull(actual.getOrgUnit()); + assertEquals("cn=units,dc=example,dc=com", actual.getOrgUnit().getConnObjectLink()); + + // now remove org unit, provisions and overrides + ResourceTO cleanup = new ResourceTO(); + cleanup.setKey(actual.getKey()); + cleanup.setConnector(CONNECTOR_KEY); + cleanup.setCreateTraceLevel(TraceLevel.ALL); + cleanup.setUpdateTraceLevel(TraceLevel.ALL); + cleanup.setDeleteTraceLevel(TraceLevel.ALL); + cleanup.setProvisioningTraceLevel(TraceLevel.ALL); + cleanup.setConfOverride(Optional.empty()); + cleanup.setCapabilitiesOverride(Optional.empty()); + + binder.update(actual, cleanup); + entityManager.flush(); + + ExternalResource cleaned = resourceDAO.findById(actual.getKey()).orElseThrow(); + assertNull(cleaned.getOrgUnit()); + assertTrue(cleaned.getProvisions().isEmpty()); + assertTrue(cleaned.getConfOverride().isEmpty()); + assertTrue(cleaned.getCapabilitiesOverride().isEmpty()); + assertTrue(cleaned.getPropagationActions().isEmpty()); + assertNull(cleaned.getProvisionSorter()); + assertNull(cleaned.getPasswordPolicy()); + assertNull(cleaned.getAccountPolicy()); + assertNull(cleaned.getPropagationPolicy()); + assertNull(cleaned.getInboundPolicy()); + assertNull(cleaned.getPushPolicy()); + } + + @Test + void invalid() { + ResourceTO nullObjectClass = new ResourceTO(); + nullObjectClass.setKey("resource-invalid"); + nullObjectClass.setConnector(CONNECTOR_KEY); + Provision provision = new Provision(); + provision.setAnyType(AnyTypeKind.USER.name()); + nullObjectClass.getProvisions().add(provision); + assertThrows(SyncopeClientException.class, () -> binder.create(nullObjectClass)); + + ResourceTO nullIntAttrName = new ResourceTO(); + nullIntAttrName.setKey("resource-invalid"); + nullIntAttrName.setConnector(CONNECTOR_KEY); + Provision provisionTO = new Provision(); + provisionTO.setAnyType(AnyTypeKind.USER.name()); + provisionTO.setObjectClass(ObjectClass.ACCOUNT_NAME); + provisionTO.setMapping(new Mapping()); + provisionTO.getMapping().setConnObjectKeyItem(new Item()); + nullIntAttrName.getProvisions().add(provisionTO); + assertThrows(SyncopeClientException.class, () -> binder.create(nullIntAttrName)); + + ResourceTO wrongDerivedPurpose = new ResourceTO(); + wrongDerivedPurpose.setKey("resource-invalid"); + wrongDerivedPurpose.setConnector(CONNECTOR_KEY); + Provision wrongDerivedPurposeProvision = new Provision(); + wrongDerivedPurposeProvision.setAnyType(AnyTypeKind.USER.name()); + wrongDerivedPurposeProvision.setObjectClass(ObjectClass.ACCOUNT_NAME); + wrongDerivedPurposeProvision.setMapping(new Mapping()); + wrongDerivedPurposeProvision.getMapping().setConnObjectKeyItem( + item("username", "userId", MappingPurpose.BOTH)); + wrongDerivedPurposeProvision.getMapping().add( + item("cn", "cn", MappingPurpose.BOTH)); + wrongDerivedPurpose.getProvisions().add(wrongDerivedPurposeProvision); + assertThrows(SyncopeClientException.class, () -> binder.create(wrongDerivedPurpose)); + + ResourceTO invalidOrgUnit = new ResourceTO(); + invalidOrgUnit.setKey("resource-invalid"); + invalidOrgUnit.setConnector(CONNECTOR_KEY); + invalidOrgUnit.setOrgUnit(new OrgUnit()); + assertThrows(SyncopeClientException.class, () -> binder.create(invalidOrgUnit)); + } } diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderTest.java new file mode 100644 index 00000000000..9294c7d65d8 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderTest.java @@ -0,0 +1,76 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.Set; +import org.apache.syncope.common.lib.to.RoleTO; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.persistence.api.dao.RoleDAO; +import org.apache.syncope.core.persistence.api.entity.Role; +import org.apache.syncope.core.provisioning.api.data.RoleDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class RoleDataBinderTest extends AbstractDataBinderTest { + + @Autowired + private RoleDataBinder binder; + + @Autowired + private RoleDAO roleDAO; + + @Test + void get() { + RoleTO role = binder.getRoleTO(roleDAO.findById("User reviewer").orElseThrow()); + assertEquals("User reviewer", role.getKey()); + assertTrue(role.getEntitlements().containsAll( + Set.of(IdRepoEntitlement.USER_READ, IdRepoEntitlement.USER_SEARCH))); + assertEquals(Set.of("/odd", "/even"), Set.copyOf(role.getRealms())); + } + + @Test + void createUpdate() { + RoleTO roleTO = new RoleTO(); + roleTO.setKey("testRole"); + roleTO.getEntitlements().addAll(List.of(IdRepoEntitlement.USER_READ, IdRepoEntitlement.GROUP_READ)); + roleTO.getRealms().addAll(List.of("/even/two", "/even/three")); + + Role role = roleDAO.save(binder.create(roleTO)); + + RoleTO created = binder.getRoleTO(role); + assertEquals("testRole", created.getKey()); + assertEquals(Set.of(IdRepoEntitlement.USER_READ, IdRepoEntitlement.GROUP_READ), + created.getEntitlements()); + assertEquals(List.of("/even/two"), created.getRealms()); + + created.getEntitlements().clear(); + created.getEntitlements().add(IdRepoEntitlement.USER_UPDATE); + created.getRealms().clear(); + created.getRealms().add("/odd"); + binder.update(role, created); + + RoleTO updated = binder.getRoleTO(role); + assertEquals(Set.of(IdRepoEntitlement.USER_UPDATE), updated.getEntitlements()); + assertEquals(List.of("/odd"), updated.getRealms()); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/SAML2IdPEntityDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/SAML2IdPEntityDataBinderTest.java new file mode 100644 index 00000000000..ed944c26d1f --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/SAML2IdPEntityDataBinderTest.java @@ -0,0 +1,88 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import org.apache.syncope.common.lib.to.SAML2IdPEntityTO; +import org.apache.syncope.core.persistence.api.dao.SAML2IdPEntityDAO; +import org.apache.syncope.core.persistence.api.entity.am.SAML2IdPEntity; +import org.apache.syncope.core.provisioning.api.data.SAML2IdPEntityDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class SAML2IdPEntityDataBinderTest extends AbstractDataBinderTest { + + private static final String KEY = "test-saml2-idp"; + + @Autowired + private SAML2IdPEntityDataBinder binder; + + @Autowired + private SAML2IdPEntityDAO saml2IdPEntityDAO; + + protected String base64(final String value) { + return Base64.getEncoder().encodeToString(value.getBytes(StandardCharsets.UTF_8)); + } + + @Test + void createGetUpdate() { + SAML2IdPEntityTO entityTO = new SAML2IdPEntityTO(); + entityTO.setKey(KEY); + entityTO.setMetadata(base64("metadata")); + entityTO.setSigningCertificate(base64("signingCertificate")); + entityTO.setSigningKey(base64("signingKey")); + entityTO.setEncryptionCertificate(base64("encryptionCertificate")); + entityTO.setEncryptionKey(base64("encryptionKey")); + + SAML2IdPEntity created = saml2IdPEntityDAO.save(binder.create(entityTO)); + entityManager.flush(); + + SAML2IdPEntityTO actual = binder.getSAML2IdPEntityTO( + saml2IdPEntityDAO.findById(KEY).orElseThrow()); + assertEquals(KEY, actual.getKey()); + assertEquals(base64("metadata"), actual.getMetadata()); + assertEquals(base64("signingCertificate"), actual.getSigningCertificate()); + assertEquals(base64("signingKey"), actual.getSigningKey()); + assertEquals(base64("encryptionCertificate"), actual.getEncryptionCertificate()); + assertEquals(base64("encryptionKey"), actual.getEncryptionKey()); + + SAML2IdPEntityTO update = new SAML2IdPEntityTO(); + update.setKey(KEY); + update.setMetadata(base64("updatedMetadata")); + binder.update(created, update); + saml2IdPEntityDAO.save(created); + entityManager.flush(); + + SAML2IdPEntityTO updated = binder.getSAML2IdPEntityTO( + saml2IdPEntityDAO.findById(KEY).orElseThrow()); + assertEquals(base64("updatedMetadata"), updated.getMetadata()); + assertNull(updated.getSigningCertificate()); + assertNull(updated.getSigningKey()); + assertNull(updated.getEncryptionCertificate()); + assertNull(updated.getEncryptionKey()); + + SAML2IdPEntity entity = saml2IdPEntityDAO.findById(KEY).orElseThrow(); + assertArrayEquals("updatedMetadata".getBytes(StandardCharsets.UTF_8), entity.getMetadata()); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderTest.java new file mode 100644 index 00000000000..19f31d38c8d --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderTest.java @@ -0,0 +1,171 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Map; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.DerSchemaTO; +import org.apache.syncope.common.lib.to.PlainSchemaTO; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.entity.DerSchema; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.provisioning.api.data.SchemaDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class SchemaDataBinderTest extends AbstractDataBinderTest { + + @Autowired + private SchemaDataBinder binder; + + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + @Autowired + private DerSchemaDAO derSchemaDAO; + + @Test + void getPlainSchemaTO() { + PlainSchemaTO fullname = binder.getPlainSchemaTO("fullname"); + assertEquals("fullname", fullname.getKey()); + assertEquals(AttrSchemaType.String, fullname.getType()); + assertEquals("true", fullname.getMandatoryCondition()); + assertTrue(fullname.isUniqueConstraint()); + assertFalse(fullname.isMultivalue()); + assertFalse(fullname.isReadonly()); + assertEquals("minimal user", fullname.getAnyTypeClass()); + + assertEquals("yyyy-MM-dd", binder.getPlainSchemaTO("loginDate").getConversionPattern()); + assertEquals("TestDropdownValueProvider", binder.getPlainSchemaTO("dd").getDropdownValueProvider()); + assertEquals("EmailAddressValidator", binder.getPlainSchemaTO("email").getValidator()); + + PlainSchemaTO gender = binder.getPlainSchemaTO("gender"); + assertEquals(AttrSchemaType.Enum, gender.getType()); + assertEquals(Map.of("M", "Male", "F", "Female"), gender.getEnumValues()); + + PlainSchemaTO obscure = binder.getPlainSchemaTO("obscure"); + assertEquals(AttrSchemaType.Encrypted, obscure.getType()); + assertEquals("SHA", obscure.getCipherAlgorithm().name()); + assertEquals("7abcdefghilmnopqrstuvz9#", obscure.getSecretKey()); + + assertThrows(NotFoundException.class, () -> binder.getPlainSchemaTO("not-existing")); + } + + @Test + void getDerSchemaTO() { + DerSchemaTO cn = binder.getDerSchemaTO("cn"); + assertEquals("cn", cn.getKey()); + assertEquals("surname + ', ' + firstname", cn.getExpression()); + assertEquals("minimal user", cn.getAnyTypeClass()); + + assertThrows(NotFoundException.class, () -> binder.getDerSchemaTO("not-existing")); + } + + @Test + void createUpdatePlain() { + PlainSchemaTO plainSchemaTO = new PlainSchemaTO(); + plainSchemaTO.setKey("testPlain"); + plainSchemaTO.setType(AttrSchemaType.String); + plainSchemaTO.setMandatoryCondition("false"); + plainSchemaTO.setAnyTypeClass("other"); + + PlainSchema plainSchema = plainSchemaDAO.save(binder.create(plainSchemaTO)); + + PlainSchemaTO created = binder.getPlainSchemaTO("testPlain"); + assertEquals(AttrSchemaType.String, created.getType()); + assertEquals("other", created.getAnyTypeClass()); + + created.setType(AttrSchemaType.Long); + created.setMultivalue(true); + binder.update(created, plainSchema); + + PlainSchemaTO updated = binder.getPlainSchemaTO("testPlain"); + assertEquals(AttrSchemaType.Long, updated.getType()); + assertTrue(updated.isMultivalue()); + + updated.setAnyTypeClass(null); + binder.update(updated, plainSchema); + assertNull(binder.getPlainSchemaTO("testPlain").getAnyTypeClass()); + } + + @Test + void updatePlainWithAttrs() { + PlainSchemaTO fullname = binder.getPlainSchemaTO("fullname"); + PlainSchema schema = plainSchemaDAO.findById("fullname").orElseThrow(); + + fullname.setType(AttrSchemaType.Long); + assertThrows(SyncopeClientException.class, () -> binder.update(fullname, schema)); + + fullname.setType(AttrSchemaType.String); + fullname.setUniqueConstraint(false); + assertThrows(SyncopeClientException.class, () -> binder.update(fullname, schema)); + } + + @Test + void invalidPlain() { + PlainSchemaTO invalidMandatoryCondition = new PlainSchemaTO(); + invalidMandatoryCondition.setKey("testPlain"); + invalidMandatoryCondition.setType(AttrSchemaType.String); + invalidMandatoryCondition.setMandatoryCondition("1 +"); + assertThrows(SyncopeClientException.class, () -> binder.create(invalidMandatoryCondition)); + } + + @Test + void createUpdateDer() { + DerSchemaTO derSchemaTO = new DerSchemaTO(); + derSchemaTO.setKey("testDer"); + derSchemaTO.setExpression("surname + ', ' + firstname"); + derSchemaTO.setAnyTypeClass("other"); + + DerSchema derSchema = derSchemaDAO.save(binder.create(derSchemaTO)); + + DerSchemaTO created = binder.getDerSchemaTO("testDer"); + assertEquals("surname + ', ' + firstname", created.getExpression()); + assertEquals("other", created.getAnyTypeClass()); + + created.setExpression("firstname + ' ' + surname"); + binder.update(created, derSchema); + assertEquals("firstname + ' ' + surname", binder.getDerSchemaTO("testDer").getExpression()); + + created.setAnyTypeClass(null); + binder.update(created, derSchema); + assertNull(binder.getDerSchemaTO("testDer").getAnyTypeClass()); + } + + @Test + void invalidDer() { + DerSchemaTO blankExpression = new DerSchemaTO(); + blankExpression.setKey("testDer"); + assertThrows(SyncopeClientException.class, () -> binder.create(blankExpression)); + + DerSchemaTO invalidExpression = new DerSchemaTO(); + invalidExpression.setKey("testDer"); + invalidExpression.setExpression("surname +"); + assertThrows(SyncopeClientException.class, () -> binder.create(invalidExpression)); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/SecurityQuestionDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/SecurityQuestionDataBinderTest.java new file mode 100644 index 00000000000..a8d8cb06c83 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/SecurityQuestionDataBinderTest.java @@ -0,0 +1,62 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.apache.syncope.common.lib.to.SecurityQuestionTO; +import org.apache.syncope.core.persistence.api.dao.SecurityQuestionDAO; +import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion; +import org.apache.syncope.core.provisioning.api.data.SecurityQuestionDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class SecurityQuestionDataBinderTest extends AbstractDataBinderTest { + + @Autowired + private SecurityQuestionDataBinder binder; + + @Autowired + private SecurityQuestionDAO securityQuestionDAO; + + @Test + void get() { + SecurityQuestionTO securityQuestion = binder.getSecurityQuestionTO( + securityQuestionDAO.findById("887028ea-66fc-41e7-b397-620d7ea6dfbb").orElseThrow()); + assertEquals("887028ea-66fc-41e7-b397-620d7ea6dfbb", securityQuestion.getKey()); + assertEquals("What's your mother's maiden name?", securityQuestion.getContent()); + } + + @Test + void createUpdate() { + SecurityQuestionTO securityQuestionTO = new SecurityQuestionTO(); + securityQuestionTO.setContent("What is your favourite colour?"); + SecurityQuestion securityQuestion = securityQuestionDAO.save(binder.create(securityQuestionTO)); + assertNotNull(securityQuestion.getKey()); + + SecurityQuestionTO created = binder.getSecurityQuestionTO(securityQuestion); + assertEquals("What is your favourite colour?", created.getContent()); + + created.setContent("What is your favourite animal?"); + binder.update(securityQuestion, created); + + assertEquals("What is your favourite animal?", binder.getSecurityQuestionTO(securityQuestion).getContent()); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderTest.java new file mode 100644 index 00000000000..fa70e82282e --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderTest.java @@ -0,0 +1,314 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.apache.syncope.common.lib.Attr; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.to.ExecTO; +import org.apache.syncope.common.lib.to.PropagationTaskTO; +import org.apache.syncope.common.lib.to.PullTaskTO; +import org.apache.syncope.common.lib.to.PushTaskTO; +import org.apache.syncope.common.lib.to.SchedTaskTO; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.JobType; +import org.apache.syncope.common.lib.types.MatchingRule; +import org.apache.syncope.common.lib.types.PullMode; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.common.lib.types.TaskType; +import org.apache.syncope.common.lib.types.ThreadPoolSettings; +import org.apache.syncope.common.lib.types.UnmatchingRule; +import org.apache.syncope.core.persistence.api.dao.TaskDAO; +import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; +import org.apache.syncope.core.persistence.api.entity.task.PullTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.api.entity.task.TaskUtils; +import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory; +import org.apache.syncope.core.provisioning.api.data.TaskDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class TaskDataBinderTest extends AbstractDataBinderTest { + + private static final String SCHED_TASK_KEY = "e95555d2-1b09-42c8-b25b-f4c4ec597979"; + + private static final String PULL_TASK_KEY = "c41b9b71-9bfa-4f90-89f2-84787def4c5c"; + + private static final String PROPAGATION_TASK_KEY = "1e697572-b896-484c-ae7f-0c8f63fcbc6c"; + + @Autowired + private TaskDataBinder binder; + + @Autowired + private TaskDAO taskDAO; + + @Autowired + private TaskUtilsFactory taskUtilsFactory; + + @Test + void getSchedTaskTO() { + SchedTask task = taskDAO.findById(TaskType.SCHEDULED, SCHED_TASK_KEY).orElseThrow(); + + SchedTaskTO taskTO = binder.getTaskTO(task, taskUtilsFactory.getInstance(task), true); + assertEquals(SCHED_TASK_KEY, taskTO.getKey()); + assertEquals("SampleJob Task", taskTO.getName()); + assertTrue(taskTO.isActive()); + assertEquals("0 0 0 1 * ?", taskTO.getCronExpression()); + assertEquals("TestSampleJobDelegate", taskTO.getJobDelegate()); + } + + @Test + void getPullTaskTO() { + PullTask task = (PullTask) taskDAO.findById(TaskType.PULL, PULL_TASK_KEY).orElseThrow(); + + PullTaskTO taskTO = binder.getTaskTO(task, taskUtilsFactory.getInstance(task), true); + assertEquals(PULL_TASK_KEY, taskTO.getKey()); + assertEquals("CSV (update matching; assign unmatching)", taskTO.getName()); + assertEquals("resource-csv", taskTO.getResource()); + assertTrue(taskTO.isPerformCreate()); + assertTrue(taskTO.isPerformUpdate()); + assertTrue(taskTO.isPerformDelete()); + assertEquals(MatchingRule.UPDATE, taskTO.getMatchingRule()); + assertEquals(UnmatchingRule.ASSIGN, taskTO.getUnmatchingRule()); + assertEquals(SyncopeConstants.ROOT_REALM, taskTO.getDestinationRealm()); + assertTrue(taskTO.getTemplates().containsKey(AnyTypeKind.USER.name())); + } + + @Test + void getPropagationTaskTO() { + PropagationTask task = + taskDAO.findById(TaskType.PROPAGATION, PROPAGATION_TASK_KEY).orElseThrow(); + + PropagationTaskTO taskTO = binder.getTaskTO(task, taskUtilsFactory.getInstance(task), true); + assertEquals(PROPAGATION_TASK_KEY, taskTO.getKey()); + assertEquals(ResourceOperation.UPDATE, taskTO.getOperation()); + assertEquals("__ACCOUNT__", taskTO.getObjectClassName()); + assertEquals("ws-target-resource-2", taskTO.getResource()); + assertEquals(AnyTypeKind.USER, taskTO.getAnyTypeKind()); + assertEquals("1417acbe-cbf6-4277-9372-e75e04f97000", taskTO.getEntityKey()); + assertEquals(1, taskTO.getExecutions().size()); + + ExecTO execTO = binder.getExecTO(task.getExecs().getFirst()); + assertEquals(task.getKey(), execTO.getRefKey()); + assertEquals(JobType.TASK, execTO.getJobType()); + assertEquals("SUCCESS", execTO.getStatus()); + assertNotNull(execTO.getRefDesc()); + assertTrue(execTO.getRefDesc().contains(PROPAGATION_TASK_KEY)); + + PropagationTaskTO withoutDetails = binder.getTaskTO(task, taskUtilsFactory.getInstance(task), false); + assertTrue(withoutDetails.getExecutions().isEmpty()); + + assertTrue(binder.buildRefDesc(task).contains(PROPAGATION_TASK_KEY)); + } + + @Test + void createSchedTask() { + SchedTaskTO taskTO = new SchedTaskTO(); + taskTO.setName("New sched task"); + taskTO.setDescription("description"); + taskTO.setCronExpression("0 0 1 * * ?"); + taskTO.setActive(true); + taskTO.setJobDelegate("TestSampleJobDelegate"); + + TaskUtils taskUtils = taskUtilsFactory.getInstance(TaskType.SCHEDULED); + SchedTask task = binder.createSchedTask(taskTO, taskUtils); + task = taskDAO.save(task); + entityManager.flush(); + + assertNotNull(task.getKey()); + + SchedTaskTO actual = binder.getTaskTO(task, taskUtils, true); + assertEquals("New sched task", actual.getName()); + assertEquals("description", actual.getDescription()); + assertEquals("0 0 1 * * ?", actual.getCronExpression()); + assertTrue(actual.isActive()); + assertEquals("TestSampleJobDelegate", actual.getJobDelegate()); + } + + @Test + void createPullTask() { + ThreadPoolSettings concurrentSettings = new ThreadPoolSettings(); + concurrentSettings.setPoolSize(5); + + PullTaskTO taskTO = new PullTaskTO(); + taskTO.setName("New pull task"); + taskTO.setDescription("pull description"); + taskTO.setCronExpression("0 0 2 * * ?"); + taskTO.setActive(true); + taskTO.setResource("resource-csv"); + taskTO.setDestinationRealm("/even/two"); + taskTO.setPullMode(PullMode.FULL_RECONCILIATION); + taskTO.setReconFilterBuilder("not-existing"); + taskTO.setMatchingRule(MatchingRule.UPDATE); + taskTO.setUnmatchingRule(UnmatchingRule.ASSIGN); + taskTO.setPerformCreate(true); + taskTO.setPerformUpdate(true); + taskTO.setPerformDelete(false); + taskTO.setSyncStatus(true); + taskTO.setRemediation(true); + taskTO.setConcurrentSettings(concurrentSettings); + taskTO.getActions().add("LDAPMembershipPullActions"); + taskTO.getActions().add("not-existing"); + + UserTO template = new UserTO(); + template.setUsername("'templatedUser'"); + template.getPlainAttrs().add(new Attr.Builder("firstname").value("'Mario'").build()); + template.getPlainAttrs().add(new Attr.Builder("surname").value("'Rossi'").build()); + taskTO.getTemplates().put(AnyTypeKind.USER.name(), template); + + TaskUtils taskUtils = taskUtilsFactory.getInstance(TaskType.PULL); + PullTask task = (PullTask) binder.createSchedTask(taskTO, taskUtils); + task = taskDAO.save(task); + entityManager.flush(); + + PullTaskTO actual = binder.getTaskTO(task, taskUtils, true); + assertEquals("New pull task", actual.getName()); + assertEquals("pull description", actual.getDescription()); + assertEquals("0 0 2 * * ?", actual.getCronExpression()); + assertEquals("resource-csv", actual.getResource()); + assertEquals("/even/two", actual.getDestinationRealm()); + assertEquals(PullMode.FULL_RECONCILIATION, actual.getPullMode()); + assertNull(actual.getReconFilterBuilder()); + assertEquals(MatchingRule.UPDATE, actual.getMatchingRule()); + assertEquals(UnmatchingRule.ASSIGN, actual.getUnmatchingRule()); + assertTrue(actual.isPerformCreate()); + assertTrue(actual.isPerformUpdate()); + assertFalse(actual.isPerformDelete()); + assertTrue(actual.isSyncStatus()); + assertTrue(actual.isRemediation()); + assertEquals(List.of("LDAPMembershipPullActions"), actual.getActions()); + assertEquals(5, actual.getConcurrentSettings().getPoolSize()); + assertTrue(actual.getTemplates().containsKey(AnyTypeKind.USER.name())); + } + + @Test + void createPushTask() { + PushTaskTO taskTO = new PushTaskTO(); + taskTO.setName("New push task"); + taskTO.setActive(true); + taskTO.setResource("ws-target-resource-1"); + taskTO.setSourceRealm("/even"); + taskTO.setMatchingRule(MatchingRule.LINK); + taskTO.setUnmatchingRule(UnmatchingRule.IGNORE); + taskTO.setPerformCreate(true); + taskTO.setPerformUpdate(true); + taskTO.setPerformDelete(true); + taskTO.getFilters().put(AnyTypeKind.USER.name(), "username==\"rossini\""); + taskTO.getFilters().put("not-existing", "username==\"rossini\""); + + TaskUtils taskUtils = taskUtilsFactory.getInstance(TaskType.PUSH); + SchedTask task = binder.createSchedTask(taskTO, taskUtils); + task = taskDAO.save(task); + entityManager.flush(); + + PushTaskTO actual = binder.getTaskTO(task, taskUtils, true); + assertEquals("New push task", actual.getName()); + assertEquals("ws-target-resource-1", actual.getResource()); + assertEquals("/even", actual.getSourceRealm()); + assertEquals(MatchingRule.LINK, actual.getMatchingRule()); + assertEquals(UnmatchingRule.IGNORE, actual.getUnmatchingRule()); + assertTrue(actual.getFilters().containsKey(AnyTypeKind.USER.name())); + assertFalse(actual.getFilters().containsKey("not-existing")); + } + + @Test + void updateSchedTask() { + SchedTask task = taskDAO.findById(TaskType.SCHEDULED, SCHED_TASK_KEY).orElseThrow(); + TaskUtils taskUtils = taskUtilsFactory.getInstance(task); + + SchedTaskTO taskTO = binder.getTaskTO(task, taskUtils, true); + taskTO.setName("Updated task"); + taskTO.setDescription("updated description"); + taskTO.setCronExpression("0 0 3 * * ?"); + taskTO.setActive(false); + + binder.updateSchedTask(task, taskTO, taskUtils); + entityManager.flush(); + + SchedTaskTO actual = binder.getTaskTO(task, taskUtils, true); + assertEquals("Updated task", actual.getName()); + assertEquals("updated description", actual.getDescription()); + assertEquals("0 0 3 * * ?", actual.getCronExpression()); + assertFalse(actual.isActive()); + + SchedTaskTO blankName = binder.getTaskTO(task, taskUtils, false); + blankName.setName(" "); + assertThrows(SyncopeClientException.class, () -> binder.updateSchedTask(task, blankName, taskUtils)); + + assertThrows(IllegalArgumentException.class, + () -> binder.updateSchedTask(task, new PullTaskTO(), taskUtils)); + assertThrows(IllegalArgumentException.class, + () -> binder.createSchedTask(new SchedTaskTO(), taskUtilsFactory.getInstance(TaskType.PULL))); + } + + @Test + void updatePullTask() { + PullTask task = (PullTask) taskDAO.findById(TaskType.PULL, PULL_TASK_KEY).orElseThrow(); + TaskUtils taskUtils = taskUtilsFactory.getInstance(task); + + PullTaskTO taskTO = binder.getTaskTO(task, taskUtils, true); + taskTO.setName("Updated pull task"); + taskTO.setDestinationRealm("/even/two"); + taskTO.setMatchingRule(MatchingRule.LINK); + taskTO.setUnmatchingRule(UnmatchingRule.IGNORE); + taskTO.setPerformCreate(false); + taskTO.setPullMode(PullMode.INCREMENTAL); + taskTO.getActions().clear(); + taskTO.getActions().add("LDAPMembershipPullActions"); + taskTO.getTemplates().clear(); + + binder.updateSchedTask(task, taskTO, taskUtils); + entityManager.flush(); + + PullTaskTO actual = binder.getTaskTO(task, taskUtils, true); + assertEquals("Updated pull task", actual.getName()); + assertEquals("/even/two", actual.getDestinationRealm()); + assertEquals(MatchingRule.LINK, actual.getMatchingRule()); + assertEquals(UnmatchingRule.IGNORE, actual.getUnmatchingRule()); + assertFalse(actual.isPerformCreate()); + assertEquals(PullMode.INCREMENTAL, actual.getPullMode()); + assertEquals(List.of("LDAPMembershipPullActions"), actual.getActions()); + assertTrue(actual.getTemplates().isEmpty()); + } + + @Test + void invalidPullTaskTemplate() { + PullTaskTO taskTO = new PullTaskTO(); + taskTO.setName("Invalid pull task"); + taskTO.setResource("resource-csv"); + taskTO.setDestinationRealm("/even"); + taskTO.setPullMode(PullMode.INCREMENTAL); + + UserTO template = new UserTO(); + template.setUsername("not a valid (("); + taskTO.getTemplates().put(AnyTypeKind.USER.name(), template); + + assertThrows(SyncopeClientException.class, () -> binder.createSchedTask( + taskTO, taskUtilsFactory.getInstance(TaskType.PULL))); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderTest.java index ed0c60fef8a..944f697bb8b 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderTest.java @@ -19,6 +19,8 @@ package org.apache.syncope.core.provisioning.java.data; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -29,18 +31,30 @@ import org.apache.syncope.common.lib.Attr; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.request.AttrPatch; +import org.apache.syncope.common.lib.request.BooleanReplacePatchItem; +import org.apache.syncope.common.lib.request.LinkedAccountUR; import org.apache.syncope.common.lib.request.MembershipUR; +import org.apache.syncope.common.lib.request.PasswordPatch; import org.apache.syncope.common.lib.request.RelationshipUR; +import org.apache.syncope.common.lib.request.StringPatchItem; +import org.apache.syncope.common.lib.request.StringReplacePatchItem; +import org.apache.syncope.common.lib.request.UserCR; import org.apache.syncope.common.lib.request.UserUR; +import org.apache.syncope.common.lib.to.LinkedAccountTO; +import org.apache.syncope.common.lib.to.MembershipTO; +import org.apache.syncope.common.lib.to.RelationshipTO; import org.apache.syncope.common.lib.to.RelationshipTypeTO; import org.apache.syncope.common.lib.to.TypeExtensionTO; import org.apache.syncope.common.lib.types.AnyEntitlement; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.common.lib.types.PatchOperation; import org.apache.syncope.core.persistence.api.EncryptorManager; import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.RelationshipType; import org.apache.syncope.core.persistence.api.entity.user.UMembership; import org.apache.syncope.core.persistence.api.entity.user.URelationship; @@ -60,7 +74,21 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class UserDataBinderTest extends AbstractTest { +class UserDataBinderTest extends AbstractTest { + + private static final String ROSSINI_KEY = "1417acbe-cbf6-4277-9372-e75e04f97000"; + + private static final String VERDI_KEY = "74cd8ece-715a-44a4-a736-e17b46c4e7e6"; + + private static final String PRINTER_KEY = "8559d14d-58c2-46eb-a2d4-a7d35161e8f8"; + + private static final String CITIZEN_KEY = "29f96485-729e-4d31-88a1-6fc60e4677f3"; + + private static final String ART_DIRECTOR_KEY = "ece66293-8f31-4a84-8e8d-23da36e70846"; + + private static final String ADDITIONAL_KEY = "034740a9-fa10-453b-af37-dc7897e98fb1"; + + private static final String SECURITY_QUESTION_KEY = "887028ea-66fc-41e7-b397-620d7ea6dfbb"; @BeforeAll public static void setAuthContext() { @@ -94,11 +122,287 @@ public static void unsetAuthContext() { @Autowired private RelationshipTypeDAO relationshipTypeDAO; + @Autowired + private PlainSchemaDAO plainSchemaDAO; + @Autowired private EncryptorManager encryptorManager; + @Autowired + private EntityFactory entityFactory; + + @Test + void createFull() { + UserCR userCR = new UserCR.Builder("/even", "fullUser"). + password("Password123!"). + storePassword(true). + securityQuestion(SECURITY_QUESTION_KEY). + securityAnswer("secret answer"). + mustChangePassword(true). + uManager(VERDI_KEY). + auxClasses("other", "csv", "not-existing"). + plainAttr(new Attr.Builder("fullname").value("Full User").build()). + plainAttr(new Attr.Builder("userId").value("full.user@example.com").build()). + plainAttr(new Attr.Builder("surname").value(AnyTypeKind.USER.name()).build()). + plainAttr(new Attr.Builder("firstname").value("Full").build()). + plainAttr(new Attr.Builder("email").value("full.user@example.com").build()). + plainAttr(new Attr.Builder("cool").value("true").build()). + plainAttr(new Attr.Builder("gender").value("M").build()). + plainAttr(new Attr.Builder("aLong").value("42").build()). + plainAttr(new Attr.Builder("loginDate").values(List.of("2020-01-01", "2021-02-02")).build()). + plainAttr(new Attr.Builder("uselessReadonly").value("ignored").build()). + plainAttr(new Attr.Builder("not-existing").value("ignored").build()). + resource("ws-target-resource-1"). + resource("not-existing"). + relationship(new RelationshipTO.Builder("neighborhood"). + otherEnd(PRINTER_KEY). + build()). + membership(new MembershipTO.Builder(ADDITIONAL_KEY). + plainAttr(new Attr.Builder("obscure").value("membership secret").build()). + build()). + membership(new MembershipTO.Builder(ART_DIRECTOR_KEY).build()). + role("Other"). + role("not-existing"). + build(); + LinkedAccountTO linkedAccount = new LinkedAccountTO.Builder("ws-target-resource-1", "full-user"). + username("fullUser"). + password("linkedPassword"). + suspended(true). + build(); + linkedAccount.getPlainAttrs().add(new Attr.Builder("email").value("linked@example.com").build()); + userCR.getLinkedAccounts().add(linkedAccount); + + User user = entityFactory.newEntity(User.class); + binder.create(user, userCR); + user = userDAO.save(user); + entityManager.flush(); + + User actual = userDAO.findById(user.getKey()).orElseThrow(); + assertEquals("fullUser", actual.getUsername()); + assertTrue(actual.isMustChangePassword()); + assertEquals("/even", actual.getRealm().getFullPath()); + assertEquals(VERDI_KEY, actual.getuManager().getKey()); + assertNull(actual.getgManager()); + assertEquals(SECURITY_QUESTION_KEY, actual.getSecurityQuestion().getKey()); + assertTrue(encryptorManager.getInstance().verify( + "Password123!", actual.getCipherAlgorithm(), actual.getPassword())); + + assertTrue(actual.getRoles().stream().anyMatch(role -> "Other".equals(role.getKey()))); + + assertEquals(2, actual.getAuxClasses().size()); + assertTrue(actual.getAuxClasses().stream().anyMatch(auxClass -> "other".equals(auxClass.getKey()))); + assertTrue(actual.getAuxClasses().stream().anyMatch(auxClass -> "csv".equals(auxClass.getKey()))); + + assertEquals("Full", actual.getPlainAttr("firstname").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals("Full User", actual.getPlainAttr("fullname").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals(2, actual.getPlainAttr("loginDate").orElseThrow().getValuesAsStrings().size()); + assertTrue(actual.getPlainAttr("uselessReadonly").isEmpty()); + assertTrue(actual.getPlainAttr("not-existing").isEmpty()); + + assertEquals(1, actual.getResources().size()); + assertEquals("ws-target-resource-1", actual.getResources().getFirst().getKey()); + + URelationship relationship = actual.getRelationship( + relationshipTypeDAO.findById("neighborhood").orElseThrow(), PRINTER_KEY).orElseThrow(); + assertNotNull(relationship); + + UMembership additional = actual.getMembership(ADDITIONAL_KEY).orElseThrow(); + assertTrue(encryptorManager.getInstance().verify( + "membership secret", + plainSchemaDAO.findById("obscure").orElseThrow().getCipherAlgorithm(), + actual.getPlainAttr("obscure", additional).orElseThrow().getValuesAsStrings().getFirst())); + assertNotNull(actual.getMembership(ART_DIRECTOR_KEY).orElseThrow()); + + assertEquals(1, actual.getLinkedAccounts().size()); + assertEquals("fullUser", actual.getLinkedAccounts().getFirst().getUsername()); + assertTrue(actual.getLinkedAccounts().getFirst().isSuspended()); + } + + @Test + void updateFull() { + UserUR userUR = new UserUR.Builder(ROSSINI_KEY). + username(new StringReplacePatchItem.Builder().value("rossini2").build()). + password(new PasswordPatch.Builder(). + value("NewPassword123!"). + onSyncope(true). + resource("ws-target-resource-1"). + build()). + securityQuestion(new StringReplacePatchItem.Builder().value(SECURITY_QUESTION_KEY).build()). + securityAnswer(new StringReplacePatchItem.Builder().value("new answer").build()). + mustChangePassword(new BooleanReplacePatchItem.Builder().value(true).build()). + uManager(new StringReplacePatchItem.Builder().value(VERDI_KEY).build()). + realm(new StringReplacePatchItem.Builder().value("/even").build()). + auxClass(new StringPatchItem.Builder().value("csv").operation(PatchOperation.ADD_REPLACE).build()). + auxClass(new StringPatchItem.Builder().value("other").operation(PatchOperation.DELETE).build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("firstname").value("Gioacchino2").build()). + build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("aLong").value("42").build()). + build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("cool").value("true").build()). + build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("gender").value("F").build()). + build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("loginDate").build()). + operation(PatchOperation.DELETE). + build()). + plainAttr(new AttrPatch.Builder(new Attr.Builder("not-existing").value("ignored").build()). + build()). + resource(new StringPatchItem.Builder(). + value("ws-target-resource-1"). + operation(PatchOperation.ADD_REPLACE). + build()). + resource(new StringPatchItem.Builder(). + value("not-existing"). + operation(PatchOperation.ADD_REPLACE). + build()). + relationship(new RelationshipUR.Builder("neighborhood"). + otherEnd(PRINTER_KEY). + build()). + membership(new MembershipUR.Builder(ART_DIRECTOR_KEY).build()). + membership(new MembershipUR.Builder("37d15e4c-cdc1-460b-a591-8505c8133806"). + operation(PatchOperation.DELETE). + build()). + membership(new MembershipUR.Builder("f779c0d4-633b-4be5-8f57-32eb478a3ca5"). + operation(PatchOperation.DELETE). + build()). + role(new StringPatchItem.Builder(). + value("User reviewer"). + operation(PatchOperation.ADD_REPLACE). + build()). + role(new StringPatchItem.Builder(). + value("Other"). + operation(PatchOperation.DELETE). + build()). + build(); + LinkedAccountTO updatedLinkedAccount = new LinkedAccountTO.Builder("ws-target-resource-1", "rossini2"). + username("rossini2"). + password("linkedPassword"). + suspended(false). + build(); + updatedLinkedAccount.getPlainAttrs(). + add(new Attr.Builder("email").value("linked.rossini@example.com").build()); + userUR.getLinkedAccounts().add(new LinkedAccountUR.Builder(). + linkedAccountTO(updatedLinkedAccount). + build()); + + binder.update(userDAO.findById(ROSSINI_KEY).orElseThrow(), userUR); + entityManager.flush(); + + User actual = userDAO.findById(ROSSINI_KEY).orElseThrow(); + assertEquals("rossini2", actual.getUsername()); + assertEquals("/even", actual.getRealm().getFullPath()); + assertTrue(actual.isMustChangePassword()); + assertEquals(VERDI_KEY, actual.getuManager().getKey()); + assertNull(actual.getgManager()); + assertEquals(SECURITY_QUESTION_KEY, actual.getSecurityQuestion().getKey()); + assertTrue(encryptorManager.getInstance().verify( + "NewPassword123!", actual.getCipherAlgorithm(), actual.getPassword())); + + assertTrue(actual.getRoles().stream().anyMatch(role -> "User reviewer".equals(role.getKey()))); + assertFalse(actual.getRoles().stream().anyMatch(role -> "Other".equals(role.getKey()))); + + assertTrue(actual.getAuxClasses().stream().anyMatch(auxClass -> "csv".equals(auxClass.getKey()))); + assertFalse(actual.getAuxClasses().stream().anyMatch(auxClass -> "other".equals(auxClass.getKey()))); + + assertEquals("Gioacchino2", actual.getPlainAttr("firstname").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals("42", actual.getPlainAttr("aLong").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals("true", actual.getPlainAttr("cool").orElseThrow().getValuesAsStrings().getFirst()); + assertEquals("F", actual.getPlainAttr("gender").orElseThrow().getValuesAsStrings().getFirst()); + assertTrue(actual.getPlainAttr("loginDate").isEmpty()); + + assertEquals(2, actual.getResources().size()); + assertTrue(actual.getResources().stream(). + anyMatch(resource -> "ws-target-resource-1".equals(resource.getKey()))); + + assertNotNull(actual.getRelationship( + relationshipTypeDAO.findById("neighborhood").orElseThrow(), PRINTER_KEY).orElseThrow()); + assertNotNull(actual.getMembership(ART_DIRECTOR_KEY).orElseThrow()); + assertTrue(actual.getMembership("37d15e4c-cdc1-460b-a591-8505c8133806").isEmpty()); + assertTrue(actual.getMembership("f779c0d4-633b-4be5-8f57-32eb478a3ca5").isEmpty()); + + assertEquals(1, actual.getLinkedAccounts().size()); + assertEquals("rossini2", actual.getLinkedAccounts().getFirst().getUsername()); + assertFalse(actual.getLinkedAccounts().getFirst().isSuspended()); + } + + @Test + void updateManagersAndLinkedAccounts() { + UserUR add = new UserUR.Builder(ROSSINI_KEY). + uManager(new StringReplacePatchItem.Builder().value(VERDI_KEY).build()). + build(); + add.getLinkedAccounts().add(new LinkedAccountUR.Builder(). + linkedAccountTO(new LinkedAccountTO.Builder("ws-target-resource-1", "rossini-linked"). + username("rossini-linked"). + build()). + build()); + + binder.update(userDAO.findById(ROSSINI_KEY).orElseThrow(), add); + entityManager.flush(); + + User updated = userDAO.findById(ROSSINI_KEY).orElseThrow(); + assertEquals(VERDI_KEY, updated.getuManager().getKey()); + assertNull(updated.getgManager()); + assertEquals(1, updated.getLinkedAccounts().size()); + + UserUR clearUserManager = new UserUR.Builder(ROSSINI_KEY). + uManager(new StringReplacePatchItem.Builder().value(null).build()). + build(); + binder.update(userDAO.findById(ROSSINI_KEY).orElseThrow(), clearUserManager); + entityManager.flush(); + + User withoutManager = userDAO.findById(ROSSINI_KEY).orElseThrow(); + assertNull(withoutManager.getuManager()); + assertNull(withoutManager.getgManager()); + + UserUR addGroupManager = new UserUR.Builder(ROSSINI_KEY). + gManager(new StringReplacePatchItem.Builder().value(CITIZEN_KEY).build()). + build(); + binder.update(userDAO.findById(ROSSINI_KEY).orElseThrow(), addGroupManager); + entityManager.flush(); + + User groupManaged = userDAO.findById(ROSSINI_KEY).orElseThrow(); + assertNull(groupManaged.getuManager()); + assertEquals(CITIZEN_KEY, groupManaged.getgManager().getKey()); + + UserUR remove = new UserUR.Builder(ROSSINI_KEY). + gManager(new StringReplacePatchItem.Builder().value(null).build()). + build(); + remove.getLinkedAccounts().add(new LinkedAccountUR.Builder(). + operation(PatchOperation.DELETE). + linkedAccountTO(new LinkedAccountTO.Builder("ws-target-resource-1", "rossini-linked"). + username("rossini-linked"). + build()). + build()); + + binder.update(userDAO.findById(ROSSINI_KEY).orElseThrow(), remove); + entityManager.flush(); + + User cleared = userDAO.findById(ROSSINI_KEY).orElseThrow(); + assertNull(cleared.getuManager()); + assertNull(cleared.getgManager()); + assertTrue(cleared.getLinkedAccounts().isEmpty()); + } + + @Test + void updatePasswordNotOnSyncope() { + UserUR userUR = new UserUR.Builder(ROSSINI_KEY). + password(new PasswordPatch.Builder(). + value("NotStoredPassword123!"). + onSyncope(false). + resources("ws-target-resource-1", "ws-target-resource-2"). + build()). + build(); + + binder.update(userDAO.findById(ROSSINI_KEY).orElseThrow(), userUR); + entityManager.flush(); + + User actual = userDAO.findById(ROSSINI_KEY).orElseThrow(); + assertTrue(encryptorManager.getInstance().verify( + "password", actual.getCipherAlgorithm(), actual.getPassword())); + } + @Test - public void membershipWithAttrNotAllowed() { + void membershipWithAttrNotAllowed() { UserUR userUR = new UserUR.Builder("1417acbe-cbf6-4277-9372-e75e04f97000").build(); // add 'obscure' to user (no membership): works because 'obscure' is from 'other', default class for USER @@ -116,7 +420,7 @@ public void membershipWithAttrNotAllowed() { } @Test - public void membershipWithAttr() { + void membershipWithAttr() { UserUR userUR = new UserUR.Builder("1417acbe-cbf6-4277-9372-e75e04f97000").build(); // add 'obscure' (no membership): works because 'obscure' is from 'other', default class for USER @@ -141,7 +445,7 @@ public void membershipWithAttr() { } @Test - public void relationshipWithAttr() { + void relationshipWithAttr() { // first add type extension to neighborhood RelationshipType relType = relationshipTypeDAO.findById("neighborhood").orElseThrow(); RelationshipTypeTO relTypeTO = relationshipTypeDataBinder.getRelationshipTypeTO(relType); diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/WAConfigDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/WAConfigDataBinderTest.java new file mode 100644 index 00000000000..6cce14ea04a --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/WAConfigDataBinderTest.java @@ -0,0 +1,58 @@ +/* + * 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.core.provisioning.java.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.apache.syncope.common.lib.Attr; +import org.apache.syncope.core.persistence.api.dao.WAConfigDAO; +import org.apache.syncope.core.persistence.api.entity.am.WAConfigEntry; +import org.apache.syncope.core.provisioning.api.data.WAConfigDataBinder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class WAConfigDataBinderTest extends AbstractDataBinderTest { + + private static final String KEY = "test.wa.config"; + + @Autowired + private WAConfigDataBinder binder; + + @Autowired + private WAConfigDAO waConfigDAO; + + @Test + void setGet() { + WAConfigEntry created = binder.set( + new Attr.Builder(KEY).value("first").value("second").build()); + waConfigDAO.save(created); + entityManager.flush(); + + Attr actual = binder.get(waConfigDAO.findById(KEY).orElseThrow()); + assertEquals(KEY, actual.getSchema()); + assertEquals(List.of("first", "second"), actual.getValues()); + + WAConfigEntry updated = binder.set(new Attr.Builder(KEY).value("third").build()); + waConfigDAO.save(updated); + entityManager.flush(); + + assertEquals(List.of("third"), binder.get(waConfigDAO.findById(KEY).orElseThrow()).getValues()); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/wa/WAClientAppDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/wa/WAClientAppDataBinderTest.java new file mode 100644 index 00000000000..b99a43f53f8 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/wa/WAClientAppDataBinderTest.java @@ -0,0 +1,200 @@ +/* + * 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.core.provisioning.java.data.wa; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.apache.syncope.common.lib.policy.DefaultAccessPolicyConf; +import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf; +import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf; +import org.apache.syncope.common.lib.policy.DefaultTicketExpirationPolicyConf; +import org.apache.syncope.common.lib.to.CASSPClientAppTO; +import org.apache.syncope.common.lib.wa.WAClientApp; +import org.apache.syncope.core.persistence.api.dao.CASSPClientAppDAO; +import org.apache.syncope.core.persistence.api.dao.PolicyDAO; +import org.apache.syncope.core.persistence.api.dao.RealmDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.am.CASSPClientApp; +import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy; +import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; +import org.apache.syncope.core.provisioning.api.data.ClientAppDataBinder; +import org.apache.syncope.core.provisioning.api.data.wa.WAClientAppDataBinder; +import org.apache.syncope.core.provisioning.java.data.AbstractDataBinderTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class WAClientAppDataBinderTest extends AbstractDataBinderTest { + + private static final String TWO_KEY = "0679e069-7355-4b20-bd11-a5a0a5453c7c"; + + @Autowired + private WAClientAppDataBinder binder; + + @Autowired + private ClientAppDataBinder clientAppDataBinder; + + @Autowired + private CASSPClientAppDAO casspClientAppDAO; + + @Autowired + private RealmDAO realmDAO; + + @Autowired + private PolicyDAO policyDAO; + + @Autowired + private EntityFactory entityFactory; + + protected AuthPolicy buildAuthPolicy(final String... authModules) { + AuthPolicy authPolicy = entityFactory.newEntity(AuthPolicy.class); + authPolicy.setName("wa auth policy"); + DefaultAuthPolicyConf conf = new DefaultAuthPolicyConf(); + conf.getAuthModules().addAll(List.of(authModules)); + authPolicy.setConf(conf); + return policyDAO.save(authPolicy); + } + + protected AccessPolicy buildAccessPolicy() { + AccessPolicy accessPolicy = entityFactory.newEntity(AccessPolicy.class); + accessPolicy.setName("wa access policy"); + DefaultAccessPolicyConf conf = new DefaultAccessPolicyConf(); + conf.setEnabled(true); + conf.setSsoEnabled(true); + accessPolicy.setConf(conf); + return policyDAO.save(accessPolicy); + } + + protected AttrReleasePolicy buildAttrReleasePolicy() { + AttrReleasePolicy attrReleasePolicy = entityFactory.newEntity(AttrReleasePolicy.class); + attrReleasePolicy.setName("wa attr release policy"); + DefaultAttrReleasePolicyConf conf = new DefaultAttrReleasePolicyConf(); + conf.getAllowedAttrs().add("cn"); + attrReleasePolicy.setConf(conf); + return policyDAO.save(attrReleasePolicy); + } + + protected TicketExpirationPolicy buildTicketExpirationPolicy() { + TicketExpirationPolicy ticketExpirationPolicy = entityFactory.newEntity(TicketExpirationPolicy.class); + ticketExpirationPolicy.setName("wa ticket expiration policy"); + DefaultTicketExpirationPolicyConf conf = new DefaultTicketExpirationPolicyConf(); + DefaultTicketExpirationPolicyConf.TGTConf tgtConf = new DefaultTicketExpirationPolicyConf.TGTConf(); + tgtConf.setMaxTimeToLiveInSeconds(600); + conf.setTgtConf(tgtConf); + ticketExpirationPolicy.setConf(conf); + return policyDAO.save(ticketExpirationPolicy); + } + + protected CASSPClientAppTO casspTO(final String name, final long clientAppId) { + CASSPClientAppTO clientAppTO = new CASSPClientAppTO(); + clientAppTO.setName(name); + clientAppTO.setClientAppId(clientAppId); + clientAppTO.setServiceId("https://" + name + ".example.com"); + clientAppTO.setRealm("/even/two"); + return clientAppTO; + } + + @Test + void explicitPolicies() { + AuthPolicy authPolicy = buildAuthPolicy("DefaultLDAPAuthModule", "not-existing"); + AccessPolicy accessPolicy = buildAccessPolicy(); + AttrReleasePolicy attrReleasePolicy = buildAttrReleasePolicy(); + TicketExpirationPolicy ticketExpirationPolicy = buildTicketExpirationPolicy(); + + CASSPClientAppTO clientAppTO = casspTO("explicit", 1L); + clientAppTO.setAuthPolicy(authPolicy.getKey()); + clientAppTO.setAccessPolicy(accessPolicy.getKey()); + clientAppTO.setAttrReleasePolicy(attrReleasePolicy.getKey()); + clientAppTO.setTicketExpirationPolicy(ticketExpirationPolicy.getKey()); + CASSPClientApp clientApp = casspClientAppDAO.save(clientAppDataBinder.create(clientAppTO)); + entityManager.flush(); + + WAClientApp waClientApp = binder.getWAClientApp(clientApp); + assertNotNull(waClientApp.getClientAppTO()); + assertEquals(clientApp.getKey(), waClientApp.getClientAppTO().getKey()); + + assertNotNull(waClientApp.getAuthPolicy()); + assertEquals(authPolicy.getKey(), waClientApp.getAuthPolicy().getKey()); + assertEquals(1, waClientApp.getAuthModules().size()); + assertEquals("DefaultLDAPAuthModule", waClientApp.getAuthModules().getFirst().getKey()); + + assertNotNull(waClientApp.getAccessPolicy()); + assertEquals(accessPolicy.getKey(), waClientApp.getAccessPolicy().getKey()); + assertNotNull(waClientApp.getAttrReleasePolicy()); + assertEquals(attrReleasePolicy.getKey(), waClientApp.getAttrReleasePolicy().getKey()); + assertNotNull(waClientApp.getTicketExpirationPolicy()); + assertEquals(ticketExpirationPolicy.getKey(), waClientApp.getTicketExpirationPolicy().getKey()); + } + + @Test + void realmPolicies() { + AuthPolicy authPolicy = buildAuthPolicy("DefaultJDBCAuthModule"); + AccessPolicy accessPolicy = buildAccessPolicy(); + AttrReleasePolicy attrReleasePolicy = buildAttrReleasePolicy(); + TicketExpirationPolicy ticketExpirationPolicy = buildTicketExpirationPolicy(); + + Realm realm = realmDAO.findById(TWO_KEY).orElseThrow(); + realm.setAuthPolicy(authPolicy); + realm.setAccessPolicy(accessPolicy); + realm.setAttrReleasePolicy(attrReleasePolicy); + realm.setTicketExpirationPolicy(ticketExpirationPolicy); + realmDAO.save(realm); + entityManager.flush(); + + CASSPClientApp clientApp = casspClientAppDAO.save( + clientAppDataBinder.create(casspTO("from-realm", 2L))); + entityManager.flush(); + + WAClientApp waClientApp = binder.getWAClientApp(clientApp); + assertNotNull(waClientApp.getAuthPolicy()); + assertEquals(authPolicy.getKey(), waClientApp.getAuthPolicy().getKey()); + assertEquals(1, waClientApp.getAuthModules().size()); + assertEquals("DefaultJDBCAuthModule", waClientApp.getAuthModules().getFirst().getKey()); + + assertNotNull(waClientApp.getAccessPolicy()); + assertEquals(accessPolicy.getKey(), waClientApp.getAccessPolicy().getKey()); + assertNotNull(waClientApp.getAttrReleasePolicy()); + assertEquals(attrReleasePolicy.getKey(), waClientApp.getAttrReleasePolicy().getKey()); + assertNotNull(waClientApp.getTicketExpirationPolicy()); + assertEquals(ticketExpirationPolicy.getKey(), waClientApp.getTicketExpirationPolicy().getKey()); + } + + @Test + void noPolicies() { + CASSPClientAppTO clientAppTO = casspTO("no-policies", 3L); + clientAppTO.setRealm(null); + CASSPClientApp clientApp = casspClientAppDAO.save(clientAppDataBinder.create(clientAppTO)); + entityManager.flush(); + + WAClientApp waClientApp = binder.getWAClientApp(clientApp); + assertNotNull(waClientApp.getClientAppTO()); + assertEquals(clientApp.getKey(), waClientApp.getClientAppTO().getKey()); + assertNull(waClientApp.getAuthPolicy()); + assertTrue(waClientApp.getAuthModules().isEmpty()); + assertNull(waClientApp.getAccessPolicy()); + assertNull(waClientApp.getAttrReleasePolicy()); + assertNull(waClientApp.getTicketExpirationPolicy()); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/job/JobStatusUpdaterTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/job/JobStatusUpdaterTest.java index 807ad300c71..e1a96e18aa0 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/job/JobStatusUpdaterTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/job/JobStatusUpdaterTest.java @@ -30,13 +30,13 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class JobStatusUpdaterTest extends AbstractTest { +class JobStatusUpdaterTest extends AbstractTest { @Autowired private JobStatusDAO jobStatusDAO; @Test - public void verifyUpdate() { + void verifyUpdate() { String jobName = "job-" + SecureRandomUtils.generateRandomNumber(); JobStatusUpdater jobStatusUpdater = new JobStatusUpdater(jobStatusDAO); diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/job/SyncopeTaskSchedulerTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/job/SyncopeTaskSchedulerTest.java index cd7f057f790..c51378ba93a 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/job/SyncopeTaskSchedulerTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/job/SyncopeTaskSchedulerTest.java @@ -42,7 +42,7 @@ import org.springframework.transaction.annotation.Transactional; @Transactional -public class SyncopeTaskSchedulerTest extends AbstractTest { +class SyncopeTaskSchedulerTest extends AbstractTest { private static final Mutable VALUE = new MutableObject<>(0); @@ -63,7 +63,7 @@ protected void execute(final JobExecutionContext context) throws JobExecutionExc private SyncopeTaskScheduler scheduler; @Test - public void schedule() { + void schedule() { JobExecutionContext context = new JobExecutionContext( AuthContextUtils.getDomain(), TestJob.class.getSimpleName() + "_" + SecureRandomUtils.generateRandomUUID(), diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManagerTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManagerTest.java index 3e456e6e7a9..db61e5d50b1 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManagerTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManagerTest.java @@ -75,7 +75,7 @@ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) -public class DefaultNotificationManagerTest { +class DefaultNotificationManagerTest { private static final String DELETE_SUCCESS = OpEvent.toString( OpEvent.CategoryType.LOGIC, "UserLogic", null, "delete", OpEvent.Outcome.SUCCESS); diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActionsTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActionsTest.java index 11577fc0038..ecb58adfc99 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActionsTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActionsTest.java @@ -35,6 +35,7 @@ import org.apache.syncope.common.lib.request.UserUR; import org.apache.syncope.common.lib.to.ProvisioningReport; import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.apache.syncope.common.lib.types.ConnConfPropSchema; import org.apache.syncope.common.lib.types.ConnConfProperty; @@ -52,7 +53,7 @@ import org.mockito.Mock; import org.springframework.test.util.ReflectionTestUtils; -public class DBPasswordPullActionsTest extends AbstractTest { +class DBPasswordPullActionsTest extends AbstractTest { @Mock private SyncDelta syncDelta; @@ -90,7 +91,7 @@ public class DBPasswordPullActionsTest extends AbstractTest { private ConnConfProperty connConfProperty; @BeforeEach - public void initTest() { + void initTest() { userTO = new UserTO(); encodedPassword = "s3cureP4ssw0rd"; cipher = CipherAlgorithm.SHA512; @@ -110,10 +111,10 @@ public void initTest() { } @Test - public void beforeProvision() throws JobExecutionException { + void beforeProvision() throws JobExecutionException { String digest = "SHA256"; String password = "t3stPassw0rd"; - userCR = new UserCR.Builder(SyncopeConstants.ROOT_REALM, "user").password(password).build(); + userCR = new UserCR.Builder(SyncopeConstants.ROOT_REALM, AnyTypeKind.USER.name()).password(password).build(); connConfProperty.getValues().clear(); connConfProperty.getValues().add(digest); @@ -129,7 +130,7 @@ public void beforeProvision() throws JobExecutionException { } @Test - public void beforeUpdate() throws JobExecutionException { + void beforeUpdate() throws JobExecutionException { userUR = new UserUR.Builder(null). password(new PasswordPatch.Builder().value("an0therTestP4ss").build()). build(); @@ -144,7 +145,7 @@ public void beforeUpdate() throws JobExecutionException { } @Test - public void after(final @Mock User user) throws JobExecutionException { + void after(final @Mock User user) throws JobExecutionException { when(userDAO.findById(user.getKey())).thenAnswer(ic -> Optional.of(user)); dBPasswordPullActions.after(profile, syncDelta, userTO, result); diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultProvisionSorterTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultProvisionSorterTest.java new file mode 100644 index 00000000000..a075b16338e --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultProvisionSorterTest.java @@ -0,0 +1,78 @@ +/* + * 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.core.provisioning.java.pushpull; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.syncope.common.lib.to.Provision; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.junit.jupiter.api.Test; + +class DefaultProvisionSorterTest { + + private static Provision provision(final String anyType) { + Provision provision = new Provision(); + provision.setAnyType(anyType); + return provision; + } + + @Test + void userBeforeGroup() { + assertTrue(new DefaultProvisionSorter(). + compare(provision(AnyTypeKind.USER.name()), provision(AnyTypeKind.GROUP.name())) < 0); + } + + @Test + void groupAfterUser() { + assertTrue(new DefaultProvisionSorter(). + compare(provision(AnyTypeKind.GROUP.name()), provision(AnyTypeKind.USER.name())) > 0); + } + + @Test + void userBeforeAnyObject() { + assertTrue(new DefaultProvisionSorter(). + compare(provision(AnyTypeKind.USER.name()), provision(AnyTypeKind.ANY_OBJECT.name())) < 0); + } + + @Test + void anyObjectAfterUser() { + assertTrue(new DefaultProvisionSorter(). + compare(provision(AnyTypeKind.ANY_OBJECT.name()), provision(AnyTypeKind.USER.name())) > 0); + } + + @Test + void groupBeforeAnyObject() { + assertTrue(new DefaultProvisionSorter(). + compare(provision(AnyTypeKind.GROUP.name()), provision(AnyTypeKind.ANY_OBJECT.name())) < 0); + } + + @Test + void anyObjectAfterGroup() { + assertTrue(new DefaultProvisionSorter(). + compare(provision(AnyTypeKind.ANY_OBJECT.name()), provision(AnyTypeKind.GROUP.name())) > 0); + } + + @Test + void sameTypeIsEqual() { + // ANY_OBJECT is the only type that falls through to ObjectUtils.compare + assertEquals(0, new DefaultProvisionSorter(). + compare(provision(AnyTypeKind.ANY_OBJECT.name()), provision(AnyTypeKind.ANY_OBJECT.name()))); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActionsTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActionsTest.java index f7138cc600c..eb36c58151c 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActionsTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActionsTest.java @@ -72,7 +72,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.test.util.ReflectionTestUtils; -public class LDAPMembershipPullActionsTest extends AbstractTest { +class LDAPMembershipPullActionsTest extends AbstractTest { @Autowired private EntityFactory entityFactory; @@ -129,7 +129,7 @@ public class LDAPMembershipPullActionsTest extends AbstractTest { private List connConfProperties; @BeforeEach - public void initTest() { + void initTest() { user = entityFactory.newEntity(User.class); ReflectionTestUtils.setField(user, "id", UUID.randomUUID().toString()); @@ -170,7 +170,7 @@ public void initTest() { } @Test - public void beforeUpdateWithGroupTOAndEmptyMemberships() throws JobExecutionException { + void beforeUpdateWithGroupTOAndEmptyMemberships() throws JobExecutionException { entity = new GroupTO(); entity.setKey(UUID.randomUUID().toString()); @@ -182,7 +182,7 @@ public void beforeUpdateWithGroupTOAndEmptyMemberships() throws JobExecutionExce } @Test - public void beforeUpdate() throws JobExecutionException { + void beforeUpdate() throws JobExecutionException { entity = new UserTO(); entity.setKey(UUID.randomUUID().toString()); @@ -195,7 +195,7 @@ public void beforeUpdate() throws JobExecutionException { } @Test - public void after() throws JobExecutionException { + void after() throws JobExecutionException { entity = new GroupTO(); entity.setKey(UUID.randomUUID().toString()); diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActionsTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActionsTest.java index a582f1f9b99..d57fe5fce7e 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActionsTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActionsTest.java @@ -51,7 +51,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; -public class LDAPPasswordPullActionsTest extends AbstractTest { +class LDAPPasswordPullActionsTest extends AbstractTest { @Mock private ProvisioningProfile profile; @@ -67,7 +67,7 @@ public class LDAPPasswordPullActionsTest extends AbstractTest { private LDAPPasswordPullActions actions; @Test - public void afterWithNoUser() throws JobExecutionException { + void afterWithNoUser() throws JobExecutionException { UserTO userTO = new UserTO(); userTO.setKey(UUID.randomUUID().toString()); when(userDAO.findById(userTO.getKey())).thenReturn(Optional.empty()); @@ -76,7 +76,7 @@ public void afterWithNoUser() throws JobExecutionException { } @Test - public void after(final @Mock User user) throws JobExecutionException { + void after(final @Mock User user) throws JobExecutionException { UserTO userTO = new UserTO(); userTO.setKey(UUID.randomUUID().toString()); when(userDAO.findById(userTO.getKey())).thenAnswer(ic -> Optional.of(user)); diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/SyncopeResultHandlerDispatcherTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/SyncopeResultHandlerDispatcherTest.java new file mode 100644 index 00000000000..ac6209fe4a2 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/SyncopeResultHandlerDispatcherTest.java @@ -0,0 +1,160 @@ +/* + * 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.core.provisioning.java.pushpull; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import org.apache.syncope.common.lib.types.ConflictResolutionAction; +import org.apache.syncope.common.lib.types.TaskType; +import org.apache.syncope.common.lib.types.ThreadPoolSettings; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.task.PushTask; +import org.apache.syncope.core.provisioning.api.job.JobExecutionException; +import org.apache.syncope.core.provisioning.api.pushpull.AnyPushResultHandler; +import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile; +import org.apache.syncope.core.provisioning.api.pushpull.PushActions; +import org.apache.syncope.core.provisioning.api.pushpull.SyncopePushExecutor; +import org.junit.jupiter.api.Test; + +class SyncopeResultHandlerDispatcherTest { + + private static final String USER = "USER"; + + private static Any mockAny(final String key) { + AnyType type = mock(AnyType.class); + when(type.getKey()).thenReturn(USER); + + Any any = mock(Any.class); + when(any.getKey()).thenReturn(key); + when(any.getType()).thenReturn(type); + + return any; + } + + private static AnyPushResultHandler blockingHandler(final CountDownLatch started, final CountDownLatch release) { + AnyPushResultHandler handler = mock(AnyPushResultHandler.class); + when(handler.handle(any(Any.class))).thenAnswer(invocation -> { + started.countDown(); + release.await(); + return true; + }); + return handler; + } + + private static PushResultHandlerDispatcher buildDispatcher( + final Integer poolSize, final int submitTimeoutSeconds, final int completionTimeoutSeconds) { + + PushTask task = mock(PushTask.class); + if (poolSize != null) { + ThreadPoolSettings concurrentSettings = new ThreadPoolSettings(); + concurrentSettings.setPoolSize(poolSize); + when(task.getConcurrentSettings()).thenReturn(concurrentSettings); + } + + ProvisioningProfile profile = new ProvisioningProfile<>( + null, TaskType.PUSH, task, ConflictResolutionAction.IGNORE, List.of(), "admin", false); + + PushResultHandlerDispatcher dispatcher = new PushResultHandlerDispatcher(); + dispatcher.setSubmitTimeoutSeconds(submitTimeoutSeconds); + dispatcher.setCompletionTimeoutSeconds(completionTimeoutSeconds); + + return dispatcher.init(profile, mock(SyncopePushExecutor.class)); + } + + @Test + void submitIsBoundedBySubmitTimeout() throws Exception { + CountDownLatch started = new CountDownLatch(1); + CountDownLatch release = new CountDownLatch(1); + + PushResultHandlerDispatcher dispatcher = buildDispatcher(1, 1, 60); + dispatcher.addHandlerSupplier(USER, () -> blockingHandler(started, release)); + + assertTrue(dispatcher.handle(mockAny("first"))); + assertTrue(started.await(5, TimeUnit.SECONDS)); + + long start = System.nanoTime(); + assertFalse(dispatcher.handle(mockAny("second"))); + long elapsedMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + assertTrue(elapsedMillis >= 1000, "expected to wait for the submit timeout, waited " + elapsedMillis + "ms"); + + release.countDown(); + + JobExecutionException e = assertThrows(JobExecutionException.class, dispatcher::shutdown); + assertTrue(e.getMessage().contains("not submitted"), e.getMessage()); + } + + @Test + void shutdownIsBoundedByCompletionTimeout() throws Exception { + CountDownLatch started = new CountDownLatch(1); + CountDownLatch release = new CountDownLatch(1); + + PushResultHandlerDispatcher dispatcher = buildDispatcher(1, 60, 1); + dispatcher.addHandlerSupplier(USER, () -> blockingHandler(started, release)); + + assertTrue(dispatcher.handle(mockAny("first"))); + assertTrue(started.await(5, TimeUnit.SECONDS)); + + long start = System.nanoTime(); + JobExecutionException e = assertThrows(JobExecutionException.class, dispatcher::shutdown); + long elapsedMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); + assertTrue(elapsedMillis >= 1000, "expected to wait for the completion timeout, waited " + + elapsedMillis + "ms"); + assertTrue(e.getMessage().contains("still running"), e.getMessage()); + + release.countDown(); + } + + @Test + void shutdownWaitsForCompletion() throws Exception { + PushResultHandlerDispatcher dispatcher = buildDispatcher(2, 5, 5); + dispatcher.addHandlerSupplier(USER, () -> { + AnyPushResultHandler handler = mock(AnyPushResultHandler.class); + when(handler.handle(any(Any.class))).thenReturn(true); + return handler; + }); + + assertTrue(dispatcher.handle(mockAny("first"))); + assertTrue(dispatcher.handle(mockAny("second"))); + + dispatcher.shutdown(); + } + + @Test + void nonConcurrentHandlingIsUnchanged() throws Exception { + AnyPushResultHandler handler = mock(AnyPushResultHandler.class); + when(handler.handle(any(Any.class))).thenReturn(true); + + PushResultHandlerDispatcher dispatcher = buildDispatcher(null, 1, 1); + dispatcher.addHandlerSupplier(USER, () -> handler); + + assertTrue(dispatcher.handle(mockAny("first"))); + + // no executor involved: shutdown() has nothing to wait for + dispatcher.shutdown(); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegateTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegateTest.java index 87e91237972..27b41834c14 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegateTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegateTest.java @@ -47,7 +47,7 @@ import tools.jackson.dataformat.csv.CsvSchema; @Transactional -public class StreamPullJobDelegateTest extends AbstractTest { +class StreamPullJobDelegateTest extends AbstractTest { @Autowired private AnyTypeDAO anyTypeDAO; @@ -67,7 +67,7 @@ private SyncopeStreamPullExecutor executor() { } @Test - public void pull() throws JobExecutionException, IOException { + void pull() throws JobExecutionException, IOException { List columns = List.of( "username", "email", diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegateTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegateTest.java index 8ac9ebfc57f..29c79260656 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegateTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegateTest.java @@ -31,6 +31,7 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.ProvisioningReport; import org.apache.syncope.common.lib.to.PushTaskTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.MatchingRule; import org.apache.syncope.common.lib.types.UnmatchingRule; import org.apache.syncope.core.persistence.api.ApplicationContextProvider; @@ -47,7 +48,7 @@ import tools.jackson.dataformat.csv.CsvSchema; @Transactional -public class StreamPushJobDelegateTest extends AbstractTest { +class StreamPushJobDelegateTest extends AbstractTest { @Autowired private AnyTypeDAO anyTypeDAO; @@ -67,7 +68,7 @@ private SyncopeStreamPushExecutor executor() { } @Test - public void push() throws IOException { + void push() throws IOException { PipedInputStream in = new PipedInputStream(); PipedOutputStream os = new PipedOutputStream(in); @@ -90,7 +91,7 @@ public void push() throws IOException { connector, List.of(), pushTask, - "user"); + AnyTypeKind.USER.name()); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtilsTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtilsTest.java new file mode 100644 index 00000000000..cda41bfc971 --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtilsTest.java @@ -0,0 +1,111 @@ +/* + * 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.core.provisioning.java.utils; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Set; +import org.apache.syncope.common.lib.Attr; +import org.apache.syncope.common.lib.to.ConnObject; +import org.identityconnectors.common.security.GuardedString; +import org.identityconnectors.common.security.SecurityUtil; +import org.identityconnectors.framework.common.objects.Attribute; +import org.identityconnectors.framework.common.objects.AttributeBuilder; +import org.identityconnectors.framework.common.objects.SyncToken; +import org.junit.jupiter.api.Test; + +class ConnObjectUtilsTest { + + @Test + void toSyncTokenNull() { + assertNull(ConnObjectUtils.toSyncToken(null)); + } + + @Test + void toStringNull() { + assertNull(ConnObjectUtils.toString(null)); + } + + @Test + void syncTokenRoundTrip() { + SyncToken token = new SyncToken("tokenValue"); + String serialized = ConnObjectUtils.toString(token); + SyncToken deserialized = ConnObjectUtils.toSyncToken(serialized); + assertEquals(token.getValue(), deserialized.getValue()); + } + + @Test + void getPasswordString() { + assertEquals("plain", ConnObjectUtils.getPassword("plain")); + } + + @Test + void getPasswordOther() { + assertEquals("42", ConnObjectUtils.getPassword(Integer.valueOf(42))); + } + + @Test + void getPasswordGuardedString() { + GuardedString guarded = new GuardedString("secret".toCharArray()); + assertEquals("secret", ConnObjectUtils.getPassword(guarded)); + } + + @Test + void getConnObjectTONull() { + ConnObject connObject = ConnObjectUtils.getConnObjectTO(null, null); + assertTrue(connObject != null); + assertTrue(connObject.getAttrs().isEmpty()); + } + + @Test + void getConnObjectTOWithAttributes() { + Attribute attr = AttributeBuilder.build("attrName", "attrValue"); + Set attrs = Set.of(attr); + + ConnObject connObject = ConnObjectUtils.getConnObjectTO("fiqlValue", attrs); + + assertEquals("fiqlValue", connObject.getFiql()); + assertEquals(1, connObject.getAttrs().size()); + Attr attrTO = connObject.getAttrs().stream().findFirst().orElseThrow(); + assertEquals("attrName", attrTO.getSchema()); + assertEquals(1, attrTO.getValues().size()); + assertEquals("attrValue", attrTO.getValues().getFirst()); + } + + @Test + void getConnObjectTOWithPasswordAttribute() { + GuardedString guarded = new GuardedString("secret".toCharArray()); + Attribute attr = AttributeBuilder.build("password", guarded); + Set attrs = Set.of(attr); + + ConnObject connObject = ConnObjectUtils.getConnObjectTO("fiqlValue", attrs); + + Attr attrTO = connObject.getAttrs().stream().findFirst().orElseThrow(); + assertEquals("secret", attrTO.getValues().getFirst()); + } + + @Test + void getConnObjectTOWithSecurityUtil() { + // Verify SecurityUtil.decrypt round-trip used by getPassword + GuardedString guarded = new GuardedString("password1".toCharArray()); + assertEquals("password1", SecurityUtil.decrypt(guarded)); + } +} diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/utils/MappingUtilsTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/utils/MappingUtilsTest.java new file mode 100644 index 00000000000..4daccf0ff0a --- /dev/null +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/utils/MappingUtilsTest.java @@ -0,0 +1,76 @@ +/* + * 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.core.provisioning.java.utils; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import java.util.stream.Stream; +import org.apache.syncope.common.lib.to.Item; +import org.apache.syncope.common.lib.types.MappingPurpose; +import org.junit.jupiter.api.Test; + +class MappingUtilsTest { + + private static Item item(final MappingPurpose purpose) { + Item item = new Item(); + item.setPurpose(purpose); + return item; + } + + @Test + void getPropagationItems() { + Stream items = Stream.of( + item(MappingPurpose.PROPAGATION), + item(MappingPurpose.BOTH), + item(MappingPurpose.PULL), + item(MappingPurpose.NONE)); + + List result = MappingUtils.getPropagationItems(items).toList(); + + assertEquals(2, result.size()); + assertEquals(MappingPurpose.PROPAGATION, result.getFirst().getPurpose()); + assertEquals(MappingPurpose.BOTH, result.get(1).getPurpose()); + } + + @Test + void getInboundItems() { + Stream items = Stream.of( + item(MappingPurpose.PULL), + item(MappingPurpose.BOTH), + item(MappingPurpose.PROPAGATION), + item(MappingPurpose.NONE)); + + List result = MappingUtils.getInboundItems(items).toList(); + + assertEquals(2, result.size()); + assertEquals(MappingPurpose.PULL, result.getFirst().getPurpose()); + assertEquals(MappingPurpose.BOTH, result.get(1).getPurpose()); + } + + @Test + void getPropagationItemsEmpty() { + assertEquals(0, MappingUtils.getPropagationItems(Stream.of()).count()); + } + + @Test + void getInboundItemsEmpty() { + assertEquals(0, MappingUtils.getInboundItems(Stream.of()).count()); + } +} diff --git a/core/self-keymaster-starter/pom.xml b/core/self-keymaster-starter/pom.xml index f288ac26807..32e5f0554f4 100644 --- a/core/self-keymaster-starter/pom.xml +++ b/core/self-keymaster-starter/pom.xml @@ -55,6 +55,19 @@ under the License. ${project.version} javadoc + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.apache.logging.log4j + log4j-to-slf4j + + + diff --git a/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/internal/InternalConfParamHelperTest.java b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/internal/InternalConfParamHelperTest.java new file mode 100644 index 00000000000..718a9f4fafd --- /dev/null +++ b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/internal/InternalConfParamHelperTest.java @@ -0,0 +1,144 @@ +/* + * 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.core.keymaster.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.keymaster.ConfParamDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.keymaster.ConfParam; +import org.junit.jupiter.api.Test; +import tools.jackson.databind.node.JsonNodeFactory; +import tools.jackson.databind.node.ObjectNode; + +class InternalConfParamHelperTest { + + private final ConfParamDAO confParamDAO = mock(ConfParamDAO.class); + + private final EntityFactory entityFactory = mock(EntityFactory.class); + + private final InternalConfParamHelper helper = new InternalConfParamHelper(confParamDAO, entityFactory); + + @Test + void list() { + ConfParam param1 = mock(ConfParam.class); + when(param1.getKey()).thenReturn("key1"); + ObjectNode node1 = new ObjectNode(JsonNodeFactory.instance); + node1.put("value", 1); + when(param1.getValue()).thenReturn(node1); + + ConfParam param2 = mock(ConfParam.class); + when(param2.getKey()).thenReturn("key2"); + ObjectNode node2 = new ObjectNode(JsonNodeFactory.instance); + node2.put("value", 2); + when(param2.getValue()).thenReturn(node2); + + doReturn(List.of(param1, param2)).when(confParamDAO).findAll(); + + Map result = helper.list(); + + assertEquals(2, result.size()); + assertEquals(Map.of("value", 1), result.get("key1")); + assertEquals(Map.of("value", 2), result.get("key2")); + } + + @Test + void listWithInvalidValueSkips() { + ConfParam param = mock(ConfParam.class); + when(param.getKey()).thenReturn("key"); + when(param.getValue()).thenReturn(null); + + doReturn(List.of(param)).when(confParamDAO).findAll(); + + Map result = helper.list(); + + assertEquals(1, result.size()); + assertNull(result.get("key")); + } + + @Test + void getWhenNotFoundReturnsNull() { + doReturn(Optional.empty()).when(confParamDAO).findById("key"); + + assertNull(helper.get("key")); + } + + @Test + void getReturnsValue() { + ObjectNode value = new ObjectNode(JsonNodeFactory.instance); + value.put("key", "value"); + ConfParam param = mock(ConfParam.class); + when(param.getValue()).thenReturn(value); + doReturn(Optional.of(param)).when(confParamDAO).findById("key"); + + assertSame(value, helper.get("key")); + } + + @Test + void setCreatesNewWhenNotFound() { + ObjectNode value = new ObjectNode(JsonNodeFactory.instance); + value.put("key", "value"); + ConfParam param = mock(ConfParam.class); + when(entityFactory.newEntity(eq(ConfParam.class))).thenReturn(param); + doReturn(Optional.empty()).when(confParamDAO).findById("key"); + + helper.set("key", value); + + verify(param).setKey("key"); + verify(param).setValue(value); + verify(confParamDAO).save(param); + } + + @Test + void setUpdatesExistingWhenFound() { + ObjectNode value = new ObjectNode(JsonNodeFactory.instance); + value.put("key", "value"); + ConfParam param = mock(ConfParam.class); + doReturn(Optional.of(param)).when(confParamDAO).findById("key"); + + helper.set("key", value); + + verify(param).setValue(value); + verify(confParamDAO).save(param); + } + + @Test + void setNullThrows() { + assertThrows(NotFoundException.class, () -> helper.set("key", null)); + } + + @Test + void remove() { + helper.remove("key"); + + verify(confParamDAO).deleteById("key"); + } +} diff --git a/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalConfParamOpsTest.java b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalConfParamOpsTest.java new file mode 100644 index 00000000000..38326590da1 --- /dev/null +++ b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalConfParamOpsTest.java @@ -0,0 +1,93 @@ +/* + * 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.core.keymaster.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; +import org.apache.syncope.common.keymaster.client.api.ConfParamOps; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; +import org.junit.jupiter.api.Test; +import tools.jackson.databind.JsonNode; + +class SelfKeymasterInternalConfParamOpsTest { + + private final InternalConfParamHelper helper = mock(InternalConfParamHelper.class); + + private final ConfParamOps ops = new SelfKeymasterInternalConfParamOps(helper); + + @Test + void list() { + Map expected = Map.of("key", "value"); + when(helper.list()).thenReturn(expected); + + assertSame(expected, ops.list("domain")); + verify(helper).list(); + } + + @Test + void getWhenNotFoundReturnsDefault() { + when(helper.get("key")).thenReturn(null); + + assertEquals("default", ops.get("domain", "key", "default", String.class)); + verify(helper).get("key"); + } + + @Test + void getDeserializesValue() { + when(helper.get("key")).thenReturn(SyncopeJsonMapper.shared().valueToTree("value")); + + assertEquals("value", ops.get("domain", "key", "default", String.class)); + verify(helper).get("key"); + } + + @Test + void setNullRemoves() { + ops.set("domain", "key", null); + + verify(helper).remove("key"); + } + + @Test + void setSerializesAndCallsHelper() { + Map value = Map.of("a", 1); + JsonNode expectedTree = SyncopeJsonMapper.shared().valueToTree(value); + + ops.set("domain", "key", value); + + verify(helper).set("key", expectedTree); + } + + @Test + void remove() { + ops.remove("domain", "key"); + + verify(helper).remove("key"); + } + + @Test + void opsIsInstanceOfConfParamOps() { + assertTrue(ops instanceof ConfParamOps); + } +} diff --git a/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalDomainOpsTest.java b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalDomainOpsTest.java new file mode 100644 index 00000000000..1951096bb3b --- /dev/null +++ b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalDomainOpsTest.java @@ -0,0 +1,162 @@ +/* + * 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.core.keymaster.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import org.apache.syncope.common.keymaster.client.api.KeymasterProperties; +import org.apache.syncope.common.keymaster.client.api.model.Domain; +import org.apache.syncope.common.lib.types.EntitlementsHolder; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.logic.DomainLogic; +import org.apache.syncope.core.persistence.api.ApplicationContextProvider; +import org.apache.syncope.core.spring.security.SecurityProperties; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.StaticApplicationContext; +import org.springframework.security.core.context.SecurityContextHolder; + +class SelfKeymasterInternalDomainOpsTest { + + private final DomainLogic logic = mock(DomainLogic.class); + + private final KeymasterProperties props = new KeymasterProperties(); + + private SelfKeymasterInternalDomainOps ops; + + private ConfigurableApplicationContext previousContext; + + private DefaultListableBeanFactory previousBeanFactory; + + @BeforeEach + void setUp() { + previousContext = ApplicationContextProvider.getApplicationContext(); + previousBeanFactory = ApplicationContextProvider.getBeanFactory(); + + SecurityContextHolder.clearContext(); + + EntitlementsHolder.getInstance().addAll(IdRepoEntitlement.values()); + + props.setUsername("keymaster"); + props.setPassword("password"); + + SecurityProperties securityProperties = new SecurityProperties(); + securityProperties.setAdminUser("admin"); + + StaticApplicationContext context = new StaticApplicationContext(); + context.getBeanFactory().registerSingleton("securityProperties", securityProperties); + context.refresh(); + + ApplicationContextProvider.setApplicationContext(context); + ApplicationContextProvider.setBeanFactory((DefaultListableBeanFactory) context.getBeanFactory()); + + ops = new SelfKeymasterInternalDomainOps(logic, props); + } + + @AfterEach + void tearDown() { + SecurityContextHolder.clearContext(); + ApplicationContextProvider.setApplicationContext(previousContext); + ApplicationContextProvider.setBeanFactory(previousBeanFactory); + } + + @Test + void list() { + List expected = List.of(mock(Domain.class)); + when(logic.list()).thenReturn(expected); + + assertEquals(expected, ops.list()); + verify(logic).list(); + } + + @Test + void read() { + String key = "domain"; + Domain expected = mock(Domain.class); + when(logic.read(key)).thenReturn(expected); + + assertSame(expected, ops.read(key)); + verify(logic).read(key); + } + + @Test + void create() { + Domain domain = mock(Domain.class); + when(domain.getKey()).thenReturn("domain"); + + ops.create(domain); + + verify(logic).create(domain); + } + + @Test + void deployed() { + String key = "domain"; + + ops.deployed(key); + + verify(logic).deployed(key); + } + + @Test + void changeAdminPassword() { + String key = "domain"; + String password = "new-password"; + + ops.changeAdminPassword(key, password, null); + + verify(logic).changeAdminPassword(key, password, null); + } + + @Test + void setAdminMfaSecret() { + String key = "domain"; + String secret = "secret"; + + ops.setAdminMfaSecret(key, secret); + + verify(logic).setAdminMfaSecret(key, secret); + } + + @Test + void adjustPoolSize() { + String key = "domain"; + + ops.adjustPoolSize(key, 20, 5); + + verify(logic).adjustPoolSize(props.getUsername(), 20, 5); + } + + @Test + void delete() { + String key = "domain"; + + ops.delete(key); + + verify(logic).delete(key); + } +} diff --git a/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalServiceOpsTest.java b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalServiceOpsTest.java new file mode 100644 index 00000000000..09762f956b5 --- /dev/null +++ b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/internal/SelfKeymasterInternalServiceOpsTest.java @@ -0,0 +1,157 @@ +/* + * 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.core.keymaster.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import org.apache.syncope.common.keymaster.client.api.KeymasterException; +import org.apache.syncope.common.keymaster.client.api.KeymasterProperties; +import org.apache.syncope.common.keymaster.client.api.model.NetworkService; +import org.apache.syncope.common.lib.types.EntitlementsHolder; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.logic.NetworkServiceLogic; +import org.apache.syncope.core.persistence.api.ApplicationContextProvider; +import org.apache.syncope.core.spring.security.SecurityProperties; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.StaticApplicationContext; +import org.springframework.security.core.context.SecurityContextHolder; + +class SelfKeymasterInternalServiceOpsTest { + + private final NetworkServiceLogic logic = mock(NetworkServiceLogic.class); + + private final KeymasterProperties props = new KeymasterProperties(); + + private SelfKeymasterInternalServiceOps ops; + + private ConfigurableApplicationContext previousContext; + + private DefaultListableBeanFactory previousBeanFactory; + + @BeforeEach + void setUp() { + previousContext = ApplicationContextProvider.getApplicationContext(); + previousBeanFactory = ApplicationContextProvider.getBeanFactory(); + + SecurityContextHolder.clearContext(); + + EntitlementsHolder.getInstance().addAll(IdRepoEntitlement.values()); + + props.setUsername("keymaster"); + props.setPassword("password"); + + SecurityProperties securityProperties = new SecurityProperties(); + securityProperties.setAdminUser("admin"); + + StaticApplicationContext context = new StaticApplicationContext(); + context.getBeanFactory().registerSingleton("securityProperties", securityProperties); + context.refresh(); + + ApplicationContextProvider.setApplicationContext(context); + ApplicationContextProvider.setBeanFactory((DefaultListableBeanFactory) context.getBeanFactory()); + + ops = new SelfKeymasterInternalServiceOps(logic, props); + } + + @AfterEach + void tearDown() { + SecurityContextHolder.clearContext(); + ApplicationContextProvider.setApplicationContext(previousContext); + ApplicationContextProvider.setBeanFactory(previousBeanFactory); + } + + @Test + void listByType() { + NetworkService.Type type = NetworkService.Type.CORE; + List expected = List.of(mock(NetworkService.class)); + when(logic.list(type)).thenReturn(expected); + + assertEquals(expected, ops.list(type)); + verify(logic).list(type); + } + + @Test + void listByTypeAndDomain() { + NetworkService.Type type = NetworkService.Type.CORE; + String domain = "domain"; + List expected = List.of(mock(NetworkService.class)); + when(logic.list(type, domain)).thenReturn(expected); + + assertEquals(expected, ops.list(type, domain)); + verify(logic).list(type, domain); + } + + @Test + void getByType() { + NetworkService.Type type = NetworkService.Type.CORE; + NetworkService expected = mock(NetworkService.class); + when(logic.get(type)).thenReturn(expected); + + assertSame(expected, ops.get(type)); + verify(logic).get(type); + } + + @Test + void getByTypeAndDomain() { + NetworkService.Type type = NetworkService.Type.CORE; + String domain = "domain"; + NetworkService expected = mock(NetworkService.class); + when(logic.get(type, domain)).thenReturn(expected); + + assertSame(expected, ops.get(type, domain)); + verify(logic).get(type, domain); + } + + @Test + void getWrapsException() { + NetworkService.Type type = NetworkService.Type.CORE; + when(logic.get(type)).thenThrow(new RuntimeException("boom")); + + assertThrows(KeymasterException.class, () -> ops.get(type)); + verify(logic).get(type); + } + + @Test + void register() { + NetworkService service = mock(NetworkService.class); + + ops.register(service); + + verify(logic).register(service); + } + + @Test + void unregister() { + NetworkService service = mock(NetworkService.class); + + ops.unregister(service); + + verify(logic).unregister(service); + } +} diff --git a/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/rest/cxf/service/ConfParamServiceImplTest.java b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/rest/cxf/service/ConfParamServiceImplTest.java new file mode 100644 index 00000000000..38b14755ea2 --- /dev/null +++ b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/rest/cxf/service/ConfParamServiceImplTest.java @@ -0,0 +1,79 @@ +/* + * 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.core.keymaster.rest.cxf.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.Response; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.Map; +import org.apache.syncope.core.logic.ConfParamLogic; +import org.junit.jupiter.api.Test; +import tools.jackson.databind.JsonNode; + +class ConfParamServiceImplTest { + + private final ConfParamLogic logic = mock(ConfParamLogic.class); + + private final ConfParamServiceImpl service = new ConfParamServiceImpl(logic); + + @Test + void list() { + Map expected = Map.of("key", "value"); + when(logic.list()).thenReturn(expected); + + assertSame(expected, service.list()); + verify(logic).list(); + } + + @Test + void get() { + JsonNode expected = mock(JsonNode.class); + when(logic.get("key")).thenReturn(expected); + + Response response = service.get("key"); + + assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + assertSame(expected, response.getEntity()); + verify(logic).get("key"); + } + + @Test + void set() { + String json = "{\"key\":\"value\"}"; + InputStream body = new ByteArrayInputStream(json.getBytes()); + + service.set("key", body); + + verify(logic).set("key", org.apache.syncope.common.lib.jackson.SyncopeJsonMapper.shared(). + readTree(json)); + } + + @Test + void remove() { + service.remove("key"); + + verify(logic).remove("key"); + } +} diff --git a/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/rest/cxf/service/DomainServiceImplTest.java b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/rest/cxf/service/DomainServiceImplTest.java new file mode 100644 index 00000000000..58858d39487 --- /dev/null +++ b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/rest/cxf/service/DomainServiceImplTest.java @@ -0,0 +1,134 @@ +/* + * 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.core.keymaster.rest.cxf.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; +import java.net.URI; +import java.util.List; +import org.apache.syncope.common.keymaster.client.api.model.Domain; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.common.rest.api.RESTHeaders; +import org.apache.syncope.core.logic.DomainLogic; +import org.junit.jupiter.api.Test; + +class DomainServiceImplTest { + + private final DomainLogic logic = mock(DomainLogic.class); + + private final UriInfo uriInfo = mock(UriInfo.class); + + private final DomainServiceImpl service = new DomainServiceImpl(logic); + + { + service.uriInfo = uriInfo; + } + + @Test + void list() { + List expected = List.of(mock(Domain.class)); + when(logic.list()).thenReturn(expected); + + assertSame(expected, service.list()); + verify(logic).list(); + } + + @Test + void read() { + String key = "domain"; + Domain expected = mock(Domain.class); + when(logic.read(key)).thenReturn(expected); + + assertSame(expected, service.read(key)); + verify(logic).read(key); + } + + @Test + void create() { + Domain domain = mock(Domain.class); + Domain created = mock(Domain.class); + when(created.getKey()).thenReturn("domain"); + when(logic.create(domain)).thenReturn(created); + + UriBuilder builder = mock(UriBuilder.class); + when(builder.path("domain")).thenReturn(builder); + when(builder.build()).thenReturn(URI.create("/keymaster/domain")); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(builder); + + Response response = service.create(domain); + + assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus()); + assertEquals("domain", response.getHeaderString(RESTHeaders.RESOURCE_KEY)); + verify(logic).create(domain); + } + + @Test + void deployed() { + String key = "domain"; + + service.deployed(key); + + verify(logic).deployed(key); + } + + @Test + void changeAdminPassword() { + String key = "domain"; + String password = "new-password"; + + service.changeAdminPassword(key, password, CipherAlgorithm.SHA512); + + verify(logic).changeAdminPassword(key, password, CipherAlgorithm.SHA512); + } + + @Test + void setAdminMfaSecret() { + String key = "domain"; + String secret = "secret"; + + service.setAdminMfaSecret(key, secret); + + verify(logic).setAdminMfaSecret(key, secret); + } + + @Test + void adjustPoolSize() { + String key = "domain"; + + service.adjustPoolSize(key, 20, 5); + + verify(logic).adjustPoolSize(key, 20, 5); + } + + @Test + void delete() { + String key = "domain"; + + service.delete(key); + + verify(logic).delete(key); + } +} diff --git a/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/rest/cxf/service/NetworkServiceServiceImplTest.java b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/rest/cxf/service/NetworkServiceServiceImplTest.java new file mode 100644 index 00000000000..e209761ba27 --- /dev/null +++ b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/rest/cxf/service/NetworkServiceServiceImplTest.java @@ -0,0 +1,96 @@ +/* + * 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.core.keymaster.rest.cxf.service; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import org.apache.syncope.common.keymaster.client.api.model.NetworkService; +import org.apache.syncope.core.logic.NetworkServiceLogic; +import org.junit.jupiter.api.Test; + +class NetworkServiceServiceImplTest { + + private final NetworkServiceLogic logic = mock(NetworkServiceLogic.class); + + private final NetworkServiceServiceImpl service = new NetworkServiceServiceImpl(logic); + + @Test + void listWithDomain() { + NetworkService.Type type = NetworkService.Type.CORE; + String domain = "domain"; + List expected = List.of(mock(NetworkService.class)); + when(logic.list(type, domain)).thenReturn(expected); + + assertSame(expected, service.list(type, domain)); + verify(logic).list(type, domain); + } + + @Test + void listWithoutDomain() { + NetworkService.Type type = NetworkService.Type.CORE; + List expected = List.of(mock(NetworkService.class)); + when(logic.list(type)).thenReturn(expected); + + assertSame(expected, service.list(type, null)); + verify(logic).list(type); + } + + @Test + void getWithDomain() { + NetworkService.Type type = NetworkService.Type.CORE; + String domain = "domain"; + NetworkService expected = mock(NetworkService.class); + when(logic.get(type, domain)).thenReturn(expected); + + assertSame(expected, service.get(type, domain)); + verify(logic).get(type, domain); + } + + @Test + void getWithoutDomain() { + NetworkService.Type type = NetworkService.Type.CORE; + NetworkService expected = mock(NetworkService.class); + when(logic.get(type)).thenReturn(expected); + + assertSame(expected, service.get(type, null)); + verify(logic).get(type); + } + + @Test + void actionRegister() { + NetworkService networkService = mock(NetworkService.class); + + this.service.action(networkService, NetworkServiceServiceImpl.Action.register); + + verify(logic).register(networkService); + } + + @Test + void actionUnregister() { + NetworkService networkService = mock(NetworkService.class); + + this.service.action(networkService, NetworkServiceServiceImpl.Action.unregister); + + verify(logic).unregister(networkService); + } +} diff --git a/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/rest/security/SelfKeymasterUsernamePasswordAuthenticationProviderTest.java b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/rest/security/SelfKeymasterUsernamePasswordAuthenticationProviderTest.java new file mode 100644 index 00000000000..aa5dfd2a962 --- /dev/null +++ b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/keymaster/rest/security/SelfKeymasterUsernamePasswordAuthenticationProviderTest.java @@ -0,0 +1,189 @@ +/* + * 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.core.keymaster.rest.security; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Set; +import javax.cache.Cache; +import org.apache.syncope.common.keymaster.client.api.DomainOps; +import org.apache.syncope.common.keymaster.client.api.KeymasterProperties; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.EntitlementsHolder; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.persistence.api.ApplicationContextProvider; +import org.apache.syncope.core.persistence.api.EncryptorManager; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.UserProvisioningManager; +import org.apache.syncope.core.spring.security.AuthDataAccessor; +import org.apache.syncope.core.spring.security.SecurityProperties; +import org.apache.syncope.core.spring.security.SyncopeAuthenticationDetails; +import org.apache.syncope.core.spring.security.SyncopeGrantedAuthority; +import org.apache.syncope.core.spring.security.throttle.ThrottlerAttempts; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.StaticApplicationContext; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; + +class SelfKeymasterUsernamePasswordAuthenticationProviderTest { + + private final DomainOps domainOps = mock(DomainOps.class); + + private final AuthDataAccessor dataAccessor = mock(AuthDataAccessor.class); + + private final UserProvisioningManager provisioningManager = mock(UserProvisioningManager.class); + + private final EncryptorManager encryptorManager = mock(EncryptorManager.class); + + @SuppressWarnings("unchecked") + private final Cache throttlerCache = mock(Cache.class); + + private SecurityProperties securityProperties; + + private KeymasterProperties keymasterProperties; + + private StaticApplicationContext context; + + private SelfKeymasterUsernamePasswordAuthenticationProvider provider; + + private ConfigurableApplicationContext previousContext; + + private DefaultListableBeanFactory previousBeanFactory; + + @BeforeEach + void setUp() { + previousContext = ApplicationContextProvider.getApplicationContext(); + previousBeanFactory = ApplicationContextProvider.getBeanFactory(); + + SecurityContextHolder.clearContext(); + + EntitlementsHolder.getInstance().addAll(IdRepoEntitlement.values()); + + securityProperties = new SecurityProperties(); + securityProperties.setAdminUser("admin"); + securityProperties.getAuthenticationThrottle().setEnabled(false); + + keymasterProperties = new KeymasterProperties(); + keymasterProperties.setUsername("keymaster"); + keymasterProperties.setPassword("keymaster-password"); + + context = new StaticApplicationContext(); + context.getBeanFactory().registerSingleton("securityProperties", securityProperties); + context.refresh(); + + ApplicationContextProvider.setApplicationContext(context); + ApplicationContextProvider.setBeanFactory((DefaultListableBeanFactory) context.getBeanFactory()); + + provider = new SelfKeymasterUsernamePasswordAuthenticationProvider( + domainOps, + dataAccessor, + provisioningManager, + securityProperties, + encryptorManager, + throttlerCache, + keymasterProperties); + } + + @AfterEach + void tearDown() { + SecurityContextHolder.clearContext(); + ApplicationContextProvider.setApplicationContext(previousContext); + ApplicationContextProvider.setBeanFactory(previousBeanFactory); + context.close(); + } + + @Test + void supportsUsernamePasswordAuthentication() { + assertTrue(provider.supports(UsernamePasswordAuthenticationToken.class)); + } + + @Test + void successfulAuthenticationAsKeymasterUser() { + SyncopeGrantedAuthority authority = new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ); + Set authorities = Set.of(authority); + UsernamePasswordAuthenticationToken authentication = + new UsernamePasswordAuthenticationToken("keymaster", "keymaster-password"); + authentication.setDetails(new SyncopeAuthenticationDetails( + SyncopeConstants.MASTER_DOMAIN, null)); + + when(dataAccessor.getAuthorities(eq("keymaster"), isNull())). + thenReturn(authorities); + + Authentication result = provider.authenticate(authentication); + + assertEquals("keymaster", result.getName()); + assertNull(result.getCredentials()); + assertSame(authentication.getDetails(), result.getDetails()); + verify(dataAccessor, never()).authenticate(any(), any()); + } + + @Test + void failedAuthenticationAsKeymasterUserWithWrongPassword() { + UsernamePasswordAuthenticationToken authentication = + new UsernamePasswordAuthenticationToken("keymaster", "wrong-password"); + authentication.setDetails(new SyncopeAuthenticationDetails( + SyncopeConstants.MASTER_DOMAIN, null)); + + BadCredentialsException ex = assertThrows( + BadCredentialsException.class, + () -> provider.authenticate(authentication)); + + assertEquals("keymaster: invalid password provided", ex.getMessage()); + verify(dataAccessor, never()).authenticate(any(), any()); + } + + @Test + void nonKeymasterUserDelegatesToSuper() { + User user = mock(User.class); + when(user.getKey()).thenReturn("user-key"); + when(user.getUsername()).thenReturn("bob"); + UsernamePasswordAuthenticationToken authentication = + new UsernamePasswordAuthenticationToken("bob", "pwd"); + authentication.setDetails(new SyncopeAuthenticationDetails("other", null)); + + when(domainOps.read("other")).thenThrow(new NotFoundException("not found")); + + BadCredentialsException ex = assertThrows( + BadCredentialsException.class, + () -> provider.authenticate(authentication)); + + assertEquals("Could not find domain other", ex.getMessage()); + assertInstanceOf(NotFoundException.class, ex.getCause()); + verify(domainOps).read("other"); + } +} diff --git a/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/logic/ConfParamLogicTest.java b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/logic/ConfParamLogicTest.java new file mode 100644 index 00000000000..407d2b9db71 --- /dev/null +++ b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/logic/ConfParamLogicTest.java @@ -0,0 +1,77 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; +import org.apache.syncope.core.keymaster.internal.InternalConfParamHelper; +import org.junit.jupiter.api.Test; +import tools.jackson.databind.JsonNode; + +class ConfParamLogicTest { + + private final InternalConfParamHelper helper = mock(InternalConfParamHelper.class); + + private final ConfParamLogic logic = new ConfParamLogic(helper); + + @Test + void list() { + Map expected = Map.of("key", "value"); + when(helper.list()).thenReturn(expected); + + assertSame(expected, logic.list()); + verify(helper).list(); + } + + @Test + void get() { + JsonNode expected = mock(JsonNode.class); + when(helper.get("key")).thenReturn(expected); + + assertSame(expected, logic.get("key")); + verify(helper).get("key"); + } + + @Test + void set() { + JsonNode value = mock(JsonNode.class); + + logic.set("key", value); + + verify(helper).set("key", value); + } + + @Test + void remove() { + logic.remove("key"); + + verify(helper).remove("key"); + } + + @Test + void resolveReferenceThrows() { + assertThrows(UnsupportedOperationException.class, + () -> logic.resolveReference(null, new Object[0])); + } +} diff --git a/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/logic/DomainLogicTest.java b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/logic/DomainLogicTest.java new file mode 100644 index 00000000000..cb2dd97d816 --- /dev/null +++ b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/logic/DomainLogicTest.java @@ -0,0 +1,275 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.keymaster.client.api.DomainWatcher; +import org.apache.syncope.common.keymaster.client.api.model.Domain; +import org.apache.syncope.common.keymaster.client.api.model.JPADomain; +import org.apache.syncope.common.keymaster.client.api.model.Neo4jDomain; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.core.persistence.api.dao.DuplicateException; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.keymaster.DomainDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.keymaster.DomainEntity; +import org.junit.jupiter.api.Test; + +class DomainLogicTest { + + private final DomainDAO domainDAO = mock(DomainDAO.class); + + private final EntityFactory entityFactory = mock(EntityFactory.class); + + private final DomainWatcher domainWatcher = mock(DomainWatcher.class); + + private final DomainLogic logic = new DomainLogic(domainDAO, entityFactory, domainWatcher); + + @Test + void list() { + DomainEntity entity = mock(DomainEntity.class); + Domain domain = mock(Domain.class); + when(entity.get()).thenReturn(domain); + doReturn(List.of(entity)).when(domainDAO).findAll(); + + List result = logic.list(); + + assertEquals(List.of(domain), result); + verify(domainDAO).findAll(); + } + + @Test + void readWhenNotFound() { + doReturn(Optional.empty()).when(domainDAO).findById("key"); + + assertThrows(NotFoundException.class, () -> logic.read("key")); + } + + @Test + void readWhenFound() { + DomainEntity entity = mock(DomainEntity.class); + Domain domain = mock(Domain.class); + when(entity.get()).thenReturn(domain); + doReturn(Optional.of(entity)).when(domainDAO).findById("key"); + + assertSame(domain, logic.read("key")); + } + + @Test + void createMasterDomainRejected() { + Domain domain = mock(Domain.class); + when(domain.getKey()).thenReturn(SyncopeConstants.MASTER_DOMAIN); + + assertThrows( + org.apache.syncope.common.keymaster.client.api.KeymasterException.class, + () -> logic.create(domain)); + } + + @Test + void createDuplicateRejected() { + JPADomain domain = new JPADomain.Builder("domain").build(); + when(domainDAO.existsById("domain")).thenReturn(true); + + assertThrows(DuplicateException.class, () -> logic.create(domain)); + } + + @Test + void createSucceeds() { + JPADomain domain = new JPADomain.Builder("domain").build(); + DomainEntity entity = mock(DomainEntity.class); + Domain created = mock(Domain.class); + + when(domainDAO.existsById("domain")).thenReturn(false); + when(entityFactory.newEntity(eq(DomainEntity.class))).thenReturn(entity); + when(domainDAO.save(entity)).thenReturn(entity); + when(entity.get()).thenReturn(created); + + Domain result = logic.create(domain); + + assertSame(created, result); + verify(entity).setKey("domain"); + verify(entity).set(domain); + verify(domainDAO).save(entity); + verify(domainWatcher).added(domain); + } + + @Test + void deployedSetsDeployed() { + String key = "domain"; + DomainEntity entity = mock(DomainEntity.class); + Domain domain = mock(Domain.class); + + doReturn(Optional.of(entity)).when(domainDAO).findById(key); + when(entity.get()).thenReturn(domain); + + logic.deployed(key); + + verify(domain).setDeployed(true); + verify(entity).set(domain); + verify(domainDAO).save(entity); + } + + @Test + void deployedWhenNotFound() { + doReturn(Optional.empty()).when(domainDAO).findById("key"); + + assertThrows(NotFoundException.class, () -> logic.deployed("key")); + } + + @Test + void changeAdminPassword() { + String key = "domain"; + String password = "new-password"; + CipherAlgorithm algorithm = CipherAlgorithm.SHA512; + + DomainEntity entity = mock(DomainEntity.class); + Domain domain = mock(Domain.class); + + doReturn(Optional.of(entity)).when(domainDAO).findById(key); + when(entity.get()).thenReturn(domain); + + logic.changeAdminPassword(key, password, algorithm); + + verify(domain).setAdminPassword(password); + verify(domain).setAdminCipherAlgorithm(algorithm); + verify(entity).set(domain); + verify(domainDAO).save(entity); + } + + @Test + void changeAdminPasswordWhenNotFound() { + doReturn(Optional.empty()).when(domainDAO).findById("key"); + + assertThrows(NotFoundException.class, + () -> logic.changeAdminPassword("key", "password", CipherAlgorithm.SHA512)); + } + + @Test + void setAdminMfaSecret() { + String key = "domain"; + String secret = "secret"; + + DomainEntity entity = mock(DomainEntity.class); + Domain domain = mock(Domain.class); + + doReturn(Optional.of(entity)).when(domainDAO).findById(key); + when(entity.get()).thenReturn(domain); + + logic.setAdminMfaSecret(key, secret); + + verify(domain).setAdminMfaSecret(secret); + verify(entity).set(domain); + verify(domainDAO).save(entity); + } + + @Test + void setAdminMfaSecretWhenNotFound() { + doReturn(Optional.empty()).when(domainDAO).findById("key"); + + assertThrows(NotFoundException.class, () -> logic.setAdminMfaSecret("key", "secret")); + } + + @Test + void adjustPoolSizeForJPADomain() { + String key = "domain"; + JPADomain domain = new JPADomain.Builder(key).build(); + DomainEntity entity = mock(DomainEntity.class); + + doReturn(Optional.of(entity)).when(domainDAO).findById(key); + when(entity.get()).thenReturn(domain); + + logic.adjustPoolSize(key, 20, 5); + + assertEquals(20, domain.getPoolMaxActive()); + assertEquals(5, domain.getPoolMinIdle()); + verify(entity).set(domain); + verify(domainDAO).save(entity); + } + + @Test + void adjustPoolSizeForNeo4jDomain() { + String key = "domain"; + Neo4jDomain domain = new Neo4jDomain.Builder(key).build(); + DomainEntity entity = mock(DomainEntity.class); + + doReturn(Optional.of(entity)).when(domainDAO).findById(key); + when(entity.get()).thenReturn(domain); + + logic.adjustPoolSize(key, 50, 5); + + assertEquals(50, domain.getMaxConnectionPoolSize()); + verify(entity).set(domain); + verify(domainDAO).save(entity); + } + + @Test + void adjustPoolSizeForUnknownDomainType() { + String key = "domain"; + Domain domain = mock(Domain.class); + DomainEntity entity = mock(DomainEntity.class); + + doReturn(Optional.of(entity)).when(domainDAO).findById(key); + when(entity.get()).thenReturn(domain); + + logic.adjustPoolSize(key, 20, 5); + + verify(entity).set(domain); + verify(domainDAO).save(entity); + } + + @Test + void adjustPoolSizeWhenNotFound() { + doReturn(Optional.empty()).when(domainDAO).findById("key"); + + assertThrows(NotFoundException.class, () -> logic.adjustPoolSize("key", 20, 5)); + } + + @Test + void delete() { + String key = "domain"; + + logic.delete(key); + + verify(domainDAO).deleteById(key); + verify(domainWatcher).removed(key); + } + + @Test + void resolveReferenceThrows() { + assertThrows(UnsupportedOperationException.class, + () -> logic.resolveReference(null, new Object[0])); + } + + @SuppressWarnings("unused") + private static void unused() { + assertNotNull(SyncopeConstants.MASTER_DOMAIN); + } +} diff --git a/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/logic/NetworkServiceLogicTest.java b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/logic/NetworkServiceLogicTest.java new file mode 100644 index 00000000000..ad064a38ae7 --- /dev/null +++ b/core/self-keymaster-starter/src/test/java/org/apache/syncope/core/logic/NetworkServiceLogicTest.java @@ -0,0 +1,185 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import org.apache.syncope.common.keymaster.client.api.model.NetworkService; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.keymaster.NetworkServiceDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.keymaster.NetworkServiceEntity; +import org.junit.jupiter.api.Test; + +class NetworkServiceLogicTest { + + private final NetworkServiceDAO serviceDAO = mock(NetworkServiceDAO.class); + + private final EntityFactory entityFactory = mock(EntityFactory.class); + + private final NetworkServiceLogic logic = new NetworkServiceLogic(serviceDAO, entityFactory); + + @Test + void listByType() { + NetworkService.Type type = NetworkService.Type.CORE; + NetworkServiceEntity entity = mock(NetworkServiceEntity.class); + when(entity.getType()).thenReturn(type); + when(entity.getAddress()).thenReturn("address"); + when(entity.getDomain()).thenReturn("domain"); + when(serviceDAO.findAll(type)).thenReturn(List.of(entity)); + + List result = logic.list(type); + + assertEquals(1, result.size()); + assertEquals(type, result.getFirst().getType()); + assertEquals("address", result.getFirst().getAddress()); + assertEquals("domain", result.getFirst().getDomain()); + } + + @Test + void listByTypeAndDomain() { + NetworkService.Type type = NetworkService.Type.CORE; + String domain = "domain"; + NetworkServiceEntity entity = mock(NetworkServiceEntity.class); + when(entity.getType()).thenReturn(type); + when(entity.getAddress()).thenReturn("address"); + when(entity.getDomain()).thenReturn(domain); + when(serviceDAO.findAll(type, domain)).thenReturn(List.of(entity)); + + List result = logic.list(type, domain); + + assertEquals(1, result.size()); + assertEquals(type, result.getFirst().getType()); + assertEquals("address", result.getFirst().getAddress()); + assertEquals(domain, result.getFirst().getDomain()); + } + + @Test + void getWhenEmpty() { + NetworkService.Type type = NetworkService.Type.CORE; + when(serviceDAO.findAll(type)).thenReturn(List.of()); + + assertThrows(NotFoundException.class, () -> logic.get(type)); + } + + @Test + void getWhenSingle() { + NetworkService.Type type = NetworkService.Type.CORE; + NetworkServiceEntity entity = mock(NetworkServiceEntity.class); + when(entity.getType()).thenReturn(type); + when(entity.getAddress()).thenReturn("address"); + when(entity.getDomain()).thenReturn("domain"); + when(serviceDAO.findAll(type)).thenReturn(List.of(entity)); + + NetworkService result = logic.get(type); + + assertEquals(type, result.getType()); + assertEquals("address", result.getAddress()); + assertEquals("domain", result.getDomain()); + } + + @Test + void getWhenMultipleReturnsOne() { + NetworkService.Type type = NetworkService.Type.CORE; + NetworkServiceEntity e1 = mock(NetworkServiceEntity.class); + when(e1.getType()).thenReturn(type); + when(e1.getAddress()).thenReturn("addr1"); + when(e1.getDomain()).thenReturn("d1"); + NetworkServiceEntity e2 = mock(NetworkServiceEntity.class); + when(e2.getType()).thenReturn(type); + when(e2.getAddress()).thenReturn("addr2"); + when(e2.getDomain()).thenReturn("d2"); + when(serviceDAO.findAll(type)).thenReturn(List.of(e1, e2)); + + NetworkService result = logic.get(type); + + assertNotNull(result); + assertEquals(type, result.getType()); + } + + @Test + void getByTypeAndDomainWhenEmpty() { + NetworkService.Type type = NetworkService.Type.CORE; + String domain = "domain"; + when(serviceDAO.findAll(type, domain)).thenReturn(List.of()); + + assertThrows(NotFoundException.class, () -> logic.get(type, domain)); + } + + @Test + void registerSkipsWhenAlreadyExists() { + NetworkService service = mock(NetworkService.class); + when(service.getType()).thenReturn(NetworkService.Type.CORE); + when(service.getAddress()).thenReturn("address"); + when(service.getDomain()).thenReturn("domain"); + + NetworkServiceEntity existing = mock(NetworkServiceEntity.class); + when(existing.getAddress()).thenReturn("address"); + when(serviceDAO.findAll(NetworkService.Type.CORE)).thenReturn(List.of(existing)); + + logic.register(service); + + verify(entityFactory, never()).newEntity(any()); + verify(serviceDAO, never()).save(any()); + } + + @Test + void registerCreatesWhenNotExists() { + NetworkService service = mock(NetworkService.class); + when(service.getType()).thenReturn(NetworkService.Type.CORE); + when(service.getAddress()).thenReturn("address"); + when(service.getDomain()).thenReturn("domain"); + + when(serviceDAO.findAll(NetworkService.Type.CORE)).thenReturn(List.of()); + + NetworkServiceEntity entity = mock(NetworkServiceEntity.class); + when(entityFactory.newEntity(eq(NetworkServiceEntity.class))).thenReturn(entity); + + logic.register(service); + + verify(entity).setType(NetworkService.Type.CORE); + verify(entity).setAddress("address"); + verify(entity).setDomain("domain"); + verify(serviceDAO).save(entity); + } + + @Test + void unregister() { + NetworkService service = mock(NetworkService.class); + + logic.unregister(service); + + verify(serviceDAO).deleteAll(service); + } + + @Test + void resolveReferenceThrows() { + assertThrows(UnsupportedOperationException.class, + () -> logic.resolveReference(null, new Object[0])); + } +} diff --git a/core/self-keymaster-starter/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/core/self-keymaster-starter/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 00000000000..8545334b3b4 --- /dev/null +++ b/core/self-keymaster-starter/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1,18 @@ +# 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. + +mock-maker-subclass diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/PasswordRuleTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/PasswordRuleTest.java index b0cd17778ff..95fd2dfe0fe 100644 --- a/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/PasswordRuleTest.java +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/PasswordRuleTest.java @@ -49,7 +49,7 @@ public static DefaultPasswordRuleConf createBaseDefaultPasswordRuleConf() { } @Test - public void concurrentPasswordRuleBuilding() { + void concurrentPasswordRuleBuilding() { String body = POJOHelper.serialize(createBaseDefaultPasswordRuleConf()); assertTimeoutPreemptively(Duration.ofSeconds(30), () -> { diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/policy/DefaultAccountRuleTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/policy/DefaultAccountRuleTest.java new file mode 100644 index 00000000000..88a1ef50e41 --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/policy/DefaultAccountRuleTest.java @@ -0,0 +1,204 @@ +/* + * 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.core.spring.policy; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.policy.AccountRuleConf; +import org.apache.syncope.common.lib.policy.DefaultAccountRuleConf; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class DefaultAccountRuleTest { + + private DefaultAccountRuleConf conf; + + private DefaultAccountRule rule; + + @BeforeEach + void setUp() { + conf = new DefaultAccountRuleConf(); + rule = new DefaultAccountRule(); + rule.setConf(conf); + } + + @Test + void setConfRequiresExpectedType() { + AccountRuleConf other = mock(AccountRuleConf.class); + + IllegalArgumentException ex = assertThrows( + IllegalArgumentException.class, + () -> rule.setConf(other)); + assertTrue(ex.getMessage().startsWith( + DefaultAccountRuleConf.class.getName() + " expected, got ")); + } + + @Test + void validUsername() { + assertDoesNotThrow(() -> rule.enforce("bob.smith")); + } + + @Test + void minLength() { + conf.setMinLength(5); + + AccountPolicyException ex = + assertThrows(AccountPolicyException.class, () -> rule.enforce("bob")); + assertEquals("Username too short", ex.getMessage()); + assertDoesNotThrow(() -> rule.enforce("bobby")); + } + + @Test + void maxLength() { + conf.setMaxLength(4); + + AccountPolicyException ex = + assertThrows(AccountPolicyException.class, () -> rule.enforce("bobby")); + assertEquals("Username too long", ex.getMessage()); + assertDoesNotThrow(() -> rule.enforce("bob")); + } + + @Test + void wordsNotPermittedAreCaseInsensitive() { + conf.getWordsNotPermitted().add("Admin"); + + AccountPolicyException ex = + assertThrows(AccountPolicyException.class, () -> rule.enforce("superadmin")); + assertEquals("Used word(s) not permitted", ex.getMessage()); + } + + @Test + void allUpperCase() { + conf.setAllUpperCase(true); + + AccountPolicyException ex = + assertThrows(AccountPolicyException.class, () -> rule.enforce("Bob")); + assertEquals("No lowercase characters permitted", ex.getMessage()); + assertDoesNotThrow(() -> rule.enforce("BOB")); + } + + @Test + void allLowerCase() { + conf.setAllLowerCase(true); + + AccountPolicyException ex = + assertThrows(AccountPolicyException.class, () -> rule.enforce("Bob")); + assertEquals("No uppercase characters permitted", ex.getMessage()); + assertDoesNotThrow(() -> rule.enforce("bob")); + } + + @Test + void defaultPattern() { + AccountPolicyException ex = + assertThrows(AccountPolicyException.class, () -> rule.enforce("bob!")); + assertEquals("Username does not match pattern", ex.getMessage()); + } + + @Test + void customPattern() { + conf.setPattern("[a-z]+"); + + assertThrows(AccountPolicyException.class, () -> rule.enforce("bob1")); + assertDoesNotThrow(() -> rule.enforce("bob")); + } + + @Test + void prefixesNotPermitted() { + conf.getPrefixesNotPermitted().add("sys"); + + AccountPolicyException ex = + assertThrows(AccountPolicyException.class, () -> rule.enforce("sysadmin")); + assertEquals("Prefix not permitted", ex.getMessage()); + assertDoesNotThrow(() -> rule.enforce("admin")); + } + + @Test + void suffixesNotPermitted() { + conf.getSuffixesNotPermitted().add("bot"); + + AccountPolicyException ex = + assertThrows(AccountPolicyException.class, () -> rule.enforce("mailbot")); + assertEquals("Suffix not permitted", ex.getMessage()); + assertDoesNotThrow(() -> rule.enforce("mail")); + } + + @Test + void enforceUserIncludesSchemaValues() { + conf.getSchemasNotPermitted().add("nickname"); + + PlainAttr nickname = mock(PlainAttr.class); + when(nickname.getValuesAsStrings()).thenReturn(List.of("BOB")); + + User user = mock(User.class); + when(user.getUsername()).thenReturn("bob"); + when(user.getPlainAttr("nickname")).thenReturn(Optional.of(nickname)); + + AccountPolicyException ex = + assertThrows(AccountPolicyException.class, () -> rule.enforce(user)); + assertEquals("Used word(s) not permitted", ex.getMessage()); + } + + @Test + void enforceUserIgnoresMissingOrEmptySchemaValues() { + conf.getSchemasNotPermitted().add("missing"); + conf.getSchemasNotPermitted().add("empty"); + + PlainAttr empty = mock(PlainAttr.class); + when(empty.getValuesAsStrings()).thenReturn(List.of()); + + User user = mock(User.class); + when(user.getUsername()).thenReturn("bob"); + when(user.getPlainAttr("missing")).thenReturn(Optional.empty()); + when(user.getPlainAttr("empty")).thenReturn(Optional.of(empty)); + + assertDoesNotThrow(() -> rule.enforce(user)); + } + + @Test + void enforceLinkedAccountWithBlankUsernameIsSkipped() { + LinkedAccount account = mock(LinkedAccount.class); + when(account.getUsername()).thenReturn(" "); + + assertDoesNotThrow(() -> rule.enforce(account)); + } + + @Test + void enforceLinkedAccountIncludesSchemaValues() { + conf.getSchemasNotPermitted().add("mail"); + + PlainAttr mail = mock(PlainAttr.class); + when(mail.getValuesAsStrings()).thenReturn(List.of("BOB")); + + LinkedAccount account = mock(LinkedAccount.class); + when(account.getUsername()).thenReturn("bob"); + when(account.getPlainAttr("mail")).thenReturn(Optional.of(mail)); + + assertThrows(AccountPolicyException.class, () -> rule.enforce(account)); + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/policy/DefaultPasswordRuleTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/policy/DefaultPasswordRuleTest.java new file mode 100644 index 00000000000..7e056278df3 --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/policy/DefaultPasswordRuleTest.java @@ -0,0 +1,216 @@ +/* + * 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.core.spring.policy; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.security.NoSuchAlgorithmException; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf; +import org.apache.syncope.common.lib.policy.PasswordRuleConf; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.core.persistence.api.Encryptor; +import org.apache.syncope.core.persistence.api.EncryptorManager; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +class DefaultPasswordRuleTest { + + private Encryptor encryptor; + + private DefaultPasswordRuleConf conf; + + private DefaultPasswordRule rule; + + @BeforeEach + void setUp() { + encryptor = mock(Encryptor.class); + EncryptorManager encryptorManager = mock(EncryptorManager.class); + when(encryptorManager.getInstance()).thenReturn(encryptor); + + conf = new DefaultPasswordRuleConf(); + conf.setUsernameAllowed(true); + + rule = new DefaultPasswordRule(); + rule.setConf(conf); + ReflectionTestUtils.setField(rule, "encryptorManager", encryptorManager); + } + + @Test + void setConfRequiresExpectedType() { + PasswordRuleConf other = mock(PasswordRuleConf.class); + + IllegalArgumentException ex = assertThrows( + IllegalArgumentException.class, + () -> rule.setConf(other)); + + assertTrue(ex.getMessage().startsWith( + DefaultPasswordRuleConf.class.getName() + " expected, got ")); + } + + @Test + void getConf() { + assertSame(conf, rule.getConf()); + } + + @Test + void nullPasswordIsIgnored() { + assertDoesNotThrow(() -> rule.enforce("bob", null)); + + User user = mock(User.class); + assertDoesNotThrow(() -> rule.enforce(user, null)); + verify(user, never()).getUsername(); + } + + @Test + void minimumLength() { + conf.setMinLength(10); + + assertThrows(PasswordPolicyException.class, () -> rule.enforce("bob", "short")); + assertDoesNotThrow(() -> rule.enforce("bob", "longenough1")); + } + + @Test + void maximumLength() { + conf.setMaxLength(5); + + assertThrows(PasswordPolicyException.class, () -> rule.enforce("bob", "toolong")); + assertDoesNotThrow(() -> rule.enforce("bob", "short")); + } + + @Test + void requiredCharacterClasses() { + conf.setAlphabetical(1); + conf.setUppercase(1); + conf.setLowercase(1); + conf.setDigit(1); + conf.setSpecial(1); + conf.getSpecialChars().add('!'); + + assertThrows(PasswordPolicyException.class, () -> rule.enforce("bob", "abcdefgh")); + assertThrows(PasswordPolicyException.class, () -> rule.enforce("bob", "ABCDEFGH1!")); + assertThrows(PasswordPolicyException.class, () -> rule.enforce("bob", "Abcdefgh!")); + assertDoesNotThrow(() -> rule.enforce("bob", "Abcdefg1!")); + } + + @Test + void illegalCharacters() { + conf.getIllegalChars().add('$'); + + assertThrows(PasswordPolicyException.class, () -> rule.enforce("bob", "abc$def")); + assertDoesNotThrow(() -> rule.enforce("bob", "abcdef")); + } + + @Test + void usernameNotAllowed() { + conf.setUsernameAllowed(false); + + PasswordPolicyException ex = + assertThrows(PasswordPolicyException.class, () -> rule.enforce("bob", "bob")); + assertTrue(ex.getMessage().contains("bob")); + assertDoesNotThrow(() -> rule.enforce("bob", "alice")); + } + + @Test + void wordsNotPermitted() { + conf.getWordsNotPermitted().add("password"); + + assertThrows(PasswordPolicyException.class, () -> rule.enforce("bob", "password")); + } + + @Test + void wordsNotPermittedAsSubstrings() { + conf.getWordsNotPermitted().add("secret"); + conf.setNotPermittedAsSubstrings(true); + + assertThrows(PasswordPolicyException.class, () -> rule.enforce("bob", "mysecretvalue")); + } + + @Test + void userSchemaValuesAreNotPermitted() { + conf.getSchemasNotPermitted().add("mail"); + + PlainAttr mail = mock(PlainAttr.class); + when(mail.getValuesAsStrings()).thenReturn(List.of("admin@example.com")); + + User user = mock(User.class); + when(user.getUsername()).thenReturn("bob"); + when(user.getPlainAttr("mail")).thenReturn(Optional.of(mail)); + + assertThrows(PasswordPolicyException.class, () -> rule.enforce(user, "admin@example.com")); + assertDoesNotThrow(() -> rule.enforce(user, "other-value")); + } + + @Test + void linkedAccountPasswordIsDecodedAndEnforced() throws Exception { + conf.setUsernameAllowed(false); + LinkedAccount account = mock(LinkedAccount.class); + when(account.getUsername()).thenReturn("bob"); + when(account.getPassword()).thenReturn("encoded"); + when(account.canDecodeSecrets()).thenReturn(true); + when(account.getCipherAlgorithm()).thenReturn(CipherAlgorithm.SHA512); + when(encryptor.decode("encoded", CipherAlgorithm.SHA512)).thenReturn("bob"); + + assertThrows(PasswordPolicyException.class, () -> rule.enforce(account)); + } + + @Test + void linkedAccountWithoutPasswordIsIgnored() { + LinkedAccount account = mock(LinkedAccount.class); + + assertDoesNotThrow(() -> rule.enforce(account)); + verify(account, never()).getUsername(); + } + + @Test + void linkedAccountWithNonDecodablePasswordIsIgnored() { + LinkedAccount account = mock(LinkedAccount.class); + when(account.getPassword()).thenReturn("encoded"); + when(account.canDecodeSecrets()).thenReturn(false); + + assertDoesNotThrow(() -> rule.enforce(account)); + verify(account, never()).getUsername(); + } + + @Test + void linkedAccountDecodingFailureIsIgnored() throws Exception { + LinkedAccount account = mock(LinkedAccount.class); + when(account.getPassword()).thenReturn("encoded"); + when(account.canDecodeSecrets()).thenReturn(true); + when(account.getCipherAlgorithm()).thenReturn(CipherAlgorithm.SHA512); + when(encryptor.decode("encoded", CipherAlgorithm.SHA512)). + thenThrow(new NoSuchAlgorithmException("boom")); + + assertDoesNotThrow(() -> rule.enforce(account)); + verify(account, never()).getUsername(); + } + +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/policy/DefaultRuleProviderTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/policy/DefaultRuleProviderTest.java new file mode 100644 index 00000000000..60ab7e3917c --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/policy/DefaultRuleProviderTest.java @@ -0,0 +1,174 @@ +/* + * 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.core.spring.policy; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; +import org.apache.syncope.core.provisioning.api.rules.AccountRule; +import org.apache.syncope.core.provisioning.api.rules.PasswordRule; +import org.apache.syncope.core.spring.implementation.ImplementationManager; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +class DefaultRuleProviderTest { + + @Test + void accountPoliciesFromResourcesAreDeduplicated() { + AccountPolicy policy = mock(AccountPolicy.class); + ExternalResource first = mock(ExternalResource.class); + when(first.getAccountPolicy()).thenReturn(policy); + ExternalResource second = mock(ExternalResource.class); + when(second.getAccountPolicy()).thenReturn(policy); + ExternalResource withoutPolicy = mock(ExternalResource.class); + + DefaultRuleProvider provider = new DefaultRuleProvider(mock(RealmSearchDAO.class)); + + assertEquals( + List.of(policy), + provider.getAccountPolicies(null, List.of(first, second, withoutPolicy))); + } + + @Test + void accountPoliciesFromRealmAncestorsAreDeduplicated() { + Realm realm = mock(Realm.class); + Realm root = mock(Realm.class); + when(root.getFullPath()).thenReturn(SyncopeConstants.ROOT_REALM); + Realm child = mock(Realm.class); + when(child.getFullPath()).thenReturn("/child"); + + AccountPolicy policy = mock(AccountPolicy.class); + AccountPolicy childPolicy = mock(AccountPolicy.class); + Realm rootEntity = mock(Realm.class); + when(rootEntity.getAccountPolicy()).thenReturn(policy); + Realm otherEntity = mock(Realm.class); + when(otherEntity.getAccountPolicy()).thenReturn(childPolicy); + + RealmSearchDAO realmSearchDAO = mock(RealmSearchDAO.class); + when(realmSearchDAO.findAncestors(realm)).thenReturn(List.of(root, child)); + when(realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM)).thenReturn(Optional.of(rootEntity)); + when(realmSearchDAO.findByFullPath("/child")).thenReturn(Optional.of(otherEntity)); + + DefaultRuleProvider provider = new DefaultRuleProvider(realmSearchDAO); + + assertEquals(List.of(policy, childPolicy), provider.getAccountPolicies(realm, List.of())); + } + + @Test + void accountPoliciesFromMissingRealmAreIgnored() { + Realm realm = mock(Realm.class); + Realm ancestor = mock(Realm.class); + when(ancestor.getFullPath()).thenReturn("/gone"); + + RealmSearchDAO realmSearchDAO = mock(RealmSearchDAO.class); + when(realmSearchDAO.findAncestors(realm)).thenReturn(List.of(ancestor)); + when(realmSearchDAO.findByFullPath("/gone")).thenReturn(Optional.empty()); + + DefaultRuleProvider provider = new DefaultRuleProvider(realmSearchDAO); + + assertTrue(provider.getAccountPolicies(realm, List.of()).isEmpty()); + } + + @Test + void passwordPoliciesFromResourcesAndRealm() { + PasswordPolicy resourcePolicy = mock(PasswordPolicy.class); + ExternalResource resource = mock(ExternalResource.class); + when(resource.getPasswordPolicy()).thenReturn(resourcePolicy); + + Realm realm = mock(Realm.class); + Realm ancestor = mock(Realm.class); + when(ancestor.getFullPath()).thenReturn(SyncopeConstants.ROOT_REALM); + + PasswordPolicy realmPolicy = mock(PasswordPolicy.class); + Realm realmEntity = mock(Realm.class); + when(realmEntity.getPasswordPolicy()).thenReturn(realmPolicy); + + RealmSearchDAO realmSearchDAO = mock(RealmSearchDAO.class); + when(realmSearchDAO.findAncestors(realm)).thenReturn(List.of(ancestor)); + when(realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM)).thenReturn(Optional.of(realmEntity)); + + DefaultRuleProvider provider = new DefaultRuleProvider(realmSearchDAO); + + assertEquals( + List.of(resourcePolicy, realmPolicy), + provider.getPasswordPolicies(realm, List.of(resource))); + } + + @Test + void accountRulesAreBuiltAndFailuresIgnored() throws Exception { + Implementation first = mock(Implementation.class); + Implementation second = mock(Implementation.class); + AccountPolicy policy = mock(AccountPolicy.class); + doReturn(List.of(first, second)).when(policy).getRules(); + + AccountRule rule = mock(AccountRule.class); + + try (MockedStatic manager = mockStatic(ImplementationManager.class)) { + manager.when(() -> ImplementationManager.buildAccountRule( + anyString(), eq(first), any(), any())). + thenReturn(Optional.of(rule)); + manager.when(() -> ImplementationManager.buildAccountRule( + anyString(), eq(second), any(), any())). + thenThrow(new ClassNotFoundException("not found")); + + DefaultRuleProvider provider = new DefaultRuleProvider(mock(RealmSearchDAO.class)); + + assertEquals(List.of(rule), provider.getAccountRules(policy)); + } + } + + @Test + void passwordRulesAreBuiltAndFailuresIgnored() throws Exception { + Implementation first = mock(Implementation.class); + Implementation second = mock(Implementation.class); + PasswordPolicy policy = mock(PasswordPolicy.class); + doReturn(List.of(first, second)).when(policy).getRules(); + + PasswordRule rule = mock(PasswordRule.class); + + try (MockedStatic manager = mockStatic(ImplementationManager.class)) { + manager.when(() -> ImplementationManager.buildPasswordRule( + anyString(), eq(first), any(), any())). + thenReturn(Optional.of(rule)); + manager.when(() -> ImplementationManager.buildPasswordRule( + anyString(), eq(second), any(), any())). + thenThrow(new ClassNotFoundException("not found")); + + DefaultRuleProvider provider = new DefaultRuleProvider(mock(RealmSearchDAO.class)); + + assertEquals(List.of(rule), provider.getPasswordRules(policy)); + } + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/policy/HaveIBeenPwnedPasswordRuleTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/policy/HaveIBeenPwnedPasswordRuleTest.java new file mode 100644 index 00000000000..c4a8350423f --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/policy/HaveIBeenPwnedPasswordRuleTest.java @@ -0,0 +1,208 @@ +/* + * 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.core.spring.policy; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockConstruction; +import static org.mockito.Mockito.when; + +import java.net.URI; +import java.security.NoSuchAlgorithmException; +import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf; +import org.apache.syncope.common.lib.policy.HaveIBeenPwnedPasswordRuleConf; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.core.persistence.api.Encryptor; +import org.apache.syncope.core.persistence.api.EncryptorManager; +import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.RestTemplate; + +class HaveIBeenPwnedPasswordRuleTest { + + private static final String SHA1_PASSWORD = "5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8"; + + private Encryptor encryptor; + + private HaveIBeenPwnedPasswordRule rule; + + @BeforeEach + void setUp() { + encryptor = mock(Encryptor.class); + + EncryptorManager encryptorManager = mock(EncryptorManager.class); + when(encryptorManager.getInstance()).thenReturn(encryptor); + + rule = new HaveIBeenPwnedPasswordRule(); + rule.setConf(new HaveIBeenPwnedPasswordRuleConf()); + ReflectionTestUtils.setField(rule, "encryptorManager", encryptorManager); + } + + private MockedConstruction pwnedService(final String body) { + return mockConstruction(RestTemplate.class, (mock, context) -> when(mock.exchange( + any(URI.class), eq(HttpMethod.GET), any(HttpEntity.class), eq(String.class))). + thenReturn(new ResponseEntity<>(body, HttpStatus.OK))); + } + + @Test + void setConfRequiresExpectedType() { + IllegalArgumentException ex = assertThrows( + IllegalArgumentException.class, + () -> rule.setConf(new DefaultPasswordRuleConf())); + assertEquals( + HaveIBeenPwnedPasswordRuleConf.class.getName() + " expected, got " + + DefaultPasswordRuleConf.class.getName(), + ex.getMessage()); + } + + @Test + void pwnedPasswordIsRejected() throws Exception { + when(encryptor.encode("password", CipherAlgorithm.SHA1)).thenReturn(SHA1_PASSWORD); + + try (MockedConstruction construction = pwnedService( + "1E4C9B93F3F0682250B6CF8331B7EE68FD8:12345\r\n" + + "0000000000000000000000000000000000A:1")) { + + PasswordPolicyException ex = + assertThrows(PasswordPolicyException.class, () -> rule.enforce("bob", "password")); + assertEquals("Password pwned", ex.getMessage()); + assertEquals(1, construction.constructed().size()); + } + } + + @Test + void notPwnedPasswordIsAccepted() throws Exception { + when(encryptor.encode("password", CipherAlgorithm.SHA1)).thenReturn(SHA1_PASSWORD); + + try (MockedConstruction construction = pwnedService( + "0000000000000000000000000000000000A:1")) { + + assertDoesNotThrow(() -> rule.enforce("bob", "password")); + assertEquals(1, construction.constructed().size()); + } + } + + @Test + void emptyResponseBodyIsAccepted() throws Exception { + when(encryptor.encode("password", CipherAlgorithm.SHA1)).thenReturn(SHA1_PASSWORD); + + try (MockedConstruction construction = pwnedService("")) { + assertDoesNotThrow(() -> rule.enforce("bob", "password")); + } + } + + @Test + void httpErrorIsIgnored() throws Exception { + when(encryptor.encode("password", CipherAlgorithm.SHA1)).thenReturn(SHA1_PASSWORD); + + try (MockedConstruction construction = mockConstruction( + RestTemplate.class, + (mock, context) -> when(mock.exchange( + any(URI.class), eq(HttpMethod.GET), any(HttpEntity.class), eq(String.class))). + thenThrow(new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR)))) { + + assertDoesNotThrow(() -> rule.enforce("bob", "password")); + } + } + + @Test + void encodingFailureIsIgnored() throws Exception { + when(encryptor.encode("password", CipherAlgorithm.SHA1)). + thenThrow(new NoSuchAlgorithmException("boom")); + + try (MockedConstruction construction = pwnedService("")) { + assertDoesNotThrow(() -> rule.enforce("bob", "password")); + assertTrue(construction.constructed().isEmpty()); + } + } + + @Test + void nullClearPasswordIsIgnored() { + assertDoesNotThrow(() -> rule.enforce("bob", null)); + + User user = mock(User.class); + assertDoesNotThrow(() -> rule.enforce(user, null)); + } + + @Test + void linkedAccountWithoutPasswordIsIgnored() { + LinkedAccount account = mock(LinkedAccount.class); + + assertDoesNotThrow(() -> rule.enforce(account)); + } + + @Test + void linkedAccountWithDecodablePasswordIsChecked() throws Exception { + when(encryptor.decode("encoded", CipherAlgorithm.SHA512)).thenReturn("password"); + when(encryptor.encode("password", CipherAlgorithm.SHA1)).thenReturn(SHA1_PASSWORD); + + LinkedAccount account = mock(LinkedAccount.class); + when(account.getPassword()).thenReturn("encoded"); + when(account.canDecodeSecrets()).thenReturn(true); + when(account.getCipherAlgorithm()).thenReturn(CipherAlgorithm.SHA512); + + try (MockedConstruction construction = pwnedService( + "1E4C9B93F3F0682250B6CF8331B7EE68FD8:12345")) { + + assertThrows(PasswordPolicyException.class, () -> rule.enforce(account)); + } + } + + @Test + void linkedAccountWithNonDecodablePasswordIsSkipped() throws Exception { + LinkedAccount account = mock(LinkedAccount.class); + when(account.getPassword()).thenReturn("encoded"); + when(account.canDecodeSecrets()).thenReturn(false); + + try (MockedConstruction construction = pwnedService("")) { + assertDoesNotThrow(() -> rule.enforce(account)); + assertTrue(construction.constructed().isEmpty()); + } + } + + @Test + void linkedAccountDecodingFailureIsIgnored() throws Exception { + when(encryptor.decode("encoded", CipherAlgorithm.SHA512)). + thenThrow(new NoSuchAlgorithmException("boom")); + + LinkedAccount account = mock(LinkedAccount.class); + when(account.getPassword()).thenReturn("encoded"); + when(account.canDecodeSecrets()).thenReturn(true); + when(account.getCipherAlgorithm()).thenReturn(CipherAlgorithm.SHA512); + + try (MockedConstruction construction = pwnedService("")) { + assertDoesNotThrow(() -> rule.enforce(account)); + assertTrue(construction.constructed().isEmpty()); + } + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/policy/PolicyPatternTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/policy/PolicyPatternTest.java new file mode 100644 index 00000000000..46ed94d3f6e --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/policy/PolicyPatternTest.java @@ -0,0 +1,84 @@ +/* + * 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.core.spring.policy; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class PolicyPatternTest { + + @Test + void digit() { + assertTrue(PolicyPattern.DIGIT.matcher("a1").matches()); + assertFalse(PolicyPattern.DIGIT.matcher("abc").matches()); + } + + @Test + void alphabeticalCase() { + assertTrue(PolicyPattern.ALPHA_LOWERCASE.matcher("aB").matches()); + assertFalse(PolicyPattern.ALPHA_LOWERCASE.matcher("AB1").matches()); + + assertTrue(PolicyPattern.ALPHA_UPPERCASE.matcher("Ab").matches()); + assertFalse(PolicyPattern.ALPHA_UPPERCASE.matcher("ab1").matches()); + } + + @Test + void firstAndLastDigit() { + assertTrue(PolicyPattern.FIRST_DIGIT.matcher("1abc").matches()); + assertFalse(PolicyPattern.FIRST_DIGIT.matcher("a1bc").matches()); + + assertTrue(PolicyPattern.LAST_DIGIT.matcher("abc1").matches()); + assertFalse(PolicyPattern.LAST_DIGIT.matcher("ab1c").matches()); + } + + @Test + void alphanumeric() { + assertTrue(PolicyPattern.ALPHANUMERIC.matcher("a").matches()); + assertTrue(PolicyPattern.ALPHANUMERIC.matcher("1").matches()); + assertFalse(PolicyPattern.ALPHANUMERIC.matcher("!").matches()); + + assertTrue(PolicyPattern.FIRST_ALPHANUMERIC.matcher("a!").matches()); + assertFalse(PolicyPattern.FIRST_ALPHANUMERIC.matcher("!a").matches()); + + assertTrue(PolicyPattern.LAST_ALPHANUMERIC.matcher("!a").matches()); + assertFalse(PolicyPattern.LAST_ALPHANUMERIC.matcher("a!").matches()); + } + + @Test + void nonAlphanumeric() { + assertTrue(PolicyPattern.NON_ALPHANUMERIC.matcher("a!b").matches()); + assertFalse(PolicyPattern.NON_ALPHANUMERIC.matcher("ab1").matches()); + + assertTrue(PolicyPattern.FIRST_NON_ALPHANUMERIC.matcher("!ab").matches()); + assertFalse(PolicyPattern.FIRST_NON_ALPHANUMERIC.matcher("a!b").matches()); + + assertTrue(PolicyPattern.LAST_NON_ALPHANUMERIC.matcher("ab!").matches()); + assertFalse(PolicyPattern.LAST_NON_ALPHANUMERIC.matcher("a!b").matches()); + } + + @Test + void nonAlphanumericCharsForPasswordValues() { + assertArrayEquals( + new char[] { '!', '£', '%', '&', '(', ')', '?', '#', '$' }, + PolicyPattern.NON_ALPHANUMERIC_CHARS_FOR_PASSWORD_VALUES); + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/AuthContextUtilsTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/AuthContextUtilsTest.java new file mode 100644 index 00000000000..6096959d102 --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/AuthContextUtilsTest.java @@ -0,0 +1,269 @@ +/* + * 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.core.spring.security; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.EntitlementsHolder; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.persistence.api.ApplicationContextProvider; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.StaticApplicationContext; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; + +class AuthContextUtilsTest { + + private StaticApplicationContext context; + + private SecurityProperties securityProperties; + + private UserDAO userDAO; + + private ConfigurableApplicationContext previousContext; + + private DefaultListableBeanFactory previousBeanFactory; + + @BeforeEach + void setUp() { + previousContext = ApplicationContextProvider.getApplicationContext(); + previousBeanFactory = ApplicationContextProvider.getBeanFactory(); + + SecurityContextHolder.clearContext(); + + EntitlementsHolder.getInstance().addAll(IdRepoEntitlement.values()); + + securityProperties = new SecurityProperties(); + securityProperties.setAdminUser("admin"); + + userDAO = mock(UserDAO.class); + + context = new StaticApplicationContext(); + context.getBeanFactory().registerSingleton("securityProperties", securityProperties); + context.getBeanFactory().registerSingleton("userDAO", userDAO); + context.refresh(); + + ApplicationContextProvider.setApplicationContext(context); + ApplicationContextProvider.setBeanFactory((DefaultListableBeanFactory) context.getBeanFactory()); + } + + @AfterEach + void tearDown() { + SecurityContextHolder.clearContext(); + ApplicationContextProvider.setApplicationContext(previousContext); + ApplicationContextProvider.setBeanFactory(previousBeanFactory); + context.close(); + } + + private Authentication authentication(final String username, final SyncopeAuthenticationDetails details) { + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( + username, + "pwd", + List.of( + new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ, "/a"), + new SyncopeGrantedAuthority(IdRepoEntitlement.GROUP_READ, "/b"))); + authentication.setDetails(details); + return authentication; + } + + @Test + void getUsernameWithoutAuthentication() { + assertEquals("unauthenticated", AuthContextUtils.getUsername()); + } + + @Test + void getUsernameWithAuthentication() { + SecurityContextHolder.getContext().setAuthentication( + authentication("bob", new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, null))); + + assertEquals("bob", AuthContextUtils.getUsername()); + } + + @Test + void updateUsernameKeepsCredentialsAuthoritiesAndDetails() { + SyncopeAuthenticationDetails details = + new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, "delegator"); + Authentication original = authentication("bob", details); + SecurityContextHolder.getContext().setAuthentication(original); + + AuthContextUtils.updateUsername("alice"); + + Authentication updated = SecurityContextHolder.getContext().getAuthentication(); + assertEquals("alice", updated.getName()); + assertEquals("pwd", updated.getCredentials()); + assertSame(details, updated.getDetails()); + assertEquals(original.getAuthorities(), updated.getAuthorities()); + } + + @Test + void getDelegatedBy() { + assertTrue(AuthContextUtils.getDelegatedBy().isEmpty()); + + SecurityContextHolder.getContext().setAuthentication( + new UsernamePasswordAuthenticationToken("bob", null, List.of())); + assertTrue(AuthContextUtils.getDelegatedBy().isEmpty()); + + SecurityContextHolder.getContext().setAuthentication( + authentication("bob", new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, "delegator"))); + assertEquals("delegator", AuthContextUtils.getDelegatedBy().orElseThrow()); + } + + @Test + void getDomain() { + assertEquals(SyncopeConstants.MASTER_DOMAIN, AuthContextUtils.getDomain()); + + SecurityContextHolder.getContext().setAuthentication( + authentication("bob", new SyncopeAuthenticationDetails(" ", null))); + assertEquals(SyncopeConstants.MASTER_DOMAIN, AuthContextUtils.getDomain()); + + SecurityContextHolder.getContext().setAuthentication( + authentication("bob", new SyncopeAuthenticationDetails("other", null))); + assertEquals("other", AuthContextUtils.getDomain()); + } + + @Test + void getAuthoritiesAndAuthorizations() { + assertTrue(AuthContextUtils.getAuthorities().isEmpty()); + assertTrue(AuthContextUtils.getAuthorizations().isEmpty()); + + SecurityContextHolder.getContext().setAuthentication( + authentication("bob", new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, null))); + + assertEquals( + Set.of(IdRepoEntitlement.USER_READ, IdRepoEntitlement.GROUP_READ), + AuthContextUtils.getAuthorities().stream().map(SyncopeGrantedAuthority::getAuthority). + collect(java.util.stream.Collectors.toSet())); + + Map> authorizations = AuthContextUtils.getAuthorizations(); + assertEquals(Set.of("/a"), authorizations.get(IdRepoEntitlement.USER_READ)); + assertEquals(Set.of("/b"), authorizations.get(IdRepoEntitlement.GROUP_READ)); + } + + @Test + void getWhoWithoutDelegation() { + SecurityContextHolder.getContext().setAuthentication( + new UsernamePasswordAuthenticationToken("bob", null, List.of())); + + assertEquals("bob", AuthContextUtils.getWho()); + } + + @Test + void getWhoWithDelegation() { + when(userDAO.findUsername("delegator")).thenReturn(java.util.Optional.of("Delegator Name")); + SecurityContextHolder.getContext().setAuthentication( + authentication("bob", new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, "delegator"))); + + assertEquals("bob [delegated by Delegator Name]", AuthContextUtils.getWho()); + } + + @Test + void callAsSetsAndRestoresAuthentication() throws Exception { + Authentication original = authentication("bob", null); + SecurityContextHolder.getContext().setAuthentication(original); + + String username = AuthContextUtils.callAs( + "other", + "alice", + List.of(new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ)), + () -> { + assertEquals("alice", AuthContextUtils.getUsername()); + assertEquals("other", AuthContextUtils.getDomain()); + return AuthContextUtils.getUsername(); + }); + + assertEquals("alice", username); + assertSame(original, SecurityContextHolder.getContext().getAuthentication()); + } + + @Test + void callAsPropagatesRuntimeExceptionAndRestoresAuthentication() { + Authentication original = authentication("bob", null); + SecurityContextHolder.getContext().setAuthentication(original); + + IllegalStateException ex = assertThrows( + IllegalStateException.class, + () -> AuthContextUtils.callAs( + SyncopeConstants.MASTER_DOMAIN, + "alice", + List.of(), + () -> { + throw new IllegalStateException("boom"); + })); + + assertEquals("boom", ex.getMessage()); + assertSame(original, SecurityContextHolder.getContext().getAuthentication()); + } + + @Test + void callAsWrapsCheckedExceptionAndRestoresAuthentication() { + Authentication original = authentication("bob", null); + SecurityContextHolder.getContext().setAuthentication(original); + + RuntimeException ex = assertThrows( + RuntimeException.class, + () -> AuthContextUtils.callAs( + SyncopeConstants.MASTER_DOMAIN, + "alice", + List.of(), + () -> { + throw new Exception("boom"); + })); + + assertTrue(ex.getCause() instanceof Exception); + assertSame(original, SecurityContextHolder.getContext().getAuthentication()); + } + + @Test + void callAsAdminUsesConfiguredAdminUser() { + String username = AuthContextUtils.callAsAdmin( + SyncopeConstants.MASTER_DOMAIN, + AuthContextUtils::getUsername); + + assertEquals("admin", username); + } + + @Test + void runAsAdminExecutesRunnable() { + boolean[] executed = { false }; + + AuthContextUtils.runAsAdmin(SyncopeConstants.MASTER_DOMAIN, () -> executed[0] = true); + + assertTrue(executed[0]); + assertNull(SecurityContextHolder.getContext().getAuthentication()); + assertFalse(AuthContextUtils.getAuthorities().contains( + new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ))); + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/AuthDataAccessorTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/AuthDataAccessorTest.java index fe29f907acc..a83cd317715 100644 --- a/core/spring/src/test/java/org/apache/syncope/core/spring/security/AuthDataAccessorTest.java +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/AuthDataAccessorTest.java @@ -19,32 +19,82 @@ package org.apache.syncope.core.spring.security; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.JWSHeader; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; -import java.lang.reflect.Constructor; +import dev.samstevens.totp.code.CodeVerifier; import java.time.OffsetDateTime; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; +import org.apache.syncope.common.keymaster.client.api.ConfParamOps; +import org.apache.syncope.common.keymaster.client.api.StandardConfParams; +import org.apache.syncope.common.keymaster.client.api.model.Domain; import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.to.Provision; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.apache.syncope.common.lib.types.EntitlementsHolder; import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.common.lib.types.Mfa; +import org.apache.syncope.common.lib.types.OpEvent; +import org.apache.syncope.core.persistence.api.Encryptor; +import org.apache.syncope.core.persistence.api.EncryptorManager; import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.persistence.api.dao.DelegationDAO; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; +import org.apache.syncope.core.persistence.api.dao.RoleDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.dao.search.SearchCond; import org.apache.syncope.core.persistence.api.entity.AccessToken; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.Delegation; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.Role; +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.AuditManager; +import org.apache.syncope.core.provisioning.api.Connector; +import org.apache.syncope.core.provisioning.api.ConnectorManager; +import org.apache.syncope.core.provisioning.api.MappingManager; import org.apache.syncope.core.spring.security.jws.AccessTokenJWSSigner; import org.apache.syncope.core.spring.security.jws.AccessTokenJWSVerifier; +import org.identityconnectors.framework.common.objects.Uid; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.DisabledException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.web.authentication.session.SessionAuthenticationException; class AuthDataAccessorTest { @@ -52,10 +102,46 @@ class AuthDataAccessorTest { private static final String LOW_PRIVILEGE_USER = "low-priv-user"; + private static final String ADMIN = "admin"; + + private static final String ANONYMOUS = "anonymous"; + + private static final String ANONYMOUS_KEY = "anonymous-key"; + private SecurityProperties securityProperties; + private EncryptorManager encryptorManager; + + private Encryptor encryptor; + + private CodeVerifier totpCodeVerifier; + + private RealmSearchDAO realmSearchDAO; + + private UserDAO userDAO; + + private GroupDAO groupDAO; + + private AnySearchDAO anySearchDAO; + private AccessTokenDAO accessTokenDAO; + private ConfParamOps confParamOps; + + private RoleDAO roleDAO; + + private DelegationDAO delegationDAO; + + private ExternalResourceDAO resourceDAO; + + private ConnectorManager connectorManager; + + private AuditManager auditManager; + + private MappingManager mappingManager; + + private DefaultCredentialChecker credentialChecker; + private AuthDataAccessor authDataAccessor; @BeforeEach @@ -64,31 +150,861 @@ void setUp() throws Exception { securityProperties.setJwtIssuer("ApacheSyncope"); securityProperties.setJwsAlgorithm("HS512"); securityProperties.setJwsKey(SecureRandomUtils.generateRandomLetters(64)); - securityProperties.setAdminUser("admin"); + securityProperties.setAdminUser(ADMIN); + securityProperties.setAdminPassword("admin-password"); + securityProperties.setAdminPasswordAlgorithm(CipherAlgorithm.SHA512); + securityProperties.setAnonymousUser(ANONYMOUS); + securityProperties.setAnonymousKey(ANONYMOUS_KEY); EntitlementsHolder.getInstance().addAll(IdRepoEntitlement.values()); - AccessToken lowPrivilegeToken = mock(AccessToken.class); - when(lowPrivilegeToken.getOwner()).thenReturn(LOW_PRIVILEGE_USER); + encryptorManager = mock(EncryptorManager.class); + encryptor = mock(Encryptor.class); + when(encryptorManager.getInstance()).thenReturn(encryptor); + when(encryptor.verify(any(), any(), any())).thenReturn(true); + when(encryptor.decode(any(), any())).thenReturn("decoded-secret"); + + totpCodeVerifier = mock(CodeVerifier.class); + realmSearchDAO = mock(RealmSearchDAO.class); + userDAO = mock(UserDAO.class); + groupDAO = mock(GroupDAO.class); + anySearchDAO = mock(AnySearchDAO.class); accessTokenDAO = mock(AccessTokenDAO.class); - doReturn(Optional.of(lowPrivilegeToken)).when(accessTokenDAO).findById(LOW_PRIVILEGE_JTI); + confParamOps = mock(ConfParamOps.class); + roleDAO = mock(RoleDAO.class); + delegationDAO = mock(DelegationDAO.class); + resourceDAO = mock(ExternalResourceDAO.class); + connectorManager = mock(ConnectorManager.class); + auditManager = mock(AuditManager.class); + mappingManager = mock(MappingManager.class); + credentialChecker = mock(DefaultCredentialChecker.class); + + when(confParamOps.get(anyString(), eq(StandardConfParams.MFA_ENABLED), any(), eq(boolean.class))). + thenReturn(false); + when(confParamOps.get(anyString(), eq(StandardConfParams.LOG_LAST_LOGIN_DATE), any(), eq(boolean.class))). + thenReturn(true); + when(confParamOps.get(anyString(), eq(StandardConfParams.AUTHENTICATION_STATUSES), any(), + eq(String[].class))).thenReturn(new String[] { "active" }); + when(confParamOps.get(anyString(), eq(StandardConfParams.AUTHENTICATION_ATTRIBUTES), any(), + eq(String[].class))).thenReturn(new String[] { "username" }); + + authDataAccessor = newAccessor(); + } + + private AuthDataAccessor newAccessor(final JWTSSOProvider... providers) { + return new AuthDataAccessor( + securityProperties, + encryptorManager, + totpCodeVerifier, + realmSearchDAO, + userDAO, + groupDAO, + anySearchDAO, + accessTokenDAO, + confParamOps, + roleDAO, + delegationDAO, + resourceDAO, + connectorManager, + auditManager, + mappingManager, + credentialChecker, + List.of(providers)); + } + + private Authentication usernamePassword(final String username, final String credentials) { + return usernamePassword(username, credentials, + new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, null)); + } + + private Authentication usernamePassword( + final String username, + final String credentials, + final SyncopeAuthenticationDetails details) { + + Authentication authentication = mock(Authentication.class); + when(authentication.getName()).thenReturn(username); + when(authentication.getCredentials()).thenReturn(credentials); + when(authentication.getDetails()).thenReturn(details); + return authentication; + } + + private JWTAuthentication jwt( + final String subject, + final String issuer, + final String jwtID, + final String delegatedBy) { + + JWTClaimsSet claims = new JWTClaimsSet.Builder(). + issuer(issuer). + subject(subject). + jwtID(jwtID). + issueTime(new Date()). + build(); + return new JWTAuthentication( + claims, + new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, delegatedBy)); + } + + private User user(final String username) { + User user = mock(User.class); + when(user.getUsername()).thenReturn(username); + when(user.getKey()).thenReturn(username + "-key"); + when(user.getStatus()).thenReturn("active"); + when(user.isSuspended()).thenReturn(false); + when(user.isMustChangePassword()).thenReturn(false); + when(user.getFailedLogins()).thenReturn(0); + when(user.getCipherAlgorithm()).thenReturn(CipherAlgorithm.SHA512); + when(user.getPassword()).thenReturn("encoded-password"); + return user; + } + + private Role role(final String entitlement, final String realmPath) { + Role role = mock(Role.class); + when(role.getEntitlements()).thenReturn(Set.of(entitlement)); + + Realm realm = mock(Realm.class); + when(realm.getFullPath()).thenReturn(realmPath); + doReturn(List.of(realm)).when(role).getRealms(); + return role; + } + + private void mfaEnabled(final boolean enabled) { + when(confParamOps.get(anyString(), eq(StandardConfParams.MFA_ENABLED), any(), eq(boolean.class))). + thenReturn(enabled); + } + + private void authenticationStatuses(final String... statuses) { + when(confParamOps.get(anyString(), eq(StandardConfParams.AUTHENTICATION_STATUSES), any(), + eq(String[].class))).thenReturn(statuses); + } + + private void authenticationAttributes(final String... attributes) { + when(confParamOps.get(anyString(), eq(StandardConfParams.AUTHENTICATION_ATTRIBUTES), any(), + eq(String[].class))).thenReturn(attributes); + } + + private static Set entitlements(final Set authorities) { + return authorities.stream().map(SyncopeGrantedAuthority::getAuthority).collect(Collectors.toSet()); + } + + private static void assertContains(final Set authorities, final String entitlement) { + assertTrue(entitlements(authorities).contains(entitlement), + "Expected entitlement " + entitlement + " in " + entitlements(authorities)); + } + + @Test + void getJWTSSOProviderRejectsNullIssuer() { + AuthenticationCredentialsNotFoundException ex = assertThrows( + AuthenticationCredentialsNotFoundException.class, + () -> authDataAccessor.getJWTSSOProvider(null)); + assertEquals("A null issuer is not permitted", ex.getMessage()); + } + + @Test + void getJWTSSOProviderRejectsUnknownIssuer() { + AuthenticationCredentialsNotFoundException ex = assertThrows( + AuthenticationCredentialsNotFoundException.class, + () -> authDataAccessor.getJWTSSOProvider("unknown")); + assertEquals("Could not find any registered JWTSSOProvider for issuer unknown", ex.getMessage()); + } + + @Test + void getJWTSSOProviderReturnsMatchingProvider() { + JWTSSOProvider provider = mock(JWTSSOProvider.class); + when(provider.getIssuer()).thenReturn("issuer"); + AuthDataAccessor accessor = newAccessor(provider); + + assertSame(provider, accessor.getJWTSSOProvider("issuer")); + } + + @Test + void anonymousAuthenticationSucceedsWithConfiguredKey() { + AuthDataAccessor.UsernamePasswordAuthResult result = authDataAccessor.authenticate( + Optional.empty(), usernamePassword(ANONYMOUS, ANONYMOUS_KEY)); + + assertTrue(result.isSuccess()); + assertTrue(result.passwordVerified()); + assertNull(result.user()); + assertNull(result.otpVerified()); + assertEquals(Set.of(IdRepoEntitlement.ANONYMOUS), entitlements(result.authorities())); + verify(credentialChecker).checkIsDefaultAnonymousKeyInUse(); + } + + @Test + void anonymousAuthenticationFailsWithWrongKey() { + AuthDataAccessor.UsernamePasswordAuthResult result = authDataAccessor.authenticate( + Optional.empty(), usernamePassword(ANONYMOUS, "wrong")); + + assertFalse(result.isSuccess()); + assertFalse(result.passwordVerified()); + assertEquals(Set.of(IdRepoEntitlement.ANONYMOUS), entitlements(result.authorities())); + } + + @Test + void adminAuthenticationWithoutMfaGrantsAllEntitlements() { + AuthDataAccessor.UsernamePasswordAuthResult result = authDataAccessor.authenticate( + Optional.empty(), usernamePassword(ADMIN, "admin-password")); + + assertTrue(result.isSuccess()); + assertNull(result.user()); + assertContains(result.authorities(), IdRepoEntitlement.USER_READ); + assertContains(result.authorities(), IdRepoEntitlement.GROUP_READ); + verify(credentialChecker).checkIsDefaultAdminPasswordInUse(); + } + + @Test + void adminAuthenticationWithMfaButNoSecretRequiresEnrollment() { + mfaEnabled(true); + + AuthDataAccessor.UsernamePasswordAuthResult result = authDataAccessor.authenticate( + Optional.empty(), usernamePassword(ADMIN, "admin-password:123456")); + + assertTrue(result.isSuccess()); + assertNull(result.otpVerified()); + assertEquals(Set.of(IdRepoEntitlement.MFA_ENROLL), entitlements(result.authorities())); + } + + @Test + void adminAuthenticationWithMfaAndValidOtpGrantsAllEntitlements() { + mfaEnabled(true); + securityProperties.setAdminMfaSecret("encoded-secret"); + when(totpCodeVerifier.isValidCode("decoded-secret", "123456")).thenReturn(true); + + AuthDataAccessor.UsernamePasswordAuthResult result = authDataAccessor.authenticate( + Optional.empty(), usernamePassword(ADMIN, "admin-password:123456")); + + assertTrue(result.isSuccess()); + assertTrue(result.otpVerified()); + assertContains(result.authorities(), IdRepoEntitlement.USER_READ); + } + + @Test + void adminAuthenticationWithUndecodableMfaSecretThrowsBadCredentials() throws Exception { + mfaEnabled(true); + securityProperties.setAdminMfaSecret("encoded-secret"); + when(encryptor.decode(any(), any())).thenThrow(new IllegalStateException("boom")); + + BadCredentialsException ex = assertThrows( + BadCredentialsException.class, + () -> authDataAccessor.authenticate( + Optional.empty(), usernamePassword(ADMIN, "admin-password:123456"))); + assertEquals("Could not decode admin MFA secret for Master", ex.getMessage()); + } + + @Test + void adminAuthenticationOnSpecificDomainUsesDomainCredentials() { + Domain domain = mock(Domain.class); + when(domain.getKey()).thenReturn("other"); + when(domain.getAdminCipherAlgorithm()).thenReturn(CipherAlgorithm.SHA512); + when(domain.getAdminPassword()).thenReturn("domain-password"); + when(domain.getAdminMfaSecret()).thenReturn(null); + + AuthDataAccessor.UsernamePasswordAuthResult result = authDataAccessor.authenticate( + Optional.of(domain), usernamePassword(ADMIN, "domain-password")); + + assertTrue(result.isSuccess()); + verify(credentialChecker, never()).checkIsDefaultAdminPasswordInUse(); + } + + @Test + void unknownUserThrowsUsernameNotFound() { + doReturn(Optional.empty()).when(userDAO).findByUsername("bob"); + + UsernameNotFoundException ex = assertThrows( + UsernameNotFoundException.class, + () -> authDataAccessor.authenticate(Optional.empty(), usernamePassword("bob", "password"))); + assertEquals("bob", ex.getMessage()); + } + + @Test + void suspendedUserCannotAuthenticate() { + User user = user("bob"); + when(user.isSuspended()).thenReturn(true); + doReturn(Optional.of(user)).when(userDAO).findByUsername("bob"); + + DisabledException ex = assertThrows( + DisabledException.class, + () -> authDataAccessor.authenticate(Optional.empty(), usernamePassword("bob", "password"))); + assertEquals("User bob is suspended", ex.getMessage()); + } + + @Test + void userWithDisallowedStatusCannotAuthenticate() { + User user = user("bob"); + when(user.getStatus()).thenReturn("inactive"); + doReturn(Optional.of(user)).when(userDAO).findByUsername("bob"); + authenticationStatuses("active"); + + DisabledException ex = assertThrows( + DisabledException.class, + () -> authDataAccessor.authenticate(Optional.empty(), usernamePassword("bob", "password"))); + assertEquals("User bob not allowed to authenticate", ex.getMessage()); + } + + @Test + void wrongPasswordIncrementsFailedLogins() { + User user = user("bob"); + when(user.getFailedLogins()).thenReturn(2); + doReturn(Optional.of(user)).when(userDAO).findByUsername("bob"); + when(userDAO.save(user)).thenReturn(user); + when(encryptor.verify(any(), any(), any())).thenReturn(false); + + AuthDataAccessor.UsernamePasswordAuthResult result = authDataAccessor.authenticate( + Optional.empty(), usernamePassword("bob", "wrong")); + + assertFalse(result.isSuccess()); + assertFalse(result.passwordVerified()); + assertNull(result.otpVerified()); + verify(user).setFailedLogins(3); + verify(userDAO).save(user); + } + + @Test + void validPasswordReturnsRoleAuthoritiesAndResetsState() { + User user = user("bob"); + when(user.getFailedLogins()).thenReturn(2); + doReturn(Optional.of(user)).when(userDAO).findByUsername("bob"); + Role userRole = role(IdRepoEntitlement.USER_READ, SyncopeConstants.ROOT_REALM); + doReturn(List.of(userRole)).when(userDAO).findAllRoles(user); + when(userDAO.save(user)).thenReturn(user); + + AuthDataAccessor.UsernamePasswordAuthResult result = authDataAccessor.authenticate( + Optional.empty(), usernamePassword("bob", "password")); + + assertTrue(result.isSuccess()); + assertContains(result.authorities(), IdRepoEntitlement.USER_READ); + verify(user).setLastLoginDate(any()); + verify(user).setFailedLogins(0); + verify(userDAO).save(user); + } + + @Test + void userWithMfaButNoSecretRequiresEnrollment() { + mfaEnabled(true); + User user = user("bob"); + doReturn(Optional.of(user)).when(userDAO).findByUsername("bob"); + when(userDAO.save(user)).thenReturn(user); + + AuthDataAccessor.UsernamePasswordAuthResult result = authDataAccessor.authenticate( + Optional.empty(), usernamePassword("bob", "password:123456")); + + assertTrue(result.isSuccess()); + assertNull(result.otpVerified()); + assertEquals(Set.of(IdRepoEntitlement.MFA_ENROLL), entitlements(result.authorities())); + } + + @Test + void userWithMfaAndValidTotpAuthenticates() { + mfaEnabled(true); + User user = user("bob"); + when(user.getMfa()).thenReturn(new Mfa("totp-secret", "data-uri", new ArrayList<>())); + doReturn(Optional.of(user)).when(userDAO).findByUsername("bob"); + Role userRole = role(IdRepoEntitlement.USER_READ, SyncopeConstants.ROOT_REALM); + doReturn(List.of(userRole)).when(userDAO).findAllRoles(user); + when(userDAO.save(user)).thenReturn(user); + when(totpCodeVerifier.isValidCode("totp-secret", "123456")).thenReturn(true); + + AuthDataAccessor.UsernamePasswordAuthResult result = authDataAccessor.authenticate( + Optional.empty(), usernamePassword("bob", "password:123456")); + + assertTrue(result.isSuccess()); + assertTrue(result.otpVerified()); + assertContains(result.authorities(), IdRepoEntitlement.USER_READ); + } + + @Test + void userWithMfaAndInvalidTotpFails() { + mfaEnabled(true); + User user = user("bob"); + when(user.getMfa()).thenReturn(new Mfa("totp-secret", "data-uri", new ArrayList<>())); + doReturn(Optional.of(user)).when(userDAO).findByUsername("bob"); + when(userDAO.save(user)).thenReturn(user); + when(totpCodeVerifier.isValidCode("totp-secret", "123456")).thenReturn(false); + + AuthDataAccessor.UsernamePasswordAuthResult result = authDataAccessor.authenticate( + Optional.empty(), usernamePassword("bob", "password:123456")); + + assertFalse(result.isSuccess()); + assertFalse(result.otpVerified()); + verify(user).setFailedLogins(1); + } + + @Test + void userWithValidRecoveryCodeConsumesIt() { + mfaEnabled(true); + User user = user("bob"); + Mfa mfa = new Mfa("totp-secret", "data-uri", new ArrayList<>(List.of("recovery-code"))); + when(user.getMfa()).thenReturn(mfa); + doReturn(Optional.of(user)).when(userDAO).findByUsername("bob"); + Role userRole = role(IdRepoEntitlement.USER_READ, SyncopeConstants.ROOT_REALM); + doReturn(List.of(userRole)).when(userDAO).findAllRoles(user); + when(userDAO.save(user)).thenReturn(user); + + AuthDataAccessor.UsernamePasswordAuthResult result = authDataAccessor.authenticate( + Optional.empty(), usernamePassword("bob", "password:recovery-code")); + + assertTrue(result.isSuccess()); + assertTrue(result.otpVerified()); + verify(user).setMfa(mfa); + } + + @Test + void userWithInvalidRecoveryCodeFails() { + mfaEnabled(true); + User user = user("bob"); + when(user.getMfa()).thenReturn(new Mfa("totp-secret", "data-uri", new ArrayList<>(List.of("recovery-code")))); + doReturn(Optional.of(user)).when(userDAO).findByUsername("bob"); + when(userDAO.save(user)).thenReturn(user); + + AuthDataAccessor.UsernamePasswordAuthResult result = authDataAccessor.authenticate( + Optional.empty(), usernamePassword("bob", "password:unknown-code")); + + assertFalse(result.isSuccess()); + assertFalse(result.otpVerified()); + verify(user).setFailedLogins(1); + } + + @Test + void userCanBeFoundViaCustomAuthenticationAttribute() { + authenticationAttributes("email"); + User user = user("bob"); + Role userRole = role(IdRepoEntitlement.USER_READ, SyncopeConstants.ROOT_REALM); + doReturn(List.of(userRole)).when(userDAO).findAllRoles(user); + when(userDAO.save(user)).thenReturn(user); + doReturn(List.of(user)).when(anySearchDAO).search(any(SearchCond.class), eq(AnyTypeKind.USER)); + + AuthDataAccessor.UsernamePasswordAuthResult result = authDataAccessor.authenticate( + Optional.empty(), usernamePassword("bob@example.com", "password")); + + assertTrue(result.isSuccess()); + verify(anySearchDAO).search(any(SearchCond.class), eq(AnyTypeKind.USER)); + } + + @Test + void searchFailureForCustomAuthenticationAttributeYieldsUsernameNotFound() { + authenticationAttributes("email"); + doThrow(new IllegalArgumentException("bad schema")). + when(anySearchDAO).search(any(SearchCond.class), eq(AnyTypeKind.USER)); + + assertThrows( + UsernameNotFoundException.class, + () -> authDataAccessor.authenticate( + Optional.empty(), usernamePassword("bob@example.com", "password"))); + } + + @Test + void customAuthenticationAttributeMatchingMultipleUsersYieldsUsernameNotFound() { + authenticationAttributes("email"); + doReturn(List.of(user("bob"), user("alice"))). + when(anySearchDAO).search(any(SearchCond.class), eq(AnyTypeKind.USER)); + + assertThrows( + UsernameNotFoundException.class, + () -> authDataAccessor.authenticate( + Optional.empty(), usernamePassword("shared@example.com", "password"))); + } + + @Test + void delegationByUsernameIsResolved() { + User user = user("bob"); + doReturn(Optional.of(user)).when(userDAO).findByUsername("bob"); + Role userRole = role(IdRepoEntitlement.USER_READ, SyncopeConstants.ROOT_REALM); + doReturn(List.of(userRole)).when(userDAO).findAllRoles(user); + when(userDAO.save(user)).thenReturn(user); + when(userDAO.findKey("delegator")).thenReturn(Optional.of("delegator-key")); + when(delegationDAO.findValidFor(eq("delegator-key"), eq("bob-key"), any())). + thenReturn(Optional.of("delegation-key")); + + SyncopeAuthenticationDetails details = + new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, "delegator"); + AuthDataAccessor.UsernamePasswordAuthResult result = authDataAccessor.authenticate( + Optional.empty(), usernamePassword("bob", "password", details)); + + assertTrue(result.isSuccess()); + assertEquals("delegation-key", result.delegationKey()); + } + + @Test + void delegationByUuidIsResolvedWithoutUserLookup() { + User user = user("bob"); + doReturn(Optional.of(user)).when(userDAO).findByUsername("bob"); + Role userRole = role(IdRepoEntitlement.USER_READ, SyncopeConstants.ROOT_REALM); + doReturn(List.of(userRole)).when(userDAO).findAllRoles(user); + when(userDAO.save(user)).thenReturn(user); + String delegatingKey = UUID.randomUUID().toString(); + when(delegationDAO.findValidFor(eq(delegatingKey), eq("bob-key"), any())). + thenReturn(Optional.of("delegation-key")); + + SyncopeAuthenticationDetails details = + new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, delegatingKey); + AuthDataAccessor.UsernamePasswordAuthResult result = authDataAccessor.authenticate( + Optional.empty(), usernamePassword("bob", "password", details)); + + assertEquals("delegation-key", result.delegationKey()); + verify(userDAO, never()).findKey(anyString()); + } + + @Test + void unknownDelegatingUserThrowsSessionAuthenticationException() { + User user = user("bob"); + doReturn(Optional.of(user)).when(userDAO).findByUsername("bob"); + when(userDAO.save(user)).thenReturn(user); + when(userDAO.findKey("ghost")).thenReturn(Optional.empty()); + + SyncopeAuthenticationDetails details = + new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, "ghost"); + SessionAuthenticationException ex = assertThrows( + SessionAuthenticationException.class, + () -> authDataAccessor.authenticate( + Optional.empty(), usernamePassword("bob", "password", details))); + assertEquals("Delegating user ghost cannot be found", ex.getMessage()); + } + + @Test + void missingValidDelegationThrowsSessionAuthenticationException() { + User user = user("bob"); + doReturn(Optional.of(user)).when(userDAO).findByUsername("bob"); + when(userDAO.save(user)).thenReturn(user); + when(userDAO.findKey("delegator")).thenReturn(Optional.of("delegator-key")); + when(delegationDAO.findValidFor(eq("delegator-key"), eq("bob-key"), any())). + thenReturn(Optional.empty()); + + SyncopeAuthenticationDetails details = + new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, "delegator"); + SessionAuthenticationException ex = assertThrows( + SessionAuthenticationException.class, + () -> authDataAccessor.authenticate( + Optional.empty(), usernamePassword("bob", "password", details))); + assertEquals("Delegation by delegator-key was requested but none found", ex.getMessage()); + } + + @Test + void passthroughResourcesAreIntersectedAcrossResourcesAndRealmAncestors() { + User user = user("bob"); + + ExternalResource first = mock(ExternalResource.class); + ExternalResource second = mock(ExternalResource.class); + AccountPolicy firstPolicy = mock(AccountPolicy.class); + AccountPolicy secondPolicy = mock(AccountPolicy.class); + when(first.getAccountPolicy()).thenReturn(firstPolicy); + when(second.getAccountPolicy()).thenReturn(secondPolicy); + when(userDAO.findAllResources(user)).thenReturn(List.of(first, second)); + when(resourceDAO.findByPolicy(firstPolicy)).thenReturn(List.of(first, second)); + when(resourceDAO.findByPolicy(secondPolicy)).thenReturn(List.of(second)); + + Realm realm = mock(Realm.class); + Realm ancestor = mock(Realm.class); + AccountPolicy ancestorPolicy = mock(AccountPolicy.class); + when(user.getRealm()).thenReturn(realm); + when(ancestor.getAccountPolicy()).thenReturn(ancestorPolicy); + when(realmSearchDAO.findAncestors(realm)).thenReturn(List.of(ancestor)); + when(resourceDAO.findByPolicy(ancestorPolicy)).thenReturn(List.of(second)); + + assertEquals(Set.of(second), authDataAccessor.getPassthroughResources(user)); + } + + @Test + void authenticationFallsBackToPassthroughResource() { + User user = user("bob"); + doReturn(Optional.of(user)).when(userDAO).findByUsername("bob"); + when(userDAO.save(user)).thenReturn(user); + when(encryptor.verify(any(), any(), any())).thenReturn(false); + + ExternalResource resource = mock(ExternalResource.class); + when(resource.getKey()).thenReturn("resource"); + AccountPolicy policy = mock(AccountPolicy.class); + when(resource.getAccountPolicy()).thenReturn(policy); + when(userDAO.findAllResources(user)).thenReturn(List.of(resource)); + when(resourceDAO.findByPolicy(policy)).thenReturn(List.of(resource)); + + Provision provision = mock(Provision.class); + when(resource.getProvisionByAnyType(AnyTypeKind.USER.name())).thenReturn(Optional.of(provision)); + when(mappingManager.getConnObjectKeyValue(user, resource, provision)). + thenReturn(Optional.of("conn-key")); + Connector connector = mock(Connector.class); + when(connectorManager.getConnector(resource)).thenReturn(connector); + when(connector.authenticate("conn-key", "password", null)).thenReturn(new Uid("uid")); + + AuthDataAccessor.UsernamePasswordAuthResult result = authDataAccessor.authenticate( + Optional.empty(), usernamePassword("bob", "password")); + + assertTrue(result.passwordVerified()); + assertTrue(result.isSuccess()); + verify(connector).authenticate("conn-key", "password", null); + } + + @Test + void passthroughResourceWithoutProvisionLeavesAuthenticationFailed() { + User user = user("bob"); + doReturn(Optional.of(user)).when(userDAO).findByUsername("bob"); + when(userDAO.save(user)).thenReturn(user); + when(encryptor.verify(any(), any(), any())).thenReturn(false); + + ExternalResource resource = mock(ExternalResource.class); + when(resource.getKey()).thenReturn("resource"); + AccountPolicy policy = mock(AccountPolicy.class); + when(resource.getAccountPolicy()).thenReturn(policy); + when(userDAO.findAllResources(user)).thenReturn(List.of(resource)); + when(resourceDAO.findByPolicy(policy)).thenReturn(List.of(resource)); + when(resource.getProvisionByAnyType(AnyTypeKind.USER.name())).thenReturn(Optional.empty()); + + AuthDataAccessor.UsernamePasswordAuthResult result = authDataAccessor.authenticate( + Optional.empty(), usernamePassword("bob", "password")); + + assertFalse(result.passwordVerified()); + assertFalse(result.isSuccess()); + verify(connectorManager, never()).getConnector(any()); + } + + @Test + void userWhoMustChangePasswordGetsOnlyThatAuthority() { + User user = user("bob"); + when(user.isMustChangePassword()).thenReturn(true); + + assertEquals( + Set.of(IdRepoEntitlement.MUST_CHANGE_PASSWORD), + entitlements(authDataAccessor.getUserAuthorities(user))); + } + + @Test + void managedEntitiesGrantManagerEntitlements() { + User user = user("bob"); + + Realm managedUserRealm = mock(Realm.class); + when(managedUserRealm.getFullPath()).thenReturn("/users"); + User managedUser = user("managed"); + when(managedUser.getRealm()).thenReturn(managedUserRealm); + when(userDAO.findManagedUsers("bob-key")).thenReturn(List.of(managedUser)); + + Realm managedGroupRealm = mock(Realm.class); + when(managedGroupRealm.getFullPath()).thenReturn("/groups"); + Group managedGroup = mock(Group.class); + when(managedGroup.getKey()).thenReturn("group-key"); + when(managedGroup.getRealm()).thenReturn(managedGroupRealm); + when(userDAO.findManagedGroups("bob-key")).thenReturn(List.of(managedGroup)); + + AnyType anyType = mock(AnyType.class); + when(anyType.getKey()).thenReturn("PRINTER"); + Realm managedAnyObjectRealm = mock(Realm.class); + when(managedAnyObjectRealm.getFullPath()).thenReturn("/printers"); + AnyObject managedAnyObject = mock(AnyObject.class); + when(managedAnyObject.getKey()).thenReturn("any-key"); + when(managedAnyObject.getType()).thenReturn(anyType); + when(managedAnyObject.getRealm()).thenReturn(managedAnyObjectRealm); + when(userDAO.findManagedAnyObjects("bob-key")).thenReturn(List.of(managedAnyObject)); + + Set authorities = authDataAccessor.getUserAuthorities(user); + + assertContains(authorities, IdRepoEntitlement.USER_READ); + assertContains(authorities, IdRepoEntitlement.GROUP_READ); + assertContains(authorities, "PRINTER_READ"); + assertContains(authorities, IdRepoEntitlement.ANYTYPE_READ); + } + + @Test + void anonymousUsernameGetsAnonymousAuthorities() { + assertEquals( + Set.of(IdRepoEntitlement.ANONYMOUS), + entitlements(authDataAccessor.getAuthorities(ANONYMOUS, null))); + } + + @Test + void adminUsernameGetsAllAuthorities() { + assertContains(authDataAccessor.getAuthorities(ADMIN, null), IdRepoEntitlement.USER_READ); + } + + @Test + void unknownDelegationThrowsUsernameNotFound() { + doReturn(Optional.empty()).when(delegationDAO).findById("delegation-key"); + + UsernameNotFoundException ex = assertThrows( + UsernameNotFoundException.class, + () -> authDataAccessor.getAuthorities("bob", "delegation-key")); + assertEquals("Could not find delegation delegation-key", ex.getMessage()); + } + + @Test + void delegationWithoutRolesUsesDelegatingUserAuthorities() { + User delegating = user("bob"); + Role userRole = role(IdRepoEntitlement.USER_READ, SyncopeConstants.ROOT_REALM); + doReturn(List.of(userRole)).when(userDAO).findAllRoles(delegating); + + Delegation delegation = mock(Delegation.class); + doReturn(Set.of()).when(delegation).getRoles(); + when(delegation.getDelegating()).thenReturn(delegating); + doReturn(Optional.of(delegation)).when(delegationDAO).findById("delegation-key"); + + assertContains( + authDataAccessor.getAuthorities("bob", "delegation-key"), + IdRepoEntitlement.USER_READ); + } + + @Test + void delegationWithRolesUsesDelegatedAuthorities() { + Delegation delegation = mock(Delegation.class); + doReturn(Set.of(role(IdRepoEntitlement.GROUP_READ, "/groups"))).when(delegation).getRoles(); + doReturn(Optional.of(delegation)).when(delegationDAO).findById("delegation-key"); + + assertContains( + authDataAccessor.getAuthorities("bob", "delegation-key"), + IdRepoEntitlement.GROUP_READ); + } + + @Test + void unknownUsernameForAuthoritiesThrowsUsernameNotFound() { + doReturn(Optional.empty()).when(userDAO).findByUsername("bob"); + + UsernameNotFoundException ex = assertThrows( + UsernameNotFoundException.class, + () -> authDataAccessor.getAuthorities("bob", null)); + assertEquals("Could not find any user with username bob", ex.getMessage()); + } + + @Test + void existingUsernameGetsUserAuthorities() { + User user = user("bob"); + doReturn(Optional.of(user)).when(userDAO).findByUsername("bob"); + Role userRole = role(IdRepoEntitlement.USER_READ, SyncopeConstants.ROOT_REALM); + doReturn(List.of(userRole)).when(userDAO).findAllRoles(user); + + assertContains(authDataAccessor.getAuthorities("bob", null), IdRepoEntitlement.USER_READ); + } + + @Test + void jwtAdminWithoutAccessTokenThrows() { + JWTAuthentication authentication = jwt(ADMIN, securityProperties.getJwtIssuer(), "jti", null); + doReturn(Optional.empty()).when(accessTokenDAO).findById("jti"); + + AuthenticationCredentialsNotFoundException ex = assertThrows( + AuthenticationCredentialsNotFoundException.class, + () -> authDataAccessor.authenticate(authentication)); + assertEquals("Could not find an Access Token for JWT jti", ex.getMessage()); + } + + @Test + void jwtAdminWithMatchingAccessTokenGetsAllAuthorities() { + AccessToken accessToken = mock(AccessToken.class); + when(accessToken.getOwner()).thenReturn(ADMIN); + doReturn(Optional.of(accessToken)).when(accessTokenDAO).findById("jti"); + + AuthDataAccessor.JWTAuthResult result = authDataAccessor.authenticate( + jwt(ADMIN, securityProperties.getJwtIssuer(), "jti", null)); + + assertEquals(ADMIN, result.username()); + assertContains(result.authorities(), IdRepoEntitlement.USER_READ); + } + + @Test + void jwtWithUnknownIssuerThrows() { + JWTAuthentication authentication = jwt("bob", "unknown", "jti", null); + + assertThrows( + AuthenticationCredentialsNotFoundException.class, + () -> authDataAccessor.authenticate(authentication)); + } + + @Test + void jwtNotResolvableToUserThrows() { + JWTSSOProvider provider = mock(JWTSSOProvider.class); + when(provider.getIssuer()).thenReturn("issuer"); + when(provider.resolve(any())).thenReturn(Optional.empty()); + AuthDataAccessor accessor = newAccessor(provider); + + AuthenticationCredentialsNotFoundException ex = assertThrows( + AuthenticationCredentialsNotFoundException.class, + () -> accessor.authenticate(jwt("bob", "issuer", "jti", null))); + assertEquals("Could not find User bob for JWT jti", ex.getMessage()); + } + + @Test + void jwtForSuspendedUserIsRejected() { + User user = user("bob"); + when(user.isSuspended()).thenReturn(true); + JWTSSOProvider provider = providerResolving(user, Set.of()); + + DisabledException ex = assertThrows( + DisabledException.class, + () -> newAccessor(provider).authenticate(jwt("bob", "issuer", "jti", null))); + assertEquals("User bob is suspended", ex.getMessage()); + } + + @Test + void jwtForUserWithDisallowedStatusIsRejected() { + User user = user("bob"); + when(user.getStatus()).thenReturn("inactive"); + authenticationStatuses("active"); + JWTSSOProvider provider = providerResolving(user, Set.of()); + + DisabledException ex = assertThrows( + DisabledException.class, + () -> newAccessor(provider).authenticate(jwt("bob", "issuer", "jti", null))); + assertEquals("User bob not allowed to authenticate", ex.getMessage()); + } + + @Test + void jwtForUserWhoMustChangePasswordResetsAuthorities() { + User user = user("bob"); + when(user.isMustChangePassword()).thenReturn(true); + JWTSSOProvider provider = providerResolving( + user, Set.of(new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ))); + + AuthDataAccessor.JWTAuthResult result = newAccessor(provider). + authenticate(jwt("bob", "issuer", "jti", null)); + + assertEquals(Set.of(IdRepoEntitlement.MUST_CHANGE_PASSWORD), entitlements(result.authorities())); + } + + @Test + void jwtWithNullResolvedAuthoritiesYieldsEmptyAuthorities() { + JWTSSOProvider provider = providerResolving(user("bob"), null); + + AuthDataAccessor.JWTAuthResult result = newAccessor(provider). + authenticate(jwt("bob", "issuer", "jti", null)); + + assertTrue(result.authorities().isEmpty()); + } + + @Test + void jwtWithResolvedAuthoritiesPreservesThem() { + Set resolved = + Set.of(new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ)); + JWTSSOProvider provider = providerResolving(user("bob"), resolved); + + AuthDataAccessor.JWTAuthResult result = newAccessor(provider). + authenticate(jwt("bob", "issuer", "jti", null)); + + assertEquals(Set.of(IdRepoEntitlement.USER_READ), entitlements(result.authorities())); + } + + @Test + void jwtWithDelegationUsesDelegatedAuthorities() { + User user = user("bob"); + JWTSSOProvider provider = providerResolving( + user, Set.of(new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ))); + + when(userDAO.findKey("delegator")).thenReturn(Optional.of("delegator-key")); + when(delegationDAO.findValidFor(eq("delegator-key"), eq("bob-key"), any())). + thenReturn(Optional.of("delegation-key")); + Delegation delegation = mock(Delegation.class); + doReturn(Set.of(role(IdRepoEntitlement.GROUP_READ, SyncopeConstants.ROOT_REALM))).when(delegation).getRoles(); + doReturn(Optional.of(delegation)).when(delegationDAO).findById("delegation-key"); - Constructor constructor = Arrays.stream(AuthDataAccessor.class.getConstructors()). - findFirst().orElseThrow(); - Object[] arguments = Arrays.stream(constructor.getParameterTypes()).map(type -> { - if (type == SecurityProperties.class) { - return securityProperties; - } - if (type == AccessTokenDAO.class) { - return accessTokenDAO; - } - if (type == List.class) { - return List.of(); - } - return mock(type); - }).toArray(); + AuthDataAccessor.JWTAuthResult result = newAccessor(provider). + authenticate(jwt("bob", "issuer", "jti", "delegator")); - authDataAccessor = (AuthDataAccessor) constructor.newInstance(arguments); + assertEquals("bob", result.username()); + assertEquals(Set.of(IdRepoEntitlement.GROUP_READ), entitlements(result.authorities())); + } + + private JWTSSOProvider providerResolving( + final User user, final Set authorities) { + + JWTSSOProvider provider = mock(JWTSSOProvider.class); + when(provider.getIssuer()).thenReturn("issuer"); + when(provider.resolve(any())). + thenReturn(Optional.of(new JWTSSOProvider.ResolvedClaims(user, authorities))); + return provider; } @Test @@ -110,9 +1026,64 @@ void lowPrivilegeTokenIdCanBeReboundToAdminWithDefaultSigningKey() throws Except forged.getJWTClaimsSet(), new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, null)); + AccessToken lowPrivilegeToken = mock(AccessToken.class); + when(lowPrivilegeToken.getOwner()).thenReturn(LOW_PRIVILEGE_USER); + doReturn(Optional.of(lowPrivilegeToken)).when(accessTokenDAO).findById(LOW_PRIVILEGE_JTI); + AuthenticationCredentialsNotFoundException ex = assertThrows( AuthenticationCredentialsNotFoundException.class, () -> authDataAccessor.authenticate(authentication)); assertEquals("Access Token owner does not match JWT subject", ex.getMessage()); } + + @Test + void removeExpiredDeletesAccessToken() { + authDataAccessor.removeExpired("token-key"); + + verify(accessTokenDAO).deleteById("token-key"); + } + + @Test + void auditAppendsDelegationKeyToUsername() { + authDataAccessor.audit( + SyncopeConstants.MASTER_DOMAIN, + "bob", + "delegation-key", + OpEvent.Outcome.SUCCESS, + "output", + "input"); + + verify(auditManager).audit( + eq(SyncopeConstants.MASTER_DOMAIN), + eq("bob [under delegation delegation-key]"), + eq(OpEvent.CategoryType.LOGIC), + eq(OpEvent.AUTHENTICATION_CATEGORY), + isNull(), + eq(OpEvent.LOGIN_OP), + eq(OpEvent.Outcome.SUCCESS), + isNull(), + eq("output"), + eq((Object) "input")); + } + + @Test + void auditWithoutDelegationKeepsUsername() { + authDataAccessor.audit( + SyncopeConstants.MASTER_DOMAIN, + "bob", + null, + OpEvent.Outcome.FAILURE, + "output"); + + verify(auditManager).audit( + eq(SyncopeConstants.MASTER_DOMAIN), + eq("bob"), + eq(OpEvent.CategoryType.LOGIC), + eq(OpEvent.AUTHENTICATION_CATEGORY), + isNull(), + eq(OpEvent.LOGIN_OP), + eq(OpEvent.Outcome.FAILURE), + isNull(), + eq("output")); + } } diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/DefaultCredentialCheckerTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/DefaultCredentialCheckerTest.java new file mode 100644 index 00000000000..1901533048f --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/DefaultCredentialCheckerTest.java @@ -0,0 +1,112 @@ +/* + * 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.core.spring.security; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import org.junit.jupiter.api.Test; + +class DefaultCredentialCheckerTest { + + private static final String DEFAULT_AES_KEY = "1abcdefghilmnopqrstuvz2!"; + + private static final String DEFAULT_JWS_KEY = + "ZW7pRixehFuNUtnY5Se47IemgMryTzazPPJ9CGX5LTCmsOJpOgHAQEuPQeV9A28f"; + + private static final String DEFAULT_ADMIN_PASSWORD = + "DE088591C00CC98B36F5ADAAF7DA2B004CF7F2FE7BBB45B766B6409876E2F3DB13C7905C6AA59464"; + + private static final String DEFAULT_ANONYMOUS_KEY = "anonymousKey"; + + private static DefaultCredentialChecker withDefaultCredentials(final boolean productionMode) throws IOException { + return new DefaultCredentialChecker( + DEFAULT_AES_KEY, + DEFAULT_JWS_KEY, + DEFAULT_ADMIN_PASSWORD, + DEFAULT_ANONYMOUS_KEY, + productionMode); + } + + @Test + void defaultAesKeyRejectedInProductionMode() throws IOException { + DefaultCredentialChecker checker = withDefaultCredentials(true); + + IllegalStateException ex = assertThrows(IllegalStateException.class, checker::checkIsDefaultAESKeyInUse); + assertTrue(ex.getMessage().contains("default AES key")); + } + + @Test + void defaultJwsKeyRejectedInProductionMode() throws IOException { + DefaultCredentialChecker checker = withDefaultCredentials(true); + + IllegalStateException ex = assertThrows(IllegalStateException.class, checker::checkIsDefaultJWSKeyInUse); + assertTrue(ex.getMessage().contains("default JWKS key")); + } + + @Test + void defaultAdminPasswordRejectedInProductionMode() throws IOException { + DefaultCredentialChecker checker = withDefaultCredentials(true); + + IllegalStateException ex = + assertThrows(IllegalStateException.class, checker::checkIsDefaultAdminPasswordInUse); + assertTrue(ex.getMessage().contains("default adminPassword")); + } + + @Test + void defaultAnonymousKeyRejectedInProductionMode() throws IOException { + DefaultCredentialChecker checker = withDefaultCredentials(true); + + IllegalStateException ex = assertThrows(IllegalStateException.class, checker::checkIsDefaultAnonymousKeyInUse); + assertTrue(ex.getMessage().contains("default anonymousKey")); + } + + @Test + void defaultCredentialsAcceptedOutsideProductionMode() throws IOException { + DefaultCredentialChecker checker = withDefaultCredentials(false); + + assertDoesNotThrow(checker::checkIsDefaultAESKeyInUse); + assertDoesNotThrow(checker::checkIsDefaultJWSKeyInUse); + assertDoesNotThrow(checker::checkIsDefaultAdminPasswordInUse); + assertDoesNotThrow(checker::checkIsDefaultAnonymousKeyInUse); + } + + @Test + void customCredentialsAcceptedInProductionMode() throws IOException { + DefaultCredentialChecker checker = new DefaultCredentialChecker( + "custom-aes", "custom-jws", "custom-admin", "custom-anonymous", true); + + assertDoesNotThrow(checker::checkIsDefaultAESKeyInUse); + assertDoesNotThrow(checker::checkIsDefaultJWSKeyInUse); + assertDoesNotThrow(checker::checkIsDefaultAdminPasswordInUse); + assertDoesNotThrow(checker::checkIsDefaultAnonymousKeyInUse); + } + + @Test + void emptyCredentialsAreNotConsideredDefault() throws IOException { + DefaultCredentialChecker checker = new DefaultCredentialChecker("", "", "", "", true); + + assertDoesNotThrow(checker::checkIsDefaultAESKeyInUse); + assertDoesNotThrow(checker::checkIsDefaultJWSKeyInUse); + assertDoesNotThrow(checker::checkIsDefaultAdminPasswordInUse); + assertDoesNotThrow(checker::checkIsDefaultAnonymousKeyInUse); + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/DefaultEncryptorManagerTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/DefaultEncryptorManagerTest.java new file mode 100644 index 00000000000..9ff21f0cbca --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/DefaultEncryptorManagerTest.java @@ -0,0 +1,77 @@ +/* + * 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.core.spring.security; + +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import org.apache.syncope.core.persistence.api.Encryptor; +import org.apache.syncope.core.spring.SpringTestConfiguration; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class DefaultEncryptorManagerTest { + + private static final String OTHER_KEY = "2bcdefghilmnopqr"; + + private DefaultCredentialChecker credentialChecker; + + private DefaultEncryptorManager manager; + + @BeforeEach + void setUp() { + credentialChecker = mock(DefaultCredentialChecker.class); + + SecurityProperties securityProperties = new SecurityProperties(); + securityProperties.setAesSecretKey(SpringTestConfiguration.AES_SECRET_KEY); + securityProperties.setProductionMode(false); + + manager = new DefaultEncryptorManager(credentialChecker, securityProperties); + } + + @Test + void configuredKeyIsUsedAndCached() { + Encryptor first = manager.getInstance(); + Encryptor second = manager.getInstance(); + + assertSame(first, second); + verify(credentialChecker, times(2)).checkIsDefaultAESKeyInUse(); + } + + @Test + void explicitKeyIsUsedAndCachedSeparately() { + Encryptor configured = manager.getInstance(); + Encryptor explicit = manager.getInstance(OTHER_KEY); + Encryptor explicitAgain = manager.getInstance(OTHER_KEY); + + assertSame(explicit, explicitAgain); + assertNotSame(configured, explicit); + } + + @Test + void blankKeyFallsBackToConfiguredKey() { + Encryptor configured = manager.getInstance(); + Encryptor blank = manager.getInstance(" "); + + assertSame(configured, blank); + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/DefaultEncryptorTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/DefaultEncryptorTest.java index 07286aea445..465a3b26a5f 100644 --- a/core/spring/src/test/java/org/apache/syncope/core/spring/security/DefaultEncryptorTest.java +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/DefaultEncryptorTest.java @@ -21,19 +21,23 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.security.InvalidKeyException; +import java.util.List; import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.apache.syncope.core.persistence.api.ApplicationContextProvider; import org.apache.syncope.core.persistence.api.Encryptor; import org.apache.syncope.core.spring.SpringTestConfiguration; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.support.StaticApplicationContext; -public class DefaultEncryptorTest { +class DefaultEncryptorTest { private static final String PASSWORD_VALUE = "password"; @@ -43,7 +47,18 @@ public class DefaultEncryptorTest { public static void setUp() throws IOException { SecurityProperties props = new SecurityProperties(); props.setAesSecretKey(SpringTestConfiguration.AES_SECRET_KEY); - ApplicationContextProvider.getBeanFactory().registerSingleton("securityProperties", props); + + DefaultListableBeanFactory beanFactory = ApplicationContextProvider.getBeanFactory(); + if (beanFactory == null) { + StaticApplicationContext context = new StaticApplicationContext(); + context.refresh(); + ApplicationContextProvider.setApplicationContext(context); + beanFactory = (DefaultListableBeanFactory) context.getBeanFactory(); + ApplicationContextProvider.setBeanFactory(beanFactory); + } + if (!beanFactory.containsSingleton("securityProperties")) { + beanFactory.registerSingleton("securityProperties", props); + } SecurityProperties securityProperties = new SecurityProperties(); securityProperties.setAesSecretKey(SpringTestConfiguration.AES_SECRET_KEY); @@ -53,7 +68,7 @@ public static void setUp() throws IOException { } @Test - public void encoder() throws Exception { + void encoder() throws Exception { for (CipherAlgorithm cipherAlgorithm : CipherAlgorithm.values()) { String encPassword = ENCRYPTOR.encode(PASSWORD_VALUE, cipherAlgorithm); @@ -72,13 +87,13 @@ public void encoder() throws Exception { } @Test - public void decodeDefaultAESKey() throws Exception { + void decodeDefaultAESKey() throws Exception { String decPassword = ENCRYPTOR.decode("9Pav+xl+UyHt02H9ZBytiA==", CipherAlgorithm.AES); assertEquals(PASSWORD_VALUE, decPassword); } @Test - public void smallKey() throws Exception { + void smallKey() throws Exception { DefaultEncryptor prodModeEncryptor = new DefaultEncryptor("123", true, new SecurityProperties().getDigester()); assertThrows(InvalidKeyException.class, () -> prodModeEncryptor.encode(PASSWORD_VALUE, CipherAlgorithm.AES)); @@ -89,7 +104,7 @@ public void smallKey() throws Exception { } @Test - public void saltedHash() throws Exception { + void saltedHash() throws Exception { String encPassword = ENCRYPTOR.encode(PASSWORD_VALUE, CipherAlgorithm.SSHA256); assertNotNull(encPassword); @@ -97,11 +112,100 @@ public void saltedHash() throws Exception { } @Test - public void verifySaltedFromExternal() throws Exception { + void verifySaltedFromExternal() throws Exception { // generated via https://github.com/peppelinux/pySSHA-slapd with command: // python3 pySSHA/ssha.py -p password -enc sha256 -s 666ac543 \ // | sed 's/{.*}//' | xargs echo -n | base64 -d | xxd -p | tr -d $'\n' | xargs echo String encPassword = "b098017d584647e3fa1f3e0eb437648aefa84093c15e0d3efb752a4183cfdcf3666ac543"; assertTrue(ENCRYPTOR.verify(PASSWORD_VALUE, CipherAlgorithm.SSHA256, encPassword)); } + + @Test + void encodeNullValue() throws Exception { + assertNull(ENCRYPTOR.encode(null, CipherAlgorithm.AES)); + assertNull(ENCRYPTOR.encode(null, CipherAlgorithm.SHA256)); + assertNull(ENCRYPTOR.encode(null, null)); + } + + @Test + void encodeNullCipherAlgorithmUsesAES() throws Exception { + String encPassword = ENCRYPTOR.encode(PASSWORD_VALUE, null); + + assertNotNull(encPassword); + assertEquals(PASSWORD_VALUE, ENCRYPTOR.decode(encPassword, CipherAlgorithm.AES)); + assertTrue(ENCRYPTOR.verify(PASSWORD_VALUE, null, encPassword)); + assertFalse(ENCRYPTOR.verify(PASSWORD_VALUE + "diff", null, encPassword)); + } + + @Test + void decodeNullEncodedAndNonAesAlgorithm() throws Exception { + assertNull(ENCRYPTOR.decode(null, CipherAlgorithm.AES)); + assertNull(ENCRYPTOR.decode("whatever", CipherAlgorithm.SHA256)); + assertNull(ENCRYPTOR.decode(null, CipherAlgorithm.SHA256)); + } + + @Test + void verifyNullValue() throws Exception { + String encPassword = ENCRYPTOR.encode(PASSWORD_VALUE, CipherAlgorithm.AES); + + assertFalse(ENCRYPTOR.verify(null, CipherAlgorithm.AES, encPassword)); + assertFalse(ENCRYPTOR.verify(null, CipherAlgorithm.SHA256, encPassword)); + assertFalse(ENCRYPTOR.verify(null, CipherAlgorithm.BCRYPT, encPassword)); + } + + @Test + void verifyMalformedEncodedValue() { + assertFalse(ENCRYPTOR.verify(PASSWORD_VALUE, CipherAlgorithm.BCRYPT, "not-a-bcrypt-hash")); + assertFalse(ENCRYPTOR.verify(PASSWORD_VALUE, CipherAlgorithm.AES, null)); + } + + @Test + void aesNotConfigured() { + DefaultEncryptor nullKeyEncryptor = new DefaultEncryptor( + null, true, new SecurityProperties().getDigester()); + assertThrows(IllegalArgumentException.class, + () -> nullKeyEncryptor.encode(PASSWORD_VALUE, CipherAlgorithm.AES)); + assertThrows(IllegalArgumentException.class, + () -> nullKeyEncryptor.decode("AAAA", CipherAlgorithm.AES)); + assertFalse(nullKeyEncryptor.verify(PASSWORD_VALUE, CipherAlgorithm.AES, "AAAA")); + + DefaultEncryptor blankKeyEncryptor = new DefaultEncryptor( + " ", true, new SecurityProperties().getDigester()); + assertThrows(IllegalArgumentException.class, + () -> blankKeyEncryptor.encode(PASSWORD_VALUE, CipherAlgorithm.AES)); + assertThrows(IllegalArgumentException.class, + () -> blankKeyEncryptor.decode("AAAA", CipherAlgorithm.AES)); + } + + @Test + void nonProductionAesKeyIsNormalized() throws Exception { + List keys = List.of( + "1234567890", + "1234567890123456", + "12345678901234567890", + "123456789012345678901234", + "123456789012345678901234567890", + "12345678901234567890123456789012", + "1234567890123456789012345678901234567890"); + + for (String key : keys) { + DefaultEncryptor encryptor = new DefaultEncryptor( + key, false, new SecurityProperties().getDigester()); + + String encPassword = encryptor.encode(PASSWORD_VALUE, CipherAlgorithm.AES); + assertEquals(PASSWORD_VALUE, encryptor.decode(encPassword, CipherAlgorithm.AES)); + } + } + + @Test + void productionModeDoesNotNormalizeAesKey() throws Exception { + DefaultEncryptor encryptor = new DefaultEncryptor( + "1234567890123456", true, new SecurityProperties().getDigester()); + + String encPassword = encryptor.encode(PASSWORD_VALUE, CipherAlgorithm.AES); + assertEquals(PASSWORD_VALUE, encryptor.decode(encPassword, CipherAlgorithm.AES)); + assertThrows(InvalidKeyException.class, + () -> new DefaultEncryptor("12345678901234567890", true, new SecurityProperties().getDigester()). + encode(PASSWORD_VALUE, CipherAlgorithm.AES)); + } } diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/JWTAuthenticationFilterTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/JWTAuthenticationFilterTest.java new file mode 100644 index 00000000000..c3894ebb1d5 --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/JWTAuthenticationFilterTest.java @@ -0,0 +1,284 @@ +/* + * 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.core.spring.security; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import jakarta.servlet.FilterChain; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.text.ParseException; +import java.util.Date; +import java.util.Set; +import org.apache.syncope.common.lib.types.EntitlementsHolder; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.persistence.api.ApplicationContextProvider; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.StaticApplicationContext; +import org.springframework.http.HttpHeaders; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.CredentialsExpiredException; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.AuthenticationEntryPoint; + +class JWTAuthenticationFilterTest { + + private static final String TOKEN = "header.payload.signature"; + + private final AuthenticationManager authenticationManager = mock(AuthenticationManager.class); + + private final AuthenticationEntryPoint authenticationEntryPoint = mock(AuthenticationEntryPoint.class); + + private final AuthDataAccessor dataAccessor = mock(AuthDataAccessor.class); + + private final DefaultCredentialChecker credentialChecker = mock(DefaultCredentialChecker.class); + + private final JWTSSOProvider jwtSSOProvider = mock(JWTSSOProvider.class); + + private StaticApplicationContext context; + + private JWTAuthenticationFilter filter; + + private HttpServletRequest request; + + private HttpServletResponse response; + + private FilterChain chain; + + private ConfigurableApplicationContext previousContext; + + private DefaultListableBeanFactory previousBeanFactory; + + @BeforeEach + void setUp() { + previousContext = ApplicationContextProvider.getApplicationContext(); + previousBeanFactory = ApplicationContextProvider.getBeanFactory(); + + SecurityContextHolder.clearContext(); + EntitlementsHolder.getInstance().addAll(IdRepoEntitlement.values()); + + SecurityProperties securityProperties = new SecurityProperties(); + securityProperties.setAdminUser("admin"); + + context = new StaticApplicationContext(); + context.getBeanFactory().registerSingleton("securityProperties", securityProperties); + context.refresh(); + + ApplicationContextProvider.setApplicationContext(context); + ApplicationContextProvider.setBeanFactory((DefaultListableBeanFactory) context.getBeanFactory()); + + filter = new JWTAuthenticationFilter( + authenticationManager, + authenticationEntryPoint, + new SyncopeAuthenticationDetailsSource(), + dataAccessor, + credentialChecker); + + request = mock(HttpServletRequest.class); + response = mock(HttpServletResponse.class); + chain = mock(FilterChain.class); + + when(dataAccessor.getJWTSSOProvider(any())).thenReturn(jwtSSOProvider); + } + + @AfterEach + void tearDown() { + SecurityContextHolder.clearContext(); + ApplicationContextProvider.setApplicationContext(previousContext); + ApplicationContextProvider.setBeanFactory(previousBeanFactory); + context.close(); + } + + private void bearerToken() { + when(request.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn("Bearer " + TOKEN); + } + + private JWTClaimsSet.Builder claims() { + return new JWTClaimsSet.Builder(). + issuer("issuer"). + subject("bob"). + jwtID("jti"); + } + + private SignedJWT signedJWT(final JWTClaimsSet claims) throws Exception { + SignedJWT jwt = mock(SignedJWT.class); + when(jwt.getJWTClaimsSet()).thenReturn(claims); + when(jwt.verify(jwtSSOProvider)).thenReturn(true); + return jwt; + } + + @Test + void noAuthorizationHeaderPassesThrough() throws Exception { + filter.doFilterInternal(request, response, chain); + + verify(chain).doFilter(request, response); + verify(authenticationEntryPoint, never()).commence(any(), any(), any()); + assertNull(SecurityContextHolder.getContext().getAuthentication()); + } + + @Test + void nonBearerAuthorizationPassesThrough() throws Exception { + when(request.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn("Basic abc"); + + filter.doFilterInternal(request, response, chain); + + verify(chain).doFilter(request, response); + assertNull(SecurityContextHolder.getContext().getAuthentication()); + } + + @Test + void malformedTokenIsRejected() throws Exception { + bearerToken(); + + try (MockedStatic mocked = mockStatic(SignedJWT.class)) { + mocked.when(() -> SignedJWT.parse(TOKEN)).thenThrow(new ParseException("bad token", 0)); + + filter.doFilterInternal(request, response, chain); + } + + verify(chain, never()).doFilter(any(), any()); + verify(authenticationEntryPoint).commence(eq(request), eq(response), any(BadCredentialsException.class)); + assertNull(SecurityContextHolder.getContext().getAuthentication()); + } + + @Test + void invalidSignatureIsRejected() throws Exception { + bearerToken(); + JWTClaimsSet claims = claims().build(); + SignedJWT jwt = mock(SignedJWT.class); + when(jwt.getJWTClaimsSet()).thenReturn(claims); + when(jwt.verify(jwtSSOProvider)).thenReturn(false); + + try (MockedStatic mocked = mockStatic(SignedJWT.class)) { + mocked.when(() -> SignedJWT.parse(TOKEN)).thenReturn(jwt); + + filter.doFilterInternal(request, response, chain); + } + + verify(chain, never()).doFilter(any(), any()); + verify(authenticationEntryPoint).commence(eq(request), eq(response), any(BadCredentialsException.class)); + assertNull(SecurityContextHolder.getContext().getAuthentication()); + } + + @Test + void expiredTokenIsRemovedAndRejected() throws Exception { + bearerToken(); + JWTClaimsSet claims = claims(). + expirationTime(new Date(System.currentTimeMillis() - 60_000)). + build(); + SignedJWT jwt = signedJWT(claims); + + try (MockedStatic mocked = mockStatic(SignedJWT.class)) { + mocked.when(() -> SignedJWT.parse(TOKEN)).thenReturn(jwt); + + filter.doFilterInternal(request, response, chain); + } + + verify(dataAccessor).removeExpired("jti"); + verify(chain, never()).doFilter(any(), any()); + verify(authenticationEntryPoint).commence( + eq(request), eq(response), any(CredentialsExpiredException.class)); + assertNull(SecurityContextHolder.getContext().getAuthentication()); + } + + @Test + void notYetValidTokenIsRejected() throws Exception { + bearerToken(); + JWTClaimsSet claims = claims(). + notBeforeTime(new Date(System.currentTimeMillis() + 60_000)). + build(); + SignedJWT jwt = signedJWT(claims); + + try (MockedStatic mocked = mockStatic(SignedJWT.class)) { + mocked.when(() -> SignedJWT.parse(TOKEN)).thenReturn(jwt); + + filter.doFilterInternal(request, response, chain); + } + + verify(dataAccessor, never()).removeExpired(any()); + verify(chain, never()).doFilter(any(), any()); + verify(authenticationEntryPoint).commence( + eq(request), eq(response), any(CredentialsExpiredException.class)); + } + + @Test + void validTokenSetsAuthentication() throws Exception { + bearerToken(); + JWTClaimsSet claims = claims().build(); + SignedJWT jwt = signedJWT(claims); + + SyncopeGrantedAuthority authority = new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ); + when(dataAccessor.authenticate(any())).thenReturn( + new AuthDataAccessor.JWTAuthResult("alice", Set.of(authority))); + + try (MockedStatic mocked = mockStatic(SignedJWT.class)) { + mocked.when(() -> SignedJWT.parse(TOKEN)).thenReturn(jwt); + + filter.doFilterInternal(request, response, chain); + } + + verify(chain).doFilter(request, response); + verify(authenticationEntryPoint, never()).commence(any(), any(), any()); + + JWTAuthentication authentication = + assertInstanceOf(JWTAuthentication.class, SecurityContextHolder.getContext().getAuthentication()); + assertTrue(authentication.isAuthenticated()); + assertEquals("alice", authentication.getName()); + assertEquals(Set.of(authority), authentication.getAuthorities()); + assertEquals("jti", authentication.getClaims().getJWTID()); + } + + @Test + void authenticationFailureCommencesEntryPoint() throws Exception { + bearerToken(); + JWTClaimsSet claims = claims().build(); + SignedJWT jwt = signedJWT(claims); + when(dataAccessor.authenticate(any())).thenThrow(new BadCredentialsException("no")); + + try (MockedStatic mocked = mockStatic(SignedJWT.class)) { + mocked.when(() -> SignedJWT.parse(TOKEN)).thenReturn(jwt); + + filter.doFilterInternal(request, response, chain); + } + + verify(chain, never()).doFilter(any(), any()); + verify(authenticationEntryPoint).commence( + eq(request), eq(response), any(AuthenticationException.class)); + assertNull(SecurityContextHolder.getContext().getAuthentication()); + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/JWTAuthenticationTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/JWTAuthenticationTest.java new file mode 100644 index 00000000000..0243fd5fc25 --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/JWTAuthenticationTest.java @@ -0,0 +1,119 @@ +/* + * 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.core.spring.security; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.nimbusds.jwt.JWTClaimsSet; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.junit.jupiter.api.Test; + +class JWTAuthenticationTest { + + private static JWTClaimsSet claims(final String subject) { + return new JWTClaimsSet.Builder(). + issuer("issuer"). + subject(subject). + jwtID("jti"). + build(); + } + + private static SyncopeAuthenticationDetails details() { + return new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, "delegator"); + } + + @Test + void initialState() { + JWTClaimsSet claims = claims("bob"); + SyncopeAuthenticationDetails details = details(); + JWTAuthentication authentication = new JWTAuthentication(claims, details); + + assertSame(claims, authentication.getClaims()); + assertSame(details, authentication.getDetails()); + assertEquals("", authentication.getCredentials()); + assertEquals("bob", authentication.getPrincipal()); + assertEquals("bob", authentication.getName()); + assertFalse(authentication.isAuthenticated()); + assertTrue(authentication.getAuthorities().isEmpty()); + } + + @Test + void setUsername() { + JWTAuthentication authentication = new JWTAuthentication(claims("bob"), details()); + + authentication.setUsername("alice"); + + assertEquals("alice", authentication.getPrincipal()); + assertEquals("alice", authentication.getName()); + } + + @Test + void authoritiesAreMutable() { + JWTAuthentication authentication = new JWTAuthentication(claims("bob"), details()); + SyncopeGrantedAuthority authority = new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ); + + authentication.getAuthorities().add(authority); + + assertEquals(1, authentication.getAuthorities().size()); + assertTrue(authentication.getAuthorities().contains(authority)); + } + + @Test + void setAuthenticated() { + JWTAuthentication authentication = new JWTAuthentication(claims("bob"), details()); + + authentication.setAuthenticated(true); + + assertTrue(authentication.isAuthenticated()); + } + + @Test + void equalsAndHashCode() { + JWTAuthentication first = new JWTAuthentication(claims("bob"), details()); + JWTAuthentication second = new JWTAuthentication(claims("bob"), details()); + JWTAuthentication differentSubject = new JWTAuthentication(claims("alice"), details()); + + assertEquals(first, first); + assertEquals(first, second); + assertEquals(first.hashCode(), second.hashCode()); + assertNotEquals(first, differentSubject); + assertNotEquals(first, null); + assertNotEquals(first, "other"); + + first.getAuthorities().add(new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ)); + assertNotEquals(first, second); + } + + @Test + void toStringContainsState() { + JWTAuthentication authentication = new JWTAuthentication(claims("bob"), details()); + + assertTrue(authentication.toString().contains("Principal: bob")); + assertTrue(authentication.toString().contains("Authenticated: false")); + assertTrue(authentication.toString().contains("Not granted any authorities")); + + authentication.getAuthorities().add(new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ)); + assertTrue(authentication.toString().contains("Granted Authorities:")); + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/MSEntraJWTSSOProviderTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/MSEntraJWTSSOProviderTest.java index efe102ac2a8..8af6cd551e9 100644 --- a/core/spring/src/test/java/org/apache/syncope/core/spring/security/MSEntraJWTSSOProviderTest.java +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/MSEntraJWTSSOProviderTest.java @@ -47,7 +47,7 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -public class MSEntraJWTSSOProviderTest { +class MSEntraJWTSSOProviderTest { private static final String TENANT_ID = "test-tenant-id"; diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/MfaTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/MfaTest.java index a91796bc069..09fabd368bd 100644 --- a/core/spring/src/test/java/org/apache/syncope/core/spring/security/MfaTest.java +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/MfaTest.java @@ -34,10 +34,10 @@ import java.time.Instant; import org.junit.jupiter.api.Test; -public class MfaTest { +class MfaTest { @Test - public void check() throws CodeGenerationException { + void check() throws CodeGenerationException { SecretGenerator totpSecretGenerator = new DefaultSecretGenerator(64); String secret = totpSecretGenerator.generate(); diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/PasswordGeneratorTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/PasswordGeneratorTest.java index 8927d4e0cbe..3d832ba7d25 100644 --- a/core/spring/src/test/java/org/apache/syncope/core/spring/security/PasswordGeneratorTest.java +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/PasswordGeneratorTest.java @@ -33,12 +33,12 @@ import tools.jackson.databind.json.JsonMapper; @SpringJUnitConfig(classes = { SpringTestConfiguration.class }) -public class PasswordGeneratorTest { +class PasswordGeneratorTest { private final DefaultPasswordGenerator passwordGenerator = new DefaultPasswordGenerator(); @Test - public void digit() { + void digit() { DefaultPasswordRuleConf pwdRuleConf = PasswordRuleTest.createBaseDefaultPasswordRuleConf(); pwdRuleConf.setDigit(1); TestImplementation passwordRule = new TestImplementation(); @@ -50,7 +50,7 @@ public void digit() { } @Test - public void alphabetical() { + void alphabetical() { DefaultPasswordRuleConf pwdRuleConf = PasswordRuleTest.createBaseDefaultPasswordRuleConf(); pwdRuleConf.setAlphabetical(1); TestImplementation passwordRule = new TestImplementation(); @@ -62,7 +62,7 @@ public void alphabetical() { } @Test - public void lowercase() { + void lowercase() { DefaultPasswordRuleConf pwdRuleConf = PasswordRuleTest.createBaseDefaultPasswordRuleConf(); pwdRuleConf.setLowercase(1); TestImplementation passwordRule = new TestImplementation(); @@ -74,7 +74,7 @@ public void lowercase() { } @Test - public void uppercase() { + void uppercase() { DefaultPasswordRuleConf pwdRuleConf = PasswordRuleTest.createBaseDefaultPasswordRuleConf(); pwdRuleConf.setUppercase(1); TestImplementation passwordRule = new TestImplementation(); @@ -86,7 +86,7 @@ public void uppercase() { } @Test - public void special() { + void special() { DefaultPasswordRuleConf pwdRuleConf = PasswordRuleTest.createBaseDefaultPasswordRuleConf(); pwdRuleConf.setSpecial(1); pwdRuleConf.getSpecialChars().add('@'); @@ -101,7 +101,7 @@ public void special() { } @Test - public void validateGenerated() { + void validateGenerated() { String input = """ { @@ -137,7 +137,7 @@ public void validateGenerated() { } @Test - public void issueSYNCOPE678() { + void issueSYNCOPE678() { String password = passwordGenerator.generate(List.of()); assertNotNull(password); diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/SecureRandomUtilsTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/SecureRandomUtilsTest.java new file mode 100644 index 00000000000..db786af4f29 --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/SecureRandomUtilsTest.java @@ -0,0 +1,92 @@ +/* + * 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.core.spring.security; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.UUID; +import org.junit.jupiter.api.Test; +import org.passay.data.EnglishCharacterData; +import org.passay.rule.CharacterRule; + +class SecureRandomUtilsTest { + + @Test + void generateRandomPassword() { + String password = SecureRandomUtils.generateRandomPassword(16); + + assertEquals(16, password.length()); + assertTrue(password.chars().allMatch(Character::isLetterOrDigit)); + } + + @Test + void generateRandomLetters() { + assertEquals(1, SecureRandomUtils.generateRandomLetter().length()); + assertEquals(8, SecureRandomUtils.generateRandomLetters(8).length()); + assertTrue(SecureRandomUtils.generateRandomLetters(8).chars().allMatch(Character::isLowerCase)); + } + + @Test + void generateRandomNumber() { + String number = SecureRandomUtils.generateRandomNumber(); + + assertEquals(1, number.length()); + assertTrue(number.chars().allMatch(Character::isDigit)); + } + + @Test + void generateRandomNonAlphanumericChar() { + char[] allowed = { '!', '@', '#' }; + + String value = SecureRandomUtils.generateRandomNonAlphanumericChar(allowed); + + assertEquals(1, value.length()); + assertTrue(List.of('!', '@', '#').contains(value.charAt(0))); + } + + @Test + void generateRandomNumbersInRange() { + for (int i = 0; i < 100; i++) { + int intValue = SecureRandomUtils.generateRandomInt(0, 10); + assertTrue(intValue >= 0 && intValue < 10); + + long longValue = SecureRandomUtils.generateRandomLong(0, 10); + assertTrue(longValue >= 0 && longValue < 10); + } + } + + @Test + void generateRandomUUIDs() { + UUID first = SecureRandomUtils.generateRandomUUID(); + UUID second = SecureRandomUtils.generateRandomUUID(); + + assertNotNull(first); + assertNotEquals(first, second); + } + + @Test + void passwordGenerator() { + assertNotNull(SecureRandomUtils.passwordGenerator( + 10, List.of(new CharacterRule(EnglishCharacterData.Alphabetical, 1)))); + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/SinglePurposeEntitlementFilterTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/SinglePurposeEntitlementFilterTest.java new file mode 100644 index 00000000000..f08fc04bc2f --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/SinglePurposeEntitlementFilterTest.java @@ -0,0 +1,163 @@ +/* + * 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.core.spring.security; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import java.util.List; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper; + +class SinglePurposeEntitlementFilterTest { + + private final SinglePurposeEntitlementFilter filter = new SinglePurposeEntitlementFilter(); + + private FilterChain chain; + + @BeforeEach + void setUp() { + chain = mock(FilterChain.class); + SecurityContextHolder.clearContext(); + } + + @AfterEach + void tearDown() { + SecurityContextHolder.clearContext(); + } + + private void authenticate(final String... entitlements) { + List authorities = java.util.Arrays.stream(entitlements). + map(SyncopeGrantedAuthority::new). + toList(); + SecurityContextHolder.getContext().setAuthentication( + new UsernamePasswordAuthenticationToken("bob", null, authorities)); + } + + private SecurityContextHolderAwareRequestWrapper wrapper(final String method, final String pathInfo) { + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getMethod()).thenReturn(method); + when(request.getPathInfo()).thenReturn(pathInfo); + return new SecurityContextHolderAwareRequestWrapper(request, "ROLE_"); + } + + @Test + void notWrappedRequestPassesThrough() throws Exception { + ServletRequest request = mock(ServletRequest.class); + ServletResponse response = mock(ServletResponse.class); + + filter.doFilter(request, response, chain); + + verify(chain).doFilter(request, response); + } + + @Test + void mustChangePasswordOnOtherEndpointIsDenied() throws Exception { + authenticate(IdRepoEntitlement.MUST_CHANGE_PASSWORD); + SecurityContextHolderAwareRequestWrapper request = wrapper("GET", "/users"); + + AccessDeniedException ex = assertThrows( + AccessDeniedException.class, + () -> filter.doFilter(request, mock(ServletResponse.class), chain)); + + assertEquals("Please change your password first", ex.getMessage()); + SingleEntitlementException cause = assertInstanceOf(SingleEntitlementException.class, ex.getCause()); + assertEquals(IdRepoEntitlement.MUST_CHANGE_PASSWORD, cause.getEntitlement()); + verify(chain, never()).doFilter(any(), any()); + } + + @Test + void mustChangePasswordOnExpectedEndpointPassesThrough() throws Exception { + authenticate(IdRepoEntitlement.MUST_CHANGE_PASSWORD); + SecurityContextHolderAwareRequestWrapper request = wrapper("POST", "/users/self/mustChangePassword"); + ServletResponse response = mock(ServletResponse.class); + + filter.doFilter(request, response, chain); + + verify(chain).doFilter(request, response); + } + + @Test + void mustChangePasswordWithWrongMethodIsDenied() throws Exception { + authenticate(IdRepoEntitlement.MUST_CHANGE_PASSWORD); + SecurityContextHolderAwareRequestWrapper request = wrapper("GET", "/users/self/mustChangePassword"); + + assertThrows( + AccessDeniedException.class, + () -> filter.doFilter(request, mock(ServletResponse.class), chain)); + verify(chain, never()).doFilter(any(), any()); + } + + @Test + void mfaEnrollOnExpectedEndpointPassesThrough() throws Exception { + authenticate(IdRepoEntitlement.MFA_ENROLL); + SecurityContextHolderAwareRequestWrapper request = wrapper("PUT", "/mfa"); + ServletResponse response = mock(ServletResponse.class); + + filter.doFilter(request, response, chain); + + verify(chain).doFilter(request, response); + } + + @Test + void mfaEnrollOnOtherEndpointIsDenied() throws Exception { + authenticate(IdRepoEntitlement.MFA_ENROLL); + SecurityContextHolderAwareRequestWrapper request = wrapper("PUT", "/users"); + + AccessDeniedException ex = assertThrows( + AccessDeniedException.class, + () -> filter.doFilter(request, mock(ServletResponse.class), chain)); + + assertEquals("Please enroll your MFA first", ex.getMessage()); + verify(chain, never()).doFilter(any(), any()); + } + + @Test + void noSinglePurposeEntitlementPassesThrough() throws Exception { + authenticate(IdRepoEntitlement.USER_READ); + SecurityContextHolderAwareRequestWrapper request = wrapper("GET", "/users"); + ServletResponse response = mock(ServletResponse.class); + + filter.doFilter(request, response, chain); + + verify(chain).doFilter(request, response); + } + + @Test + void initAndDestroyAreNoOps() { + filter.init(null); + filter.destroy(); + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/SyncopeAccessDeniedHandlerTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/SyncopeAccessDeniedHandlerTest.java new file mode 100644 index 00000000000..9f9fc0bda62 --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/SyncopeAccessDeniedHandlerTest.java @@ -0,0 +1,78 @@ +/* + * 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.core.spring.security; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.common.rest.api.RESTHeaders; +import org.junit.jupiter.api.Test; +import org.springframework.security.access.AccessDeniedException; + +class SyncopeAccessDeniedHandlerTest { + + private final SyncopeAccessDeniedHandler handler = new SyncopeAccessDeniedHandler(); + + @Test + void singleEntitlementCauseIsExposedInHeaders() throws Exception { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + AccessDeniedException exception = new AccessDeniedException( + "Please change your password first", + new SingleEntitlementException(IdRepoEntitlement.MUST_CHANGE_PASSWORD)); + + handler.handle(request, response, exception); + + verify(response).addHeader(RESTHeaders.OWNED_ENTITLEMENTS, IdRepoEntitlement.MUST_CHANGE_PASSWORD); + verify(response).addHeader(RESTHeaders.ERROR_INFO, "Please change your password first"); + verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), anyString()); + } + + @Test + void noCauseOnlyExposesErrorInfo() throws Exception { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + AccessDeniedException exception = new AccessDeniedException("Access is denied"); + + handler.handle(request, response, exception); + + verify(response, never()).addHeader(eq(RESTHeaders.OWNED_ENTITLEMENTS), anyString()); + verify(response).addHeader(RESTHeaders.ERROR_INFO, "Access is denied"); + verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), anyString()); + } + + @Test + void otherCauseOnlyExposesErrorInfo() throws Exception { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + AccessDeniedException exception = new AccessDeniedException("Access is denied", new RuntimeException("boom")); + + handler.handle(request, response, exception); + + verify(response, never()).addHeader(eq(RESTHeaders.OWNED_ENTITLEMENTS), anyString()); + verify(response).addHeader(RESTHeaders.ERROR_INFO, "Access is denied"); + verify(response).sendError(eq(HttpServletResponse.SC_FORBIDDEN), anyString()); + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/SyncopeAuthenticationDetailsSourceTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/SyncopeAuthenticationDetailsSourceTest.java new file mode 100644 index 00000000000..be5bd571b79 --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/SyncopeAuthenticationDetailsSourceTest.java @@ -0,0 +1,64 @@ +/* + * 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.core.spring.security; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import jakarta.servlet.http.HttpServletRequest; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.rest.api.RESTHeaders; +import org.junit.jupiter.api.Test; + +class SyncopeAuthenticationDetailsSourceTest { + + @Test + void buildDetailsFromRequestHeaders() { + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getHeader(RESTHeaders.DOMAIN)).thenReturn("other"); + when(request.getHeader(RESTHeaders.DELEGATED_BY)).thenReturn("delegator"); + + SyncopeAuthenticationDetails details = new SyncopeAuthenticationDetailsSource().buildDetails(request); + + assertEquals("other", details.getDomain()); + assertEquals("delegator", details.getDelegatedBy()); + } + + @Test + void buildDetailsWithoutHeaders() { + HttpServletRequest request = mock(HttpServletRequest.class); + + SyncopeAuthenticationDetails details = new SyncopeAuthenticationDetailsSource().buildDetails(request); + + assertEquals(SyncopeConstants.MASTER_DOMAIN, details.getDomain()); + assertNull(details.getDelegatedBy()); + } + + @Test + void buildDetailsWithBlankDomain() { + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getHeader(RESTHeaders.DOMAIN)).thenReturn(" "); + + SyncopeAuthenticationDetails details = new SyncopeAuthenticationDetailsSource().buildDetails(request); + + assertEquals(SyncopeConstants.MASTER_DOMAIN, details.getDomain()); + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/SyncopeGrantedAuthorityTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/SyncopeGrantedAuthorityTest.java new file mode 100644 index 00000000000..c908a97b8bb --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/SyncopeGrantedAuthorityTest.java @@ -0,0 +1,103 @@ +/* + * 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.core.spring.security; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.Set; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.junit.jupiter.api.Test; + +class SyncopeGrantedAuthorityTest { + + @Test + void entitlementOnlyConstructor() { + SyncopeGrantedAuthority authority = new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ); + + assertEquals(IdRepoEntitlement.USER_READ, authority.getAuthority()); + assertTrue(authority.getRealms().isEmpty()); + } + + @Test + void realmConstructor() { + SyncopeGrantedAuthority authority = new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ, "/a"); + + assertEquals(Set.of("/a"), authority.getRealms()); + } + + @Test + void normalizingAddRealm() { + SyncopeGrantedAuthority authority = new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ); + + assertTrue(authority.addRealm("/a")); + assertEquals(Set.of("/a"), authority.getRealms()); + + assertFalse(authority.addRealm("/a/b")); + assertEquals(Set.of("/a"), authority.getRealms()); + + assertTrue(authority.addRealm(SyncopeConstants.ROOT_REALM)); + assertEquals(Set.of(SyncopeConstants.ROOT_REALM), authority.getRealms()); + } + + @Test + void addRealms() { + SyncopeGrantedAuthority authority = new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ); + + authority.addRealms(List.of("/a", "/a/b", "/c")); + + assertEquals(Set.of("/a", "/c"), authority.getRealms()); + } + + @Test + void realmsAreUnmodifiable() { + SyncopeGrantedAuthority authority = new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ, "/a"); + + assertThrows(UnsupportedOperationException.class, () -> authority.getRealms().add("/b")); + } + + @Test + void equalsAndHashCode() { + SyncopeGrantedAuthority first = new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ, "/a"); + SyncopeGrantedAuthority same = new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ, "/a"); + SyncopeGrantedAuthority differentRealm = new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ, "/b"); + SyncopeGrantedAuthority differentEntitlement = new SyncopeGrantedAuthority(IdRepoEntitlement.GROUP_READ, "/a"); + + assertEquals(first, first); + assertEquals(first, same); + assertEquals(first.hashCode(), same.hashCode()); + assertNotEquals(first, differentRealm); + assertNotEquals(first, differentEntitlement); + assertNotEquals(first, null); + assertNotEquals(first, "other"); + } + + @Test + void toStringContainsEntitlementAndRealms() { + SyncopeGrantedAuthority authority = new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ, "/a"); + + assertTrue(authority.toString().contains(IdRepoEntitlement.USER_READ)); + assertTrue(authority.toString().contains("/a")); + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProviderTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProviderTest.java new file mode 100644 index 00000000000..27c2cd16aa2 --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/SyncopeJWTSSOProviderTest.java @@ -0,0 +1,257 @@ +/* + * 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.core.spring.security; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.jca.JCAContext; +import com.nimbusds.jose.util.Base64URL; +import com.nimbusds.jwt.JWTClaimsSet; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.persistence.api.Encryptor; +import org.apache.syncope.core.persistence.api.EncryptorManager; +import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.AccessToken; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.spring.security.jws.AccessTokenJWSVerifier; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; + +@ExtendWith(MockitoExtension.class) +class SyncopeJWTSSOProviderTest { + + private static final String JWT_ID = "jwt-id"; + + private static final String USERNAME = "bob"; + + private static final String ISSUER = "configured-issuer"; + + @Mock + private EncryptorManager encryptorManager; + + @Mock + private Encryptor encryptor; + + @Mock + private AccessTokenJWSVerifier delegate; + + @Mock + private UserDAO userDAO; + + @Mock + private AccessTokenDAO accessTokenDAO; + + @Mock + private AccessToken accessToken; + + @Mock + private User user; + + @Mock + private JWSHeader header; + + @Mock + private JCAContext jcaContext; + + private SecurityProperties securityProperties; + + private SyncopeJWTSSOProvider provider; + + @BeforeEach + void setUp() { + securityProperties = new SecurityProperties(); + securityProperties.setJwtIssuer(ISSUER); + + provider = new SyncopeJWTSSOProvider( + securityProperties, encryptorManager, delegate, userDAO, accessTokenDAO); + } + + private JWTClaimsSet claims(final String subject, final String jwtID) { + return new JWTClaimsSet.Builder().subject(subject).jwtID(jwtID).build(); + } + + private void mockAccessToken(final String owner) { + when(accessToken.getOwner()).thenReturn(owner); + doReturn(Optional.of(accessToken)).when(accessTokenDAO).findById(JWT_ID); + } + + @Test + void getIssuer() { + assertEquals(ISSUER, provider.getIssuer()); + } + + @Test + void supportedJWSAlgorithms() { + Set algorithms = Set.of(JWSAlgorithm.HS512); + when(delegate.supportedJWSAlgorithms()).thenReturn(algorithms); + + assertSame(algorithms, provider.supportedJWSAlgorithms()); + } + + @Test + void getJCAContext() { + when(delegate.getJCAContext()).thenReturn(jcaContext); + + assertSame(jcaContext, provider.getJCAContext()); + } + + @Test + void verifyDelegatesToJWSVerifier() throws JOSEException { + byte[] signingInput = new byte[] { 1, 2, 3 }; + Base64URL signature = new Base64URL("signature"); + when(delegate.verify(header, signingInput, signature)).thenReturn(true); + + assertTrue(provider.verify(header, signingInput, signature)); + verify(delegate).verify(header, signingInput, signature); + } + + @Test + void verifyPropagatesJOSEException() throws JOSEException { + byte[] signingInput = new byte[] { 1, 2, 3 }; + Base64URL signature = new Base64URL("signature"); + when(delegate.verify(header, signingInput, signature)).thenThrow(new JOSEException("boom")); + + JOSEException ex = assertThrows( + JOSEException.class, + () -> provider.verify(header, signingInput, signature)); + assertEquals("boom", ex.getMessage()); + } + + @Test + void resolveThrowsWhenAccessTokenIsMissing() { + doReturn(Optional.empty()).when(accessTokenDAO).findById(JWT_ID); + + AuthenticationCredentialsNotFoundException ex = assertThrows( + AuthenticationCredentialsNotFoundException.class, + () -> provider.resolve(claims(USERNAME, JWT_ID))); + assertEquals("Could not find an Access Token for JWT " + JWT_ID, ex.getMessage()); + } + + @Test + void resolveThrowsWhenAccessTokenOwnerDoesNotMatchSubject() { + mockAccessToken("alice"); + + AuthenticationCredentialsNotFoundException ex = assertThrows( + AuthenticationCredentialsNotFoundException.class, + () -> provider.resolve(claims(USERNAME, JWT_ID))); + assertEquals("Access Token owner does not match JWT subject", ex.getMessage()); + } + + @Test + void resolveReturnsEmptyWhenUserCannotBeFound() { + mockAccessToken(USERNAME); + doReturn(Optional.empty()).when(userDAO).findByUsername(USERNAME); + + assertTrue(provider.resolve(claims(USERNAME, JWT_ID)).isEmpty()); + verifyNoInteractions(encryptorManager); + } + + @Test + void resolveReturnsUserWithoutAuthoritiesWhenAccessTokenHasNone() { + mockAccessToken(USERNAME); + doReturn(Optional.of(user)).when(userDAO).findByUsername(USERNAME); + + JWTSSOProvider.ResolvedClaims resolved = provider.resolve(claims(USERNAME, JWT_ID)).orElseThrow(); + + assertSame(user, resolved.user()); + assertTrue(resolved.authorities().isEmpty()); + verifyNoInteractions(encryptorManager); + } + + @Test + void resolveDecodesStoredAuthorities() throws Exception { + Set stored = Set.of( + new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ, "/users")); + mockAccessToken(USERNAME); + when(accessToken.getAuthorities()).thenReturn("encoded-authorities"); + when(encryptorManager.getInstance()).thenReturn(encryptor); + when(encryptor.decode("encoded-authorities", CipherAlgorithm.AES)). + thenReturn(POJOHelper.serialize(stored)); + doReturn(Optional.of(user)).when(userDAO).findByUsername(USERNAME); + + JWTSSOProvider.ResolvedClaims resolved = provider.resolve(claims(USERNAME, JWT_ID)).orElseThrow(); + + assertEquals(1, resolved.authorities().size()); + SyncopeGrantedAuthority authority = resolved.authorities().iterator().next(); + assertEquals(IdRepoEntitlement.USER_READ, authority.getAuthority()); + assertEquals(Set.of("/users"), authority.getRealms()); + verify(encryptor).decode("encoded-authorities", CipherAlgorithm.AES); + } + + @Test + void resolveReturnsUserWithoutAuthoritiesWhenDecodingFails() throws Exception { + mockAccessToken(USERNAME); + when(accessToken.getAuthorities()).thenReturn("encoded-authorities"); + when(encryptorManager.getInstance()).thenReturn(encryptor); + when(encryptor.decode("encoded-authorities", CipherAlgorithm.AES)). + thenThrow(new IllegalStateException("boom")); + doReturn(Optional.of(user)).when(userDAO).findByUsername(USERNAME); + + JWTSSOProvider.ResolvedClaims resolved = provider.resolve(claims(USERNAME, JWT_ID)).orElseThrow(); + + assertSame(user, resolved.user()); + assertTrue(resolved.authorities().isEmpty()); + } + + @Test + void resolveReturnsUserWithoutAuthoritiesWhenDeserializationFails() throws Exception { + mockAccessToken(USERNAME); + when(accessToken.getAuthorities()).thenReturn("encoded-authorities"); + when(encryptorManager.getInstance()).thenReturn(encryptor); + when(encryptor.decode("encoded-authorities", CipherAlgorithm.AES)).thenReturn("not-json"); + doReturn(Optional.of(user)).when(userDAO).findByUsername(USERNAME); + + JWTSSOProvider.ResolvedClaims resolved = provider.resolve(claims(USERNAME, JWT_ID)).orElseThrow(); + + assertSame(user, resolved.user()); + assertTrue(resolved.authorities().isEmpty()); + } + + @Test + void resolveReturnsUserWithoutAuthoritiesWhenEncryptorManagerFails() { + mockAccessToken(USERNAME); + when(accessToken.getAuthorities()).thenReturn("encoded-authorities"); + when(encryptorManager.getInstance()).thenThrow(new IllegalStateException("boom")); + doReturn(Optional.of(user)).when(userDAO).findByUsername(USERNAME); + + JWTSSOProvider.ResolvedClaims resolved = provider.resolve(claims(USERNAME, JWT_ID)).orElseThrow(); + + assertSame(user, resolved.user()); + assertTrue(resolved.authorities().isEmpty()); + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProviderTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProviderTest.java new file mode 100644 index 00000000000..416d1641f9a --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProviderTest.java @@ -0,0 +1,292 @@ +/* + * 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.core.spring.security; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Optional; +import java.util.Set; +import javax.cache.Cache; +import org.apache.syncope.common.keymaster.client.api.DomainOps; +import org.apache.syncope.common.keymaster.client.api.KeymasterException; +import org.apache.syncope.common.keymaster.client.api.model.Domain; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.EntitlementsHolder; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.common.lib.types.OpEvent; +import org.apache.syncope.core.persistence.api.ApplicationContextProvider; +import org.apache.syncope.core.persistence.api.EncryptorManager; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.UserProvisioningManager; +import org.apache.syncope.core.spring.security.throttle.ThrottlerAttempts; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.StaticApplicationContext; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.authentication.ott.InvalidOneTimeTokenException; +import org.springframework.security.core.Authentication; + +class UsernamePasswordAuthenticationProviderTest { + + private final DomainOps domainOps = mock(DomainOps.class); + + private final AuthDataAccessor dataAccessor = mock(AuthDataAccessor.class); + + private final UserProvisioningManager provisioningManager = mock(UserProvisioningManager.class); + + private final EncryptorManager encryptorManager = mock(EncryptorManager.class); + + @SuppressWarnings("unchecked") + private final Cache throttlerCache = mock(Cache.class); + + private SecurityProperties securityProperties; + + private StaticApplicationContext context; + + private UsernamePasswordAuthenticationProvider provider; + + private ConfigurableApplicationContext previousContext; + + private DefaultListableBeanFactory previousBeanFactory; + + @BeforeEach + void setUp() { + previousContext = ApplicationContextProvider.getApplicationContext(); + previousBeanFactory = ApplicationContextProvider.getBeanFactory(); + + EntitlementsHolder.getInstance().addAll(IdRepoEntitlement.values()); + + securityProperties = new SecurityProperties(); + securityProperties.setAdminUser("admin"); + securityProperties.getAuthenticationThrottle().setEnabled(false); + + context = new StaticApplicationContext(); + context.getBeanFactory().registerSingleton("securityProperties", securityProperties); + context.refresh(); + + ApplicationContextProvider.setApplicationContext(context); + ApplicationContextProvider.setBeanFactory((DefaultListableBeanFactory) context.getBeanFactory()); + + provider = new UsernamePasswordAuthenticationProvider( + domainOps, + dataAccessor, + provisioningManager, + securityProperties, + encryptorManager, + throttlerCache); + } + + @AfterEach + void tearDown() { + ApplicationContextProvider.setApplicationContext(previousContext); + ApplicationContextProvider.setBeanFactory(previousBeanFactory); + context.close(); + } + + private UsernamePasswordAuthenticationToken authentication(final String domain) { + UsernamePasswordAuthenticationToken authentication = + new UsernamePasswordAuthenticationToken("bob", "pwd"); + authentication.setDetails(new SyncopeAuthenticationDetails(domain, null)); + return authentication; + } + + private User user() { + User user = mock(User.class); + when(user.getKey()).thenReturn("user-key"); + when(user.getUsername()).thenReturn("bob"); + return user; + } + + private AuthDataAccessor.UsernamePasswordAuthResult result( + final User user, + final boolean passwordVerified, + final Boolean otpVerified, + final Set authorities) { + + return new AuthDataAccessor.UsernamePasswordAuthResult( + user, passwordVerified, otpVerified, null, authorities); + } + + @Test + void supportsUsernamePasswordAuthentication() { + assertTrue(provider.supports(UsernamePasswordAuthenticationToken.class)); + assertFalse(provider.supports(JWTAuthentication.class)); + } + + @Test + void successfulAuthenticationOnMasterDomain() { + User user = user(); + Set authorities = + Set.of(new SyncopeGrantedAuthority(IdRepoEntitlement.USER_READ)); + UsernamePasswordAuthenticationToken authentication = authentication(SyncopeConstants.MASTER_DOMAIN); + + when(dataAccessor.authenticate(eq(Optional.empty()), any())). + thenReturn(result(user, true, true, authorities)); + + Authentication result = provider.authenticate(authentication); + + assertEquals("bob", result.getName()); + assertEquals(authorities, Set.copyOf(result.getAuthorities())); + assertSame(authentication.getDetails(), result.getDetails()); + assertNull(result.getCredentials()); + verify(domainOps, never()).read(anyString()); + verify(dataAccessor).audit( + eq(SyncopeConstants.MASTER_DOMAIN), + eq("bob"), + isNull(), + eq(OpEvent.Outcome.SUCCESS), + eq(true), + eq(authentication), + anyString()); + } + + @Test + void successfulAuthenticationOnConfiguredDomain() { + User user = user(); + Domain domain = mock(Domain.class); + UsernamePasswordAuthenticationToken authentication = authentication("other"); + + when(domainOps.read("other")).thenReturn(domain); + when(dataAccessor.authenticate(eq(Optional.of(domain)), any())). + thenReturn(result(user, true, true, Set.of())); + + Authentication result = provider.authenticate(authentication); + + assertEquals("bob", result.getName()); + verify(domainOps).read("other"); + } + + @Test + void unknownDomainIsRejected() { + when(domainOps.read("other")).thenThrow(new NotFoundException("not found")); + + BadCredentialsException ex = assertThrows( + BadCredentialsException.class, + () -> provider.authenticate(authentication("other"))); + + assertEquals("Could not find domain other", ex.getMessage()); + assertInstanceOf(NotFoundException.class, ex.getCause()); + verify(dataAccessor, never()).authenticate(any(), any()); + } + + @Test + void keymasterFailureIsRejected() { + when(domainOps.read("other")).thenThrow(new KeymasterException("boom")); + + BadCredentialsException ex = assertThrows( + BadCredentialsException.class, + () -> provider.authenticate(authentication("other"))); + + assertEquals("Could not find domain other", ex.getMessage()); + assertInstanceOf(KeymasterException.class, ex.getCause()); + } + + @Test + void failedPasswordSuspendsUserAndIsRejected() { + User user = user(); + UsernamePasswordAuthenticationToken authentication = authentication(SyncopeConstants.MASTER_DOMAIN); + + when(dataAccessor.authenticate(eq(Optional.empty()), any())). + thenReturn(result(user, false, null, Set.of())); + + BadCredentialsException ex = assertThrows( + BadCredentialsException.class, + () -> provider.authenticate(authentication)); + + assertEquals("bob: invalid password provided", ex.getMessage()); + verify(provisioningManager).suspendOnAuthFailures("user-key", "admin", "Failed authentication"); + verify(dataAccessor).audit( + eq(SyncopeConstants.MASTER_DOMAIN), + eq("bob"), + isNull(), + eq(OpEvent.Outcome.FAILURE), + eq(false), + eq(authentication), + eq("Not authenticated")); + } + + @Test + void failedOtpIsRejected() { + User user = user(); + UsernamePasswordAuthenticationToken authentication = authentication(SyncopeConstants.MASTER_DOMAIN); + + when(dataAccessor.authenticate(eq(Optional.empty()), any())). + thenReturn(result(user, true, false, Set.of())); + + InvalidOneTimeTokenException ex = assertThrows( + InvalidOneTimeTokenException.class, + () -> provider.authenticate(authentication)); + + assertEquals("bob: invalid OTP or recovery code provided", ex.getMessage()); + verify(provisioningManager).suspendOnAuthFailures("user-key", "admin", "Failed authentication"); + } + + @Test + void missingUserFallsBackToAuthenticatingPrincipal() { + UsernamePasswordAuthenticationToken authentication = authentication(SyncopeConstants.MASTER_DOMAIN); + + when(dataAccessor.authenticate(eq(Optional.empty()), any())). + thenReturn(result(null, true, true, Set.of())); + + Authentication result = provider.authenticate(authentication); + + assertEquals("bob", result.getName()); + verify(provisioningManager, never()).suspendOnAuthFailures(any(), any(), any()); + } + + @Test + void auditUsesDelegationKeyOnlyWhenProvided() { + User user = user(); + UsernamePasswordAuthenticationToken authentication = authentication(SyncopeConstants.MASTER_DOMAIN); + AuthDataAccessor.UsernamePasswordAuthResult authResult = new AuthDataAccessor.UsernamePasswordAuthResult( + user, true, true, "delegation-key", Set.of()); + + when(dataAccessor.authenticate(eq(Optional.empty()), any())).thenReturn(authResult); + + provider.authenticate(authentication); + + verify(dataAccessor).audit( + eq(SyncopeConstants.MASTER_DOMAIN), + eq("bob"), + eq("delegation-key"), + eq(OpEvent.Outcome.SUCCESS), + eq(true), + eq(authentication), + anyString()); + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSSignerTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSSignerTest.java new file mode 100644 index 00000000000..163c43ec14c --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSSignerTest.java @@ -0,0 +1,89 @@ +/* + * 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.core.spring.security.jws; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.util.Base64URL; +import java.nio.charset.StandardCharsets; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.util.Base64; +import org.junit.jupiter.api.Test; + +class AccessTokenJWSSignerTest { + + private static final String HMAC_KEY = + "0123456789012345678901234567890123456789012345678901234567890123456789"; + + private static String keyPair() throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); + generator.initialize(2048); + KeyPair keyPair = generator.generateKeyPair(); + + return Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()) + + ':' + Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()); + } + + @Test + void signWithHmac() throws Exception { + AccessTokenJWSSigner signer = new AccessTokenJWSSigner(JWSAlgorithm.HS512, HMAC_KEY); + + assertEquals(JWSAlgorithm.HS512, signer.getJwsAlgorithm()); + assertTrue(signer.supportedJWSAlgorithms().contains(JWSAlgorithm.HS512)); + assertNotNull(signer.getJCAContext()); + + Base64URL signature = signer.sign( + new JWSHeader(JWSAlgorithm.HS512), + "payload".getBytes(StandardCharsets.UTF_8)); + assertNotNull(signature); + assertTrue(signature.toString().length() > 0); + } + + @Test + void signWithRsa() throws Exception { + AccessTokenJWSSigner signer = new AccessTokenJWSSigner(JWSAlgorithm.RS256, keyPair()); + + assertTrue(signer.supportedJWSAlgorithms().contains(JWSAlgorithm.RS256)); + assertNotNull(signer.sign( + new JWSHeader(JWSAlgorithm.RS256), + "payload".getBytes(StandardCharsets.UTF_8))); + } + + @Test + void rsaRequiresKeyPair() throws Exception { + IllegalArgumentException ex = assertThrows( + IllegalArgumentException.class, + () -> new AccessTokenJWSSigner(JWSAlgorithm.RS256, HMAC_KEY)); + assertEquals("A key pair is required, in the 'private:public' format", ex.getMessage()); + } + + @Test + void unsupportedAlgorithm() throws Exception { + IllegalArgumentException ex = assertThrows( + IllegalArgumentException.class, + () -> new AccessTokenJWSSigner(JWSAlgorithm.ES256, HMAC_KEY)); + assertEquals("Unsupported JWS algorithm: ES256", ex.getMessage()); + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSVerifierTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSVerifierTest.java new file mode 100644 index 00000000000..7ce96b6e791 --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/jws/AccessTokenJWSVerifierTest.java @@ -0,0 +1,105 @@ +/* + * 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.core.spring.security.jws; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.util.Base64URL; +import java.nio.charset.StandardCharsets; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.util.Base64; +import org.junit.jupiter.api.Test; + +class AccessTokenJWSVerifierTest { + + private static final String HMAC_KEY = + "0123456789012345678901234567890123456789012345678901234567890123456789"; + + private static final byte[] PAYLOAD = "payload".getBytes(StandardCharsets.UTF_8); + + private static String keyPair() throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); + generator.initialize(2048); + KeyPair keyPair = generator.generateKeyPair(); + + return Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()) + + ':' + Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()); + } + + @Test + void verifyHmacSignature() throws Exception { + AccessTokenJWSSigner signer = new AccessTokenJWSSigner(JWSAlgorithm.HS512, HMAC_KEY); + AccessTokenJWSVerifier verifier = new AccessTokenJWSVerifier(JWSAlgorithm.HS512, HMAC_KEY); + + JWSHeader header = new JWSHeader(JWSAlgorithm.HS512); + Base64URL signature = signer.sign(header, PAYLOAD); + + assertTrue(verifier.supportedJWSAlgorithms().contains(JWSAlgorithm.HS512)); + assertNotNull(verifier.getJCAContext()); + assertTrue(verifier.verify(header, PAYLOAD, signature)); + assertFalse(verifier.verify(header, "other".getBytes(StandardCharsets.UTF_8), signature)); + } + + @Test + void rejectSignatureFromDifferentKey() throws Exception { + AccessTokenJWSSigner signer = new AccessTokenJWSSigner( + JWSAlgorithm.HS512, HMAC_KEY + "different"); + AccessTokenJWSVerifier verifier = new AccessTokenJWSVerifier(JWSAlgorithm.HS512, HMAC_KEY); + + JWSHeader header = new JWSHeader(JWSAlgorithm.HS512); + Base64URL signature = signer.sign(header, PAYLOAD); + + assertFalse(verifier.verify(header, PAYLOAD, signature)); + } + + @Test + void verifyRsaSignature() throws Exception { + String keys = keyPair(); + AccessTokenJWSSigner signer = new AccessTokenJWSSigner(JWSAlgorithm.RS256, keys); + AccessTokenJWSVerifier verifier = new AccessTokenJWSVerifier(JWSAlgorithm.RS256, keys); + + JWSHeader header = new JWSHeader(JWSAlgorithm.RS256); + Base64URL signature = signer.sign(header, PAYLOAD); + + assertTrue(verifier.verify(header, PAYLOAD, signature)); + } + + @Test + void rsaRequiresKeyPair() throws Exception { + IllegalArgumentException ex = assertThrows( + IllegalArgumentException.class, + () -> new AccessTokenJWSVerifier(JWSAlgorithm.RS256, HMAC_KEY)); + assertEquals("A key pair is required, in the 'private:public' format", ex.getMessage()); + } + + @Test + void unsupportedAlgorithm() throws Exception { + IllegalArgumentException ex = assertThrows( + IllegalArgumentException.class, + () -> new AccessTokenJWSVerifier(JWSAlgorithm.ES256, HMAC_KEY)); + assertEquals("Unsupported JWS algorithm: ES256", ex.getMessage()); + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/jws/MSEntraAccessTokenJWSVerifierTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/jws/MSEntraAccessTokenJWSVerifierTest.java index a936ccb1a7e..bd644840954 100644 --- a/core/spring/src/test/java/org/apache/syncope/core/spring/security/jws/MSEntraAccessTokenJWSVerifierTest.java +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/jws/MSEntraAccessTokenJWSVerifierTest.java @@ -32,7 +32,7 @@ import javax.cache.expiry.Duration; import org.junit.jupiter.api.Test; -public class MSEntraAccessTokenJWSVerifierTest extends AbstractTest { +class MSEntraAccessTokenJWSVerifierTest extends AbstractTest { private static class TestMSEntraAccessTokenJWSVerifier extends MSEntraJWSVerifierCacheLoader { diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/jws/MSEntraJWSVerifierCacheLoaderTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/jws/MSEntraJWSVerifierCacheLoaderTest.java index 0dd6816d6a7..2e9198d123d 100644 --- a/core/spring/src/test/java/org/apache/syncope/core/spring/security/jws/MSEntraJWSVerifierCacheLoaderTest.java +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/jws/MSEntraJWSVerifierCacheLoaderTest.java @@ -31,7 +31,7 @@ import java.util.Map; import org.junit.jupiter.api.Test; -public class MSEntraJWSVerifierCacheLoaderTest extends AbstractTest { +class MSEntraJWSVerifierCacheLoaderTest extends AbstractTest { @Test void getOpenIDMetadataDocumentUrl() { diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/throttle/PasswordResetRequestThrottlerTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/throttle/PasswordResetRequestThrottlerTest.java new file mode 100644 index 00000000000..8381513864a --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/throttle/PasswordResetRequestThrottlerTest.java @@ -0,0 +1,173 @@ +/* + * 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.core.spring.security.throttle; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.LongSupplier; +import javax.cache.Cache; +import javax.cache.Caching; +import javax.cache.configuration.MutableConfiguration; +import javax.cache.expiry.Duration; +import javax.cache.expiry.TouchedExpiryPolicy; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.core.spring.security.SecurityProperties; +import org.junit.jupiter.api.Test; + +class PasswordResetRequestThrottlerTest { + + private static final String DOMAIN = SyncopeConstants.MASTER_DOMAIN; + + private static final String USERNAME = "rossini"; + + private static final String CLIENT = "127.0.0.1"; + + private static SecurityProperties securityProperties(final boolean enabled) { + SecurityProperties securityProperties = new SecurityProperties(); + securityProperties.getPasswordResetThrottle().setEnabled(enabled); + securityProperties.getPasswordResetThrottle().setMaxAttempts(3); + securityProperties.getPasswordResetThrottle().setWindowSeconds(60); + securityProperties.getPasswordResetThrottle().setLockSeconds(30); + return securityProperties; + } + + private static PasswordResetRequestThrottler throttler( + final SecurityProperties securityProperties, + final LongSupplier clock) { + + Cache cache = + Caching.getCachingProvider().getCacheManager().getCache(PasswordResetRequestThrottler.CACHE); + if (cache == null) { + cache = Caching.getCachingProvider().getCacheManager().createCache( + PasswordResetRequestThrottler.CACHE, + new MutableConfiguration(). + setTypes(String.class, ThrottlerAttempts.class). + setExpiryPolicyFactory( + TouchedExpiryPolicy.factoryOf(new Duration(TimeUnit.SECONDS, 30)))); + } else { + cache.clear(); + } + + return new PasswordResetRequestThrottler(securityProperties, cache) { + + @Override + protected long now() { + return clock.getAsLong(); + } + }; + } + + @Test + void disabledThrottlerNeverBlocks() { + PasswordResetRequestThrottler throttler = throttler(securityProperties(false), () -> 0L); + + for (int i = 0; i < 10; i++) { + assertDoesNotThrow(() -> throttler.recordAndCheck(DOMAIN, USERNAME, CLIENT)); + } + } + + @Test + void blocksAfterConfiguredAttempts() { + AtomicLong now = new AtomicLong(1_000_000L); + PasswordResetRequestThrottler throttler = throttler(securityProperties(true), now::get); + + for (int i = 0; i < 3; i++) { + assertDoesNotThrow(() -> throttler.recordAndCheck(DOMAIN, USERNAME, CLIENT)); + } + + PasswordResetThrottleException ex = assertThrows( + PasswordResetThrottleException.class, + () -> throttler.recordAndCheck(DOMAIN, USERNAME, CLIENT)); + assertEquals(30, ex.getRetryAfterSeconds()); + + assertThrows( + PasswordResetThrottleException.class, + () -> throttler.recordAndCheck(DOMAIN, USERNAME, CLIENT)); + } + + @Test + void blockedClientUnblocksAfterLockPeriod() { + AtomicLong now = new AtomicLong(1_000_000L); + PasswordResetRequestThrottler throttler = throttler(securityProperties(true), now::get); + + for (int i = 0; i < 4; i++) { + try { + throttler.recordAndCheck(DOMAIN, USERNAME, CLIENT); + } catch (PasswordResetThrottleException e) { + // expected on the fourth attempt + } + } + + now.addAndGet(TimeUnit.SECONDS.toMillis(61)); + assertDoesNotThrow(() -> throttler.recordAndCheck(DOMAIN, USERNAME, CLIENT)); + } + + @Test + void failuresOutsideWindowArePruned() { + AtomicLong now = new AtomicLong(1_000_000L); + PasswordResetRequestThrottler throttler = throttler(securityProperties(true), now::get); + + assertDoesNotThrow(() -> throttler.recordAndCheck(DOMAIN, USERNAME, CLIENT)); + now.addAndGet(TimeUnit.SECONDS.toMillis(61)); + + assertDoesNotThrow(() -> throttler.recordAndCheck(DOMAIN, USERNAME, CLIENT)); + assertDoesNotThrow(() -> throttler.recordAndCheck(DOMAIN, USERNAME, CLIENT)); + assertDoesNotThrow(() -> throttler.recordAndCheck(DOMAIN, USERNAME, CLIENT)); + } + + @Test + void keysAreIndependent() { + PasswordResetRequestThrottler throttler = throttler(securityProperties(true), () -> 1_000_000L); + + for (int i = 0; i < 4; i++) { + try { + throttler.recordAndCheck(DOMAIN, USERNAME, CLIENT); + } catch (PasswordResetThrottleException e) { + // expected on the fourth attempt + } + } + + assertDoesNotThrow(() -> throttler.recordAndCheck(DOMAIN, "other", CLIENT)); + assertDoesNotThrow(() -> throttler.recordAndCheck(DOMAIN, USERNAME, "10.0.0.1")); + assertDoesNotThrow(() -> throttler.recordAndCheck("other", USERNAME, CLIENT)); + } + + @Test + void keyHandlesNullValues() { + assertEquals("::", PasswordResetRequestThrottler.key(null, null, null)); + assertEquals( + PasswordResetRequestThrottler.key(DOMAIN, USERNAME, CLIENT), + PasswordResetRequestThrottler.key(DOMAIN, USERNAME, CLIENT)); + } + + @Test + void misconfiguredThrottlerIsDisabled() { + SecurityProperties securityProperties = securityProperties(true); + securityProperties.getPasswordResetThrottle().setMaxAttempts(0); + PasswordResetRequestThrottler throttler = throttler(securityProperties, () -> 1_000_000L); + + for (int i = 0; i < 10; i++) { + assertDoesNotThrow(() -> throttler.recordAndCheck(DOMAIN, USERNAME, CLIENT)); + } + } +} diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/task/VirtualThreadPoolTaskExecutorTest.java b/core/spring/src/test/java/org/apache/syncope/core/spring/task/VirtualThreadPoolTaskExecutorTest.java new file mode 100644 index 00000000000..73f3845630c --- /dev/null +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/task/VirtualThreadPoolTaskExecutorTest.java @@ -0,0 +1,88 @@ +/* + * 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.core.spring.task; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.jupiter.api.Test; + +class VirtualThreadPoolTaskExecutorTest { + + @Test + void executesTasks() throws Exception { + VirtualThreadPoolTaskExecutor executor = new VirtualThreadPoolTaskExecutor(); + executor.setPoolSize(2); + executor.setTaskTerminationTimeout(1_000); + executor.afterPropertiesSet(); + + try { + CountDownLatch latch = new CountDownLatch(1); + executor.execute(latch::countDown); + + assertTrue(latch.await(5, TimeUnit.SECONDS)); + } finally { + executor.shutdown(); + } + } + + @Test + void submitsCallableAndRunnable() throws Exception { + VirtualThreadPoolTaskExecutor executor = new VirtualThreadPoolTaskExecutor(); + executor.afterPropertiesSet(); + + try { + Future result = executor.submit(() -> "done"); + assertEquals("done", result.get(5, TimeUnit.SECONDS)); + + CountDownLatch latch = new CountDownLatch(1); + Future runnable = executor.submit(latch::countDown); + runnable.get(5, TimeUnit.SECONDS); + assertTrue(latch.await(5, TimeUnit.SECONDS)); + } finally { + executor.shutdown(); + } + } + + @Test + void appliesTaskDecorator() throws Exception { + AtomicBoolean decorated = new AtomicBoolean(); + + VirtualThreadPoolTaskExecutor executor = new VirtualThreadPoolTaskExecutor(); + executor.setTaskDecorator(task -> () -> { + decorated.set(true); + task.run(); + }); + executor.afterPropertiesSet(); + + try { + CountDownLatch latch = new CountDownLatch(1); + executor.execute(latch::countDown); + + assertTrue(latch.await(5, TimeUnit.SECONDS)); + assertTrue(decorated.get()); + } finally { + executor.shutdown(); + } + } +} diff --git a/core/starter/pom.xml b/core/starter/pom.xml index fd696ffaa91..b627a88d6fe 100644 --- a/core/starter/pom.xml +++ b/core/starter/pom.xml @@ -106,6 +106,18 @@ under the License. swagger-ui provided + + + + org.mockito + mockito-junit-jupiter + test + + + org.junit.jupiter + junit-jupiter + test + diff --git a/core/starter/src/main/resources/core.properties b/core/starter/src/main/resources/core.properties index b65b6eb6259..606cb7cf9dc 100644 --- a/core/starter/src/main/resources/core.properties +++ b/core/starter/src/main/resources/core.properties @@ -64,6 +64,9 @@ provisioning.asyncConnectorFacadeExecutor.poolSize=25 provisioning.propagationTaskExecutorAsyncExecutor.poolSize=25 +provisioning.resultHandlerDispatcher.submitTimeoutSeconds=300 +provisioning.resultHandlerDispatcher.completionTimeoutSeconds=600 + provisioning.connIdLocation=${syncope.connid.location} ######### diff --git a/core/starter/src/test/java/org/apache/syncope/core/starter/ProductionModeCheckerTest.java b/core/starter/src/test/java/org/apache/syncope/core/starter/ProductionModeCheckerTest.java new file mode 100644 index 00000000000..8d70046403d --- /dev/null +++ b/core/starter/src/test/java/org/apache/syncope/core/starter/ProductionModeCheckerTest.java @@ -0,0 +1,94 @@ +/* + * 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.core.starter; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.Logger; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.Property; +import org.junit.jupiter.api.Test; + +class ProductionModeCheckerTest { + + private static final String WARNING = "not running in Production Mode"; + + private static final class CapturingAppender extends AbstractAppender { + + private final List events = new ArrayList<>(); + + private CapturingAppender() { + super("ProductionModeCheckerTest", null, null, true, Property.EMPTY_ARRAY); + } + + @Override + public void append(final LogEvent event) { + events.add(event.toImmutable()); + } + + private List getEvents() { + return events; + } + } + + private List check(final boolean productionMode) { + ProductionModeChecker checker = new ProductionModeChecker(); + checker.productionMode = productionMode; + + @SuppressWarnings("unchecked") + Logger logger = (Logger) LogManager.getLogger(ProductionModeChecker.class); + Level previousLevel = logger.getLevel(); + logger.setLevel(Level.WARN); + + CapturingAppender appender = new CapturingAppender(); + appender.start(); + logger.addAppender(appender); + try { + checker.check(); + } finally { + logger.removeAppender(appender); + appender.stop(); + logger.setLevel(previousLevel); + } + + return appender.getEvents(); + } + + @Test + void doesNotWarnInProductionMode() { + List events = check(true); + + assertFalse(events.stream().anyMatch( + event -> event.getMessage().getFormattedMessage().contains(WARNING))); + } + + @Test + void warnsWhenNotInProductionMode() { + List events = check(false); + + assertTrue(events.stream().anyMatch( + event -> event.getMessage().getFormattedMessage().contains(WARNING))); + } +} diff --git a/core/starter/src/test/java/org/apache/syncope/core/starter/TaskExecutorUnloaderTest.java b/core/starter/src/test/java/org/apache/syncope/core/starter/TaskExecutorUnloaderTest.java new file mode 100644 index 00000000000..826768f631e --- /dev/null +++ b/core/starter/src/test/java/org/apache/syncope/core/starter/TaskExecutorUnloaderTest.java @@ -0,0 +1,84 @@ +/* + * 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.core.starter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.scheduling.concurrent.ExecutorConfigurationSupport; + +class TaskExecutorUnloaderTest { + + private ListableBeanFactory beanFactory; + + private TaskExecutorUnloader unloader; + + @BeforeEach + void setUp() { + beanFactory = mock(ListableBeanFactory.class); + unloader = new TaskExecutorUnloader(beanFactory); + } + + @Test + void orderIsHighestPriority() { + assertEquals(Integer.MIN_VALUE, unloader.getOrder()); + } + + @Test + void unloadShutsDownLoadedExecutors() { + ExecutorConfigurationSupport executor1 = mock(ExecutorConfigurationSupport.class); + ExecutorConfigurationSupport executor2 = mock(ExecutorConfigurationSupport.class); + when(beanFactory.getBeansOfType(ExecutorConfigurationSupport.class)). + thenReturn(Map.of("executor1", executor1, "executor2", executor2)); + + unloader.load(); + unloader.unload(); + + verify(executor1).shutdown(); + verify(executor2).shutdown(); + } + + @Test + void unloadWithoutLoadDoesNothing() { + unloader.unload(); + + verifyNoInteractions(beanFactory); + } + + @Test + void unloadDoesNotPropagateExecutorShutdownFailure() { + ExecutorConfigurationSupport failing = mock(ExecutorConfigurationSupport.class); + doThrow(new IllegalStateException("boom")).when(failing).shutdown(); + when(beanFactory.getBeansOfType(ExecutorConfigurationSupport.class)).thenReturn(Map.of("failing", failing)); + + unloader.load(); + + unloader.unload(); + + verify(failing).shutdown(); + } +} diff --git a/core/starter/src/test/java/org/apache/syncope/core/starter/actuate/DomainsHealthIndicatorTest.java b/core/starter/src/test/java/org/apache/syncope/core/starter/actuate/DomainsHealthIndicatorTest.java new file mode 100644 index 00000000000..dae0c1fedc7 --- /dev/null +++ b/core/starter/src/test/java/org/apache/syncope/core/starter/actuate/DomainsHealthIndicatorTest.java @@ -0,0 +1,77 @@ +/* + * 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.core.starter.actuate; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.HashMap; +import java.util.Map; +import org.apache.syncope.core.persistence.api.DomainHolder; +import org.junit.jupiter.api.Test; +import org.springframework.boot.health.contributor.Health; +import org.springframework.boot.health.contributor.Status; + +class DomainsHealthIndicatorTest { + + private static final class TestDomainHolder implements DomainHolder { + + private final Map healthInfo; + + TestDomainHolder(final Map healthInfo) { + this.healthInfo = healthInfo; + } + + @Override + public Map getDomains() { + Map domains = new HashMap<>(); + healthInfo.keySet().forEach(domain -> domains.put(domain, domain)); + return domains; + } + + @Override + public Map getHealthInfo() { + return healthInfo; + } + } + + private Health health(final Map healthInfo) { + return new DomainsHealthIndicator(new TestDomainHolder(healthInfo)).health(); + } + + @Test + void isUpWhenAllDomainsAreUp() { + Health result = health(Map.of("Master", true, "Other", true)); + + assertEquals(Status.UP, result.getStatus()); + assertEquals(Map.of("Master", Status.UP, "Other", Status.UP), result.getDetails()); + } + + @Test + void isDownWhenAnyDomainIsDown() { + Health result = health(Map.of("Master", true, "Other", false)); + + assertEquals(Status.DOWN, result.getStatus()); + assertEquals(Map.of("Master", Status.UP, "Other", Status.DOWN), result.getDetails()); + } + + @Test + void isUpWhenNoDomainIsConfigured() { + assertEquals(Status.UP, health(Map.of()).getStatus()); + } +} diff --git a/core/starter/src/test/java/org/apache/syncope/core/starter/actuate/EntityCacheEndpointTest.java b/core/starter/src/test/java/org/apache/syncope/core/starter/actuate/EntityCacheEndpointTest.java new file mode 100644 index 00000000000..7bb52020f5b --- /dev/null +++ b/core/starter/src/test/java/org/apache/syncope/core/starter/actuate/EntityCacheEndpointTest.java @@ -0,0 +1,100 @@ +/* + * 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.core.starter.actuate; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; +import org.apache.syncope.core.persistence.api.dao.EntityCacheDAO; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; + +class EntityCacheEndpointTest { + + private EntityCacheDAO entityCacheDAO; + + private EntityCacheEndpoint endpoint; + + @BeforeEach + void setUp() { + entityCacheDAO = mock(EntityCacheDAO.class); + endpoint = new EntityCacheEndpoint(entityCacheDAO); + } + + @Test + void statisticsDelegatesToDao() { + Map statistics = Map.of("hits", 1L); + when(entityCacheDAO.getStatistics()).thenReturn(statistics); + + assertSame(statistics, endpoint.statistics()); + } + + @Test + void writeOperationEnablesStatistics() { + endpoint.statistics("enable"); + verify(entityCacheDAO).enableStatistics(); + } + + @Test + void writeOperationEnablesStatisticsIgnoringCase() { + endpoint.statistics("ENABLE"); + verify(entityCacheDAO).enableStatistics(); + } + + @Test + void writeOperationDisablesStatistics() { + endpoint.statistics("disable"); + verify(entityCacheDAO).disableStatistics(); + } + + @Test + void writeOperationResetsStatistics() { + endpoint.statistics("reset"); + verify(entityCacheDAO).resetStatistics(); + } + + @Test + void writeOperationResetsStatisticsIgnoringCase() { + endpoint.statistics("RESET"); + verify(entityCacheDAO).resetStatistics(); + } + + @Test + void writeOperationRejectsUnknownOperation() { + ResponseStatusException exception = + assertThrows(ResponseStatusException.class, () -> endpoint.statistics("unknown")); + + assertEquals(HttpStatus.NOT_FOUND, exception.getStatusCode()); + assertTrue(exception.getReason().contains("unknown")); + } + + @Test + void clearCacheDelegatesToDao() { + endpoint.clearCache(); + verify(entityCacheDAO).clearCache(); + } +} diff --git a/core/starter/src/test/java/org/apache/syncope/core/starter/actuate/ExternalResourcesHealthIndicatorTest.java b/core/starter/src/test/java/org/apache/syncope/core/starter/actuate/ExternalResourcesHealthIndicatorTest.java new file mode 100644 index 00000000000..b08bdb4b4b9 --- /dev/null +++ b/core/starter/src/test/java/org/apache/syncope/core/starter/actuate/ExternalResourcesHealthIndicatorTest.java @@ -0,0 +1,123 @@ +/* + * 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.core.starter.actuate; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.common.keymaster.client.api.DomainOps; +import org.apache.syncope.common.keymaster.client.api.model.Domain; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.entity.ConnInstance; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.provisioning.api.Connector; +import org.apache.syncope.core.provisioning.api.ConnectorManager; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.springframework.boot.health.contributor.Health; +import org.springframework.boot.health.contributor.Status; + +class ExternalResourcesHealthIndicatorTest { + + private static ExternalResource resource(final String key) { + ExternalResource resource = mock(ExternalResource.class); + when(resource.getKey()).thenReturn(key); + when(resource.getConnector()).thenReturn(mock(ConnInstance.class)); + when(resource.getConfOverride()).thenReturn(Optional.empty()); + when(resource.getCapabilitiesOverride()).thenReturn(Optional.empty()); + return resource; + } + + private static Connector connectorFor(final boolean healthy) { + Connector connector = mock(Connector.class); + if (!healthy) { + doThrow(new IllegalStateException("connection failed")).when(connector).test(); + } + return connector; + } + + private Health health(final List domains, final ExternalResourceDAO resourceDAO, final boolean healthy) { + DomainOps domainOps = mock(DomainOps.class); + when(domainOps.list()).thenReturn(domains); + + ConnectorManager connectorManager = mock(ConnectorManager.class); + ConnInstance connInstance = mock(ConnInstance.class); + Connector connector = connectorFor(healthy); + when(connectorManager.buildConnInstanceOverride(any(), any(), any())).thenReturn(connInstance); + when(connectorManager.createConnector(connInstance)).thenReturn(connector); + + try (MockedStatic authContextUtils = mockStatic(AuthContextUtils.class)) { + authContextUtils.when(() -> AuthContextUtils.runAsAdmin(anyString(), any(Runnable.class))). + thenAnswer(invocation -> { + invocation.getArgument(1, Runnable.class).run(); + return null; + }); + + return new ExternalResourcesHealthIndicator(domainOps, resourceDAO, connectorManager).health(); + } + } + + @Test + void isUpWhenAllResourcesAreHealthy() { + ExternalResourceDAO resourceDAO = mock(ExternalResourceDAO.class); + doReturn(List.of(resource("res1"), resource("res2"))).when(resourceDAO).findAll(); + + Health result = health(List.of(), resourceDAO, true); + + assertEquals(Status.UP, result.getStatus()); + assertEquals( + Set.of("Master#res1", "Master#res2"), + result.getDetails().keySet()); + assertEquals(Status.UP, result.getDetails().get("Master#res1")); + assertEquals(Status.UP, result.getDetails().get("Master#res2")); + } + + @Test + void isDownWhenAnyResourceIsUnhealthy() { + Domain domain = mock(Domain.class); + when(domain.getKey()).thenReturn("Other"); + + ExternalResourceDAO resourceDAO = mock(ExternalResourceDAO.class); + doReturn(List.of(resource("res1"))).when(resourceDAO).findAll(); + + Health result = health(List.of(domain), resourceDAO, false); + + assertEquals(Status.DOWN, result.getStatus()); + assertEquals(Map.of("Master#res1", Status.DOWN, "Other#res1", Status.DOWN), result.getDetails()); + } + + @Test + void isUpWhenNoResourceIsConfigured() { + ExternalResourceDAO resourceDAO = mock(ExternalResourceDAO.class); + when(resourceDAO.findAll()).thenReturn(List.of()); + + assertEquals(Status.UP, health(List.of(), resourceDAO, true).getStatus()); + } +} diff --git a/core/starter/src/test/java/org/apache/syncope/core/starter/actuate/JobEndpointTest.java b/core/starter/src/test/java/org/apache/syncope/core/starter/actuate/JobEndpointTest.java new file mode 100644 index 00000000000..aa47c6b1779 --- /dev/null +++ b/core/starter/src/test/java/org/apache/syncope/core/starter/actuate/JobEndpointTest.java @@ -0,0 +1,178 @@ +/* + * 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.core.starter.actuate; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.Callable; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.JobAction; +import org.apache.syncope.core.persistence.api.dao.JobStatusDAO; +import org.apache.syncope.core.persistence.api.entity.JobStatus; +import org.apache.syncope.core.provisioning.api.job.JobExecutionContext; +import org.apache.syncope.core.provisioning.java.job.Job; +import org.apache.syncope.core.provisioning.java.job.SyncopeTaskScheduler; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +class JobEndpointTest { + + private JobStatusDAO jobStatusDAO; + + private JobEndpoint endpoint; + + @BeforeEach + void setUp() { + jobStatusDAO = mock(JobStatusDAO.class); + } + + private static Job job(final String jobName, final String executor, final boolean dryRun) { + Job job = mock(Job.class); + when(job.getContext()).thenReturn( + new JobExecutionContext( + SyncopeConstants.MASTER_DOMAIN, jobName, executor, dryRun, Map.of("k", "v"))); + return job; + } + + private static JobStatus jobStatus(final String key, final String status) { + JobStatus jobStatus = mock(JobStatus.class); + when(jobStatus.getKey()).thenReturn(key); + when(jobStatus.getStatus()).thenReturn(status); + return jobStatus; + } + + @SuppressWarnings("unchecked") + private static ScheduledFuture scheduledFuture( + final long delaySeconds, final boolean done, final boolean cancelled) { + ScheduledFuture future = mock(ScheduledFuture.class); + when(future.getDelay(TimeUnit.SECONDS)).thenReturn(delaySeconds); + when(future.isDone()).thenReturn(done); + when(future.isCancelled()).thenReturn(cancelled); + return future; + } + + private static SyncopeTaskScheduler schedulerWith( + final Map jobs, final List jobNames) { + + SyncopeTaskScheduler scheduler = mock(SyncopeTaskScheduler.class); + when(scheduler.getJobs()).thenReturn(jobs); + when(scheduler.getJobNames(SyncopeConstants.MASTER_DOMAIN)).thenReturn(jobNames); + return scheduler; + } + + @Test + @SuppressWarnings("unchecked") + void statusReportsScheduledJobsAndUnreconciledJobStatuses() { + ScheduledFuture instant = scheduledFuture(42L, false, false); + ScheduledFuture cron = scheduledFuture(7L, true, false); + SyncopeTaskScheduler scheduler = schedulerWith( + Map.of( + new SyncopeTaskScheduler.Key(SyncopeConstants.MASTER_DOMAIN, "job1"), + new SyncopeTaskScheduler.Value( + job("job1", "executor1", true), Optional.of(instant), Optional.of(cron))), + List.of("job1")); + doReturn(List.of(jobStatus("job1", "Scheduled"), jobStatus("job2", "Running"))). + when(jobStatusDAO).findAll(); + endpoint = new JobEndpoint(scheduler, jobStatusDAO); + + Map status; + try (MockedStatic auth = mockStatic(AuthContextUtils.class)) { + auth.when(() -> AuthContextUtils.callAsAdmin(anyString(), any(Callable.class))). + thenAnswer(invocation -> { + Callable callable = invocation.getArgument(1); + try { + return callable.call(); + } catch (final Exception e) { + throw new IllegalStateException(e); + } + }); + status = endpoint.status(); + } + + Map domainJobs = (Map) status.get(SyncopeConstants.MASTER_DOMAIN); + Map job = (Map) domainJobs.get("job1"); + assertEquals("executor1", job.get("executor")); + assertEquals(Boolean.TRUE, job.get("dryRun")); + assertEquals(Map.of("k", "v"), job.get("context")); + assertEquals(42L, job.get("delay (seconds)")); + assertEquals(7L, job.get("next schedule (seconds)")); + assertEquals(Boolean.TRUE, job.get("done")); + assertEquals(Boolean.FALSE, job.get("cancelled")); + + Map unreconciled = (Map) status.get("unreconciled"); + Map unreconciledJobs = (Map) unreconciled.get(SyncopeConstants.MASTER_DOMAIN); + assertEquals("Running", unreconciledJobs.get("job2")); + assertFalse(unreconciledJobs.containsKey("job1")); + } + + @Test + void statusWithoutJobsIsEmpty() { + SyncopeTaskScheduler scheduler = schedulerWith(Map.of(), List.of()); + endpoint = new JobEndpoint(scheduler, jobStatusDAO); + + assertTrue(endpoint.status().isEmpty()); + } + + @Test + void actionStartsStopsAndDeletesJobs() { + SyncopeTaskScheduler scheduler = mock(SyncopeTaskScheduler.class); + endpoint = new JobEndpoint(scheduler, jobStatusDAO); + + endpoint.action(SyncopeConstants.MASTER_DOMAIN, "job1", JobAction.START); + verify(scheduler).start(SyncopeConstants.MASTER_DOMAIN, "job1"); + + endpoint.action(SyncopeConstants.MASTER_DOMAIN, "job1", JobAction.STOP); + verify(scheduler).stop(SyncopeConstants.MASTER_DOMAIN, "job1"); + + endpoint.action(SyncopeConstants.MASTER_DOMAIN, "job1", JobAction.DELETE); + verify(scheduler).delete(SyncopeConstants.MASTER_DOMAIN, "job1"); + } + + @Test + void forceUnlockReleasesJobStatusLock() { + SyncopeTaskScheduler scheduler = mock(SyncopeTaskScheduler.class); + endpoint = new JobEndpoint(scheduler, jobStatusDAO); + + try (MockedStatic auth = mockStatic(AuthContextUtils.class)) { + auth.when(() -> AuthContextUtils.runAsAdmin(anyString(), any(Runnable.class))).thenAnswer(ic -> { + ic.getArgument(1, Runnable.class).run(); + return null; + }); + endpoint.forceUnlock(SyncopeConstants.MASTER_DOMAIN, "job1"); + } + + verify(jobStatusDAO).unlock("job1"); + } +} diff --git a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/AbstractTest.java b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/AbstractTest.java index f4393ec0e90..39fec193af9 100644 --- a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/AbstractTest.java +++ b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/AbstractTest.java @@ -21,15 +21,26 @@ import static org.junit.jupiter.api.Assertions.fail; import io.zonky.test.db.postgres.embedded.EmbeddedPostgres; +import java.util.List; import java.util.function.Supplier; +import java.util.stream.Collectors; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; import org.apache.syncope.core.persistence.jpa.MasterDomain; +import org.apache.syncope.core.spring.security.SyncopeAuthenticationDetails; +import org.apache.syncope.core.spring.security.SyncopeGrantedAuthority; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; @SpringJUnitConfig(classes = { MasterDomain.class, WorkflowTestContext.class }) -public abstract class AbstractTest { +abstract class AbstractTest { private static Supplier JDBC_URL_SUPPLIER; @@ -56,4 +67,23 @@ static void configureProperties(final DynamicPropertyRegistry registry) { registry.add("DB_USER", DB_CRED_SUPPLIER); registry.add("DB_PASSWORD", DB_CRED_SUPPLIER); } + + @BeforeAll + static void setAuthContext() { + List authorities = IdRepoEntitlement.values().stream(). + map(entitlement -> new SyncopeGrantedAuthority(entitlement, SyncopeConstants.ROOT_REALM)). + collect(Collectors.toList()); + authorities.add(new SyncopeGrantedAuthority("PRINTER_READ", SyncopeConstants.ROOT_REALM)); + + UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken( + new org.springframework.security.core.userdetails.User( + "admin", "FAKE_PASSWORD", authorities), "FAKE_PASSWORD", authorities); + auth.setDetails(new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, null)); + SecurityContextHolder.getContext().setAuthentication(auth); + } + + @AfterAll + static void unsetAuthContext() { + SecurityContextHolder.getContext().setAuthentication(null); + } } diff --git a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/DefaultAnyObjectWorkflowAdapterTest.java b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/DefaultAnyObjectWorkflowAdapterTest.java new file mode 100644 index 00000000000..07b9b26de2a --- /dev/null +++ b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/DefaultAnyObjectWorkflowAdapterTest.java @@ -0,0 +1,93 @@ +/* + * 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.core.workflow.java; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.NoSuchElementException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.request.AnyObjectCR; +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.provisioning.api.WorkflowResult; +import org.apache.syncope.core.workflow.api.AnyObjectWorkflowAdapter; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +class DefaultAnyObjectWorkflowAdapterTest extends AbstractTest { + + @Autowired + private AnyObjectDAO anyObjectDAO; + + @Autowired + private AnyObjectWorkflowAdapter awfAdapter; + + @Test + void createAnyObject() { + // Use the Builder pattern to set type, as required by AnyObjectCR + AnyObjectCR anyObjectCR = new AnyObjectCR.Builder( + SyncopeConstants.ROOT_REALM, "PRINTER", "testAnyObject").build(); + + WorkflowResult result = awfAdapter.create(anyObjectCR, "admin", "test"); + assertNotNull(result.getResult()); + + AnyObject anyObject = anyObjectDAO.findById(result.getResult()).orElseThrow(); + assertEquals("testAnyObject", anyObject.getName()); + } + + @Test + void createAnyObjectWithInvalidRealm() { + // Use the Builder pattern with invalid realm - this tests the validation path + AnyObjectCR anyObjectCR = new AnyObjectCR.Builder( + "/nonexistent", "PRINTER", "testAnyObject").build(); + + assertThrows(NoSuchElementException.class, () -> awfAdapter.create(anyObjectCR, "admin", "test")); + } + + @Test + void createAnyObjectWithInvalidAnyType() { + // Use the Builder pattern with invalid any type - this tests the validation path + AnyObjectCR anyObjectCR = new AnyObjectCR.Builder( + SyncopeConstants.ROOT_REALM, "nonexistent", "testAnyObject").build(); + + assertThrows(NoSuchElementException.class, () -> awfAdapter.create(anyObjectCR, "admin", "test")); + } + + @Test + void deleteAnyObject() { + // First create an anyobject using Builder pattern + AnyObjectCR anyObjectCR = new AnyObjectCR.Builder( + SyncopeConstants.ROOT_REALM, "PRINTER", "anyObjectToDelete").build(); + + WorkflowResult createResult = awfAdapter.create(anyObjectCR, "admin", "test"); + + assertTrue(anyObjectDAO.findById(createResult.getResult()).isPresent()); + + // Now delete it using the key from the result + awfAdapter.delete(createResult.getResult(), "admin", "test"); + + // Verify it's deleted + assertTrue(anyObjectDAO.findById(createResult.getResult()).isEmpty()); + } +} diff --git a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/DefaultGroupWorkflowAdapterTest.java b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/DefaultGroupWorkflowAdapterTest.java new file mode 100644 index 00000000000..48e778813f7 --- /dev/null +++ b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/DefaultGroupWorkflowAdapterTest.java @@ -0,0 +1,88 @@ +/* + * 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.core.workflow.java; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.NoSuchElementException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.request.GroupCR; +import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.provisioning.api.WorkflowResult; +import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +class DefaultGroupWorkflowAdapterTest extends AbstractTest { + + @Autowired + private GroupDAO groupDAO; + + @Autowired + private GroupWorkflowAdapter gwfAdapter; + + @Test + void createGroup() { + // Use the Builder pattern if available, or direct construction + GroupCR groupCR = new GroupCR(); + groupCR.setName("testGroup"); + groupCR.setRealm(SyncopeConstants.ROOT_REALM); + + WorkflowResult result = gwfAdapter.create(groupCR, "admin", "test"); + + assertNotNull(result.getResult()); + Group group = groupDAO.findById(result.getResult()).orElseThrow(); + assertEquals("testGroup", group.getName()); + } + + @Test + void createGroupWithInvalidRealm() { + // Use direct construction with invalid realm + GroupCR groupCR = new GroupCR(); + groupCR.setName("testGroup"); + groupCR.setRealm("/nonexistent"); + + assertThrows(NoSuchElementException.class, () -> gwfAdapter.create(groupCR, "admin", "test")); + } + + @Test + void deleteGroup() { + // First create a group + GroupCR groupCR = new GroupCR(); + groupCR.setName("groupToDelete"); + groupCR.setRealm(SyncopeConstants.ROOT_REALM); + + WorkflowResult createResult = gwfAdapter.create(groupCR, "admin", "test"); + assertNotNull(createResult.getResult()); + + assertTrue(groupDAO.findById(createResult.getResult()).isPresent()); + + // Now delete it + gwfAdapter.delete(createResult.getResult(), "admin", "test"); + + // Verify it's deleted + assertTrue(groupDAO.findById(createResult.getResult()).isEmpty()); + } +} diff --git a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/DefaultUserWorkflowAdapterTest.java b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/DefaultUserWorkflowAdapterTest.java index 89ce2cf7e5b..59c07ed255f 100644 --- a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/DefaultUserWorkflowAdapterTest.java +++ b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/DefaultUserWorkflowAdapterTest.java @@ -21,48 +21,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import java.util.List; -import java.util.stream.Collectors; import org.apache.commons.lang3.tuple.Pair; -import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.request.UserCR; -import org.apache.syncope.common.lib.types.IdRepoEntitlement; import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.provisioning.api.UserWorkflowResult; -import org.apache.syncope.core.spring.security.SyncopeAuthenticationDetails; -import org.apache.syncope.core.spring.security.SyncopeGrantedAuthority; import org.apache.syncope.core.workflow.api.UserWorkflowAdapter; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.transaction.annotation.Transactional; @Transactional -public class DefaultUserWorkflowAdapterTest extends AbstractTest { - - @BeforeAll - public static void setAuthContext() { - List authorities = IdRepoEntitlement.values().stream(). - map(entitlement -> new SyncopeGrantedAuthority(entitlement, SyncopeConstants.ROOT_REALM)). - collect(Collectors.toList()); - - UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken( - new org.springframework.security.core.userdetails.User( - "admin", "FAKE_PASSWORD", authorities), "FAKE_PASSWORD", authorities); - auth.setDetails(new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, null)); - SecurityContextHolder.getContext().setAuthentication(auth); - } - - @AfterAll - public static void unsetAuthContext() { - SecurityContextHolder.getContext().setAuthentication(null); - } +class DefaultUserWorkflowAdapterTest extends AbstractTest { @Autowired private UserWorkflowAdapter uwfAdapter; @@ -71,7 +42,7 @@ public static void unsetAuthContext() { private UserDAO userDAO; @Test - public void createInvalidPassword() { + void createInvalidPassword() { UserCR userCR = new UserCR(); userCR.setUsername("username"); userCR.setRealm("/even/two"); @@ -81,7 +52,7 @@ public void createInvalidPassword() { } @Test - public void createInvalidUsername() { + void createInvalidUsername() { UserCR userCR = new UserCR(); userCR.setUsername("username!"); userCR.setRealm("/even/two"); @@ -91,7 +62,7 @@ public void createInvalidUsername() { } @Test - public void passwordHistory() { + void passwordHistory() { UserCR userCR = new UserCR(); userCR.setUsername("username"); userCR.setRealm("/even/two"); diff --git a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/WorkflowTestContext.java b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/WorkflowTestContext.java index 678bf5c6c01..64183c26cf5 100644 --- a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/WorkflowTestContext.java +++ b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/WorkflowTestContext.java @@ -28,11 +28,16 @@ import org.apache.syncope.common.keymaster.client.api.ConfParamOps; import org.apache.syncope.common.keymaster.client.api.DomainOps; import org.apache.syncope.common.keymaster.client.api.model.JPADomain; +import org.apache.syncope.common.lib.request.AnyObjectCR; +import org.apache.syncope.common.lib.request.GroupCR; import org.apache.syncope.common.lib.request.UserCR; import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.apache.syncope.core.persistence.api.DomainRegistry; import org.apache.syncope.core.persistence.api.content.ContentLoader; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.jpa.MariaDBPersistenceContext; import org.apache.syncope.core.persistence.jpa.MySQLPersistenceContext; @@ -97,13 +102,42 @@ public UserDataBinder userDataBinder(final RealmSearchDAO realmSearchDAO) { } @Bean - public GroupDataBinder groupDataBinder() { - return mock(GroupDataBinder.class); + public GroupDataBinder groupDataBinder(final RealmSearchDAO realmSearchDAO) { + GroupDataBinder dataBinder = mock(GroupDataBinder.class); + + doAnswer(ic -> { + Group group = ic.getArgument(0); + GroupCR groupCR = ic.getArgument(1); + + group.setName(groupCR.getName()); + group.setRealm(realmSearchDAO.findByFullPath(groupCR.getRealm()).orElseThrow()); + group.setCreator("admin"); + group.setCreationDate(OffsetDateTime.now()); + + return null; + }).when(dataBinder).create(any(Group.class), any(GroupCR.class)); + + return dataBinder; } @Bean - public AnyObjectDataBinder anyObjectDataBinder() { - return mock(AnyObjectDataBinder.class); + public AnyObjectDataBinder anyObjectDataBinder(final RealmSearchDAO realmSearchDAO, final AnyTypeDAO anyTypeDAO) { + AnyObjectDataBinder dataBinder = mock(AnyObjectDataBinder.class); + + doAnswer(ic -> { + AnyObject anyObject = ic.getArgument(0); + AnyObjectCR anyObjectCR = ic.getArgument(1); + + anyObject.setName(anyObjectCR.getName()); + anyObject.setRealm(realmSearchDAO.findByFullPath(anyObjectCR.getRealm()).orElseThrow()); + anyObject.setType(anyTypeDAO.findById(anyObjectCR.getType()).orElseThrow()); + anyObject.setCreator("admin"); + anyObject.setCreationDate(OffsetDateTime.now()); + + return null; + }).when(dataBinder).create(any(AnyObject.class), any(AnyObjectCR.class)); + + return dataBinder; } @Bean diff --git a/ext/elasticsearch/client-elasticsearch/pom.xml b/ext/elasticsearch/client-elasticsearch/pom.xml index a44301b7f8b..12bd6133139 100644 --- a/ext/elasticsearch/client-elasticsearch/pom.xml +++ b/ext/elasticsearch/client-elasticsearch/pom.xml @@ -53,6 +53,28 @@ under the License. org.springframework.boot spring-boot-health + + + + org.slf4j + slf4j-simple + test + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-junit-jupiter + test + + + org.assertj + assertj-core + test + diff --git a/ext/elasticsearch/client-elasticsearch/src/test/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexLoaderTest.java b/ext/elasticsearch/client-elasticsearch/src/test/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexLoaderTest.java new file mode 100644 index 00000000000..2c8986e983b --- /dev/null +++ b/ext/elasticsearch/client-elasticsearch/src/test/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexLoaderTest.java @@ -0,0 +1,109 @@ +/* + * 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.ext.elasticsearch.client; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import co.elastic.clients.elasticsearch._types.mapping.TypeMapping; +import co.elastic.clients.elasticsearch.indices.IndexSettings; +import java.io.IOException; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.core.Ordered; + +@ExtendWith(MockitoExtension.class) +public class ElasticsearchIndexLoaderTest { + + @Mock + private ElasticsearchIndexManager indexManager; + + private ElasticsearchIndexLoader loader; + + @BeforeEach + public void setupLoader() { + loader = new ElasticsearchIndexLoader(indexManager); + } + + @Test + public void order() { + assertThat(loader.getOrder()).isEqualTo(Ordered.LOWEST_PRECEDENCE); + } + + @Test + public void loadMissingIndexes() throws Exception { + when(indexManager.existsAnyIndex("Master", AnyTypeKind.USER)).thenReturn(false); + when(indexManager.existsAnyIndex("Master", AnyTypeKind.GROUP)).thenReturn(false); + when(indexManager.existsAnyIndex("Master", AnyTypeKind.ANY_OBJECT)).thenReturn(false); + when(indexManager.existsRealmIndex("Master")).thenReturn(false); + when(indexManager.existsAuditIndex("Master")).thenReturn(false); + + IndexSettings settings = new IndexSettings.Builder().numberOfShards("1").numberOfReplicas("1").build(); + TypeMapping anyMapping = new TypeMapping.Builder().build(); + TypeMapping realmMapping = new TypeMapping.Builder().build(); + TypeMapping auditMapping = new TypeMapping.Builder().build(); + when(indexManager.defaultSettings()).thenReturn(settings); + when(indexManager.defaultAnyMapping()).thenReturn(anyMapping); + when(indexManager.defaultRealmMapping()).thenReturn(realmMapping); + when(indexManager.defaultAuditMapping()).thenReturn(auditMapping); + + loader.load("Master"); + + verify(indexManager).createAnyIndex("Master", AnyTypeKind.USER, settings, anyMapping); + verify(indexManager).createAnyIndex("Master", AnyTypeKind.GROUP, settings, anyMapping); + verify(indexManager).createAnyIndex("Master", AnyTypeKind.ANY_OBJECT, settings, anyMapping); + verify(indexManager).createRealmIndex("Master", settings, realmMapping); + verify(indexManager).createAuditIndex("Master", settings, auditMapping); + } + + @Test + public void loadExistingIndexes() throws Exception { + when(indexManager.existsAnyIndex("Master", AnyTypeKind.USER)).thenReturn(true); + when(indexManager.existsAnyIndex("Master", AnyTypeKind.GROUP)).thenReturn(true); + when(indexManager.existsAnyIndex("Master", AnyTypeKind.ANY_OBJECT)).thenReturn(true); + when(indexManager.existsRealmIndex("Master")).thenReturn(true); + when(indexManager.existsAuditIndex("Master")).thenReturn(true); + + loader.load("Master"); + + verify(indexManager, never()).createAnyIndex(anyString(), any(), any(), any()); + verify(indexManager, never()).createRealmIndex(anyString(), any(), any()); + verify(indexManager, never()).createAuditIndex(anyString(), any(), any()); + } + + @Test + public void loadFailureIsSwallowed() throws Exception { + when(indexManager.existsAnyIndex("Master", AnyTypeKind.USER)).thenThrow(new IOException("boom")); + + assertDoesNotThrow(() -> loader.load("Master")); + + verify(indexManager, never()).createAnyIndex(anyString(), any(), any(), any()); + verify(indexManager, never()).createRealmIndex(anyString(), any(), any()); + verify(indexManager, never()).createAuditIndex(anyString(), any(), any()); + } +} diff --git a/ext/elasticsearch/client-elasticsearch/src/test/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManagerTest.java b/ext/elasticsearch/client-elasticsearch/src/test/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManagerTest.java new file mode 100644 index 00000000000..68389f7bd0b --- /dev/null +++ b/ext/elasticsearch/client-elasticsearch/src/test/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManagerTest.java @@ -0,0 +1,296 @@ +/* + * 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.ext.elasticsearch.client; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.ElasticsearchException; +import co.elastic.clients.elasticsearch._types.ErrorCause; +import co.elastic.clients.elasticsearch._types.ErrorResponse; +import co.elastic.clients.elasticsearch._types.Refresh; +import co.elastic.clients.elasticsearch._types.mapping.Property; +import co.elastic.clients.elasticsearch._types.mapping.TypeMapping; +import co.elastic.clients.elasticsearch.core.DeleteRequest; +import co.elastic.clients.elasticsearch.core.IndexRequest; +import co.elastic.clients.elasticsearch.indices.CreateIndexRequest; +import co.elastic.clients.elasticsearch.indices.DeleteIndexRequest; +import co.elastic.clients.elasticsearch.indices.ElasticsearchIndicesClient; +import co.elastic.clients.elasticsearch.indices.ExistsRequest; +import co.elastic.clients.elasticsearch.indices.IndexSettings; +import co.elastic.clients.transport.endpoints.BooleanResponse; +import java.util.Map; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.AuditEvent; +import org.apache.syncope.core.persistence.api.entity.Entity; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.event.EntityLifecycleEvent; +import org.identityconnectors.framework.common.objects.SyncDeltaType; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class ElasticsearchIndexManagerTest { + + @Mock + private ElasticsearchClient client; + + @Mock + private ElasticsearchIndicesClient indices; + + @Mock + private ElasticsearchUtils elasticsearchUtils; + + private ElasticsearchIndexManager indexManager; + + @BeforeEach + public void setupIndexManager() { + indexManager = new ElasticsearchIndexManager(client, elasticsearchUtils, "3", "2"); + } + + private static ElasticsearchException indexAlreadyExists() { + return new ElasticsearchException("indices.create", new ErrorResponse.Builder(). + status(400). + error(new ErrorCause.Builder(). + type("resource_already_exists_exception"). + reason("already exists"). + build()). + build()); + } + + @Test + public void existsIndexes() throws Exception { + when(client.indices()).thenReturn(indices); + when(indices.exists(any(ExistsRequest.class))).thenReturn(new BooleanResponse(true)); + + assertThat(indexManager.existsAnyIndex("Master", AnyTypeKind.USER)).isTrue(); + assertThat(indexManager.existsRealmIndex("Master")).isTrue(); + assertThat(indexManager.existsAuditIndex("Master")).isTrue(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ExistsRequest.class); + verify(indices, times(3)).exists(captor.capture()); + assertThat(captor.getAllValues().get(0).index()).containsExactly("master_user"); + assertThat(captor.getAllValues().get(1).index()).containsExactly("master_realm"); + assertThat(captor.getAllValues().get(2).index()).containsExactly("master_audit"); + } + + @Test + public void defaultSettings() { + IndexSettings settings = indexManager.defaultSettings(); + + assertThat(settings.numberOfShards()).isEqualTo("3"); + assertThat(settings.numberOfReplicas()).isEqualTo("2"); + assertThat(settings.analysis()).isNotNull(); + } + + @Test + public void defaultMappings() { + TypeMapping anyMapping = indexManager.defaultAnyMapping(); + assertThat(anyMapping.properties()).isEmpty(); + assertThat(anyMapping.dynamicTemplates()).hasSize(1); + assertThat(anyMapping.dynamicTemplates().getFirst().name()).isEqualTo("strings"); + assertThat(anyMapping.dynamicTemplates().getFirst().value().matchMappingType()).containsExactly("string"); + assertThat(anyMapping.dynamicTemplates().getFirst().value().mapping()._kind()). + isEqualTo(Property.Kind.Keyword); + + assertThat(indexManager.defaultRealmMapping().properties()).isEmpty(); + + TypeMapping auditMapping = indexManager.defaultAuditMapping(); + assertThat(auditMapping.properties()).containsOnlyKeys("before", "inputs", "output", "throwable"); + assertThat(auditMapping.properties().get("before").text().analyzer()).isEqualTo("standard"); + assertThat(auditMapping.properties().get("inputs").text().analyzer()).isEqualTo("standard"); + assertThat(auditMapping.properties().get("output").text().analyzer()).isEqualTo("standard"); + assertThat(auditMapping.properties().get("throwable").text().analyzer()).isEqualTo("standard"); + } + + @Test + public void createAnyIndex() throws Exception { + when(client.indices()).thenReturn(indices); + + indexManager.createAnyIndex( + "Master", AnyTypeKind.GROUP, indexManager.defaultSettings(), indexManager.defaultAnyMapping()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(CreateIndexRequest.class); + verify(indices).create(captor.capture()); + assertThat(captor.getValue().index()).isEqualTo("master_group"); + assertThat(captor.getValue().settings().numberOfShards()).isEqualTo("3"); + assertThat(captor.getValue().mappings().dynamicTemplates()).hasSize(1); + verify(indices, never()).delete(any(DeleteIndexRequest.class)); + } + + @Test + public void createAnyIndexAfterFailure() throws Exception { + when(client.indices()).thenReturn(indices); + when(indices.create(any(CreateIndexRequest.class))). + thenThrow(indexAlreadyExists()). + thenReturn(null); + + indexManager.createAnyIndex( + "Master", AnyTypeKind.USER, indexManager.defaultSettings(), indexManager.defaultAnyMapping()); + + verify(indices, times(2)).create(any(CreateIndexRequest.class)); + ArgumentCaptor captor = ArgumentCaptor.forClass(DeleteIndexRequest.class); + verify(indices).delete(captor.capture()); + assertThat(captor.getValue().index()).containsExactly("master_user"); + } + + @Test + public void createRealmIndex() throws Exception { + when(client.indices()).thenReturn(indices); + + indexManager.createRealmIndex("Master", indexManager.defaultSettings(), indexManager.defaultRealmMapping()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(CreateIndexRequest.class); + verify(indices).create(captor.capture()); + assertThat(captor.getValue().index()).isEqualTo("master_realm"); + verify(indices, never()).delete(any(DeleteIndexRequest.class)); + } + + @Test + public void createAuditIndexAfterFailure() throws Exception { + when(client.indices()).thenReturn(indices); + when(indices.create(any(CreateIndexRequest.class))). + thenThrow(indexAlreadyExists()). + thenReturn(null); + + indexManager.createAuditIndex("Master", indexManager.defaultSettings(), indexManager.defaultAuditMapping()); + + verify(indices, times(2)).create(any(CreateIndexRequest.class)); + ArgumentCaptor captor = ArgumentCaptor.forClass(DeleteIndexRequest.class); + verify(indices).delete(captor.capture()); + assertThat(captor.getValue().index()).containsExactly("master_audit"); + } + + @Test + public void removeIndexes() throws Exception { + when(client.indices()).thenReturn(indices); + + indexManager.removeAnyIndex("Master", AnyTypeKind.ANY_OBJECT); + indexManager.removeRealmIndex("Master"); + indexManager.removeAuditIndex("Master"); + + ArgumentCaptor captor = ArgumentCaptor.forClass(DeleteIndexRequest.class); + verify(indices, times(3)).delete(captor.capture()); + assertThat(captor.getAllValues().get(0).index()).containsExactly("master_any_object"); + assertThat(captor.getAllValues().get(1).index()).containsExactly("master_realm"); + assertThat(captor.getAllValues().get(2).index()).containsExactly("master_audit"); + } + + @Test + public void entityAnyDelete() throws Exception { + AnyType anyType = mock(AnyType.class); + when(anyType.getKind()).thenReturn(AnyTypeKind.USER); + User user = mock(User.class); + when(user.getKey()).thenReturn("user-key"); + when(user.getType()).thenReturn(anyType); + + indexManager.entity(new EntityLifecycleEvent<>(this, SyncDeltaType.DELETE, user, "Master")); + + ArgumentCaptor captor = ArgumentCaptor.forClass(DeleteRequest.class); + verify(client).delete(captor.capture()); + assertThat(captor.getValue().index()).isEqualTo("master_user"); + assertThat(captor.getValue().id()).isEqualTo("user-key"); + verifyNoInteractions(elasticsearchUtils); + } + + @Test + public void entityAnyUpdate() throws Exception { + AnyType anyType = mock(AnyType.class); + when(anyType.getKind()).thenReturn(AnyTypeKind.GROUP); + Group group = mock(Group.class); + when(group.getKey()).thenReturn("group-key"); + when(group.getType()).thenReturn(anyType); + when(elasticsearchUtils.document(group)).thenReturn(Map.of("name", "citizens")); + + indexManager.entity(new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, group, "Master")); + + verify(client).index(ArgumentMatchers.>>argThat( + request -> "master_group".equals(request.index()) + && "group-key".equals(request.id()) + && Map.of("name", "citizens").equals(request.document()))); + verify(client, never()).delete(any(DeleteRequest.class)); + } + + @Test + public void entityRealmDelete() throws Exception { + Realm realm = mock(Realm.class); + when(realm.getKey()).thenReturn("realm-key"); + + indexManager.entity(new EntityLifecycleEvent<>(this, SyncDeltaType.DELETE, realm, "Master")); + + ArgumentCaptor captor = ArgumentCaptor.forClass(DeleteRequest.class); + verify(client).delete(captor.capture()); + assertThat(captor.getValue().index()).isEqualTo("master_realm"); + assertThat(captor.getValue().id()).isEqualTo("realm-key"); + assertThat(captor.getValue().refresh()).isEqualTo(Refresh.True); + verifyNoInteractions(elasticsearchUtils); + } + + @Test + public void entityRealmUpdate() throws Exception { + Realm realm = mock(Realm.class); + when(realm.getKey()).thenReturn("realm-key"); + when(elasticsearchUtils.document(realm)).thenReturn(Map.of("name", "citizens")); + + indexManager.entity(new EntityLifecycleEvent<>(this, SyncDeltaType.CREATE, realm, "Master")); + + verify(client).index(ArgumentMatchers.>>argThat( + request -> "master_realm".equals(request.index()) + && "realm-key".equals(request.id()) + && Refresh.True.equals(request.refresh()) + && Map.of("name", "citizens").equals(request.document()))); + } + + @Test + public void entityNotIndexable() throws Exception { + Entity entity = mock(Entity.class); + + indexManager.entity(new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, entity, "Master")); + + verifyNoInteractions(client, elasticsearchUtils); + } + + @Test + public void audit() throws Exception { + AuditEvent auditEvent = mock(AuditEvent.class); + when(elasticsearchUtils.document(auditEvent)).thenReturn(Map.of("who", "admin")); + + indexManager.audit("Master", auditEvent); + + verify(client).index(ArgumentMatchers.>>argThat( + request -> "master_audit".equals(request.index()) + && request.id() == null + && Map.of("who", "admin").equals(request.document()))); + } +} diff --git a/ext/elasticsearch/client-elasticsearch/src/test/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtilsTest.java b/ext/elasticsearch/client-elasticsearch/src/test/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtilsTest.java new file mode 100644 index 00000000000..90b93c3dc03 --- /dev/null +++ b/ext/elasticsearch/client-elasticsearch/src/test/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtilsTest.java @@ -0,0 +1,332 @@ +/* + * 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.ext.elasticsearch.client; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.AuditEvent; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.RelationshipType; +import org.apache.syncope.core.persistence.api.entity.Role; +import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership; +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.persistence.api.entity.group.GRelationship; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.user.UMembership; +import org.apache.syncope.core.persistence.api.entity.user.URelationship; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.Pageable; + +@ExtendWith(MockitoExtension.class) +public class ElasticsearchUtilsTest { + + @Mock + private PlainSchemaDAO plainSchemaDAO; + + @Mock + private UserDAO userDAO; + + @Mock + private GroupDAO groupDAO; + + @Mock + private AnyObjectDAO anyObjectDAO; + + private ElasticsearchUtils utils; + + @BeforeEach + public void setupUtils() { + utils = new ElasticsearchUtils(plainSchemaDAO, userDAO, groupDAO, anyObjectDAO); + } + + private static PlainAttr plainAttr(final String schema, final Object... values) { + PlainAttr plainAttr = new PlainAttr(); + plainAttr.setSchema(schema); + for (Object value : values) { + PlainAttrValue plainAttrValue = new PlainAttrValue(); + switch (value) { + case Long l -> + plainAttrValue.setLongValue(l); + case byte[] b -> + plainAttrValue.setBinaryValue(b); + default -> + plainAttrValue.setStringValue(value.toString()); + } + plainAttr.add(plainAttrValue); + } + return plainAttr; + } + + private void plainSchema(final String key, final AttrSchemaType type) { + PlainSchema plainSchema = mock(PlainSchema.class); + when(plainSchema.getType()).thenReturn(type); + doReturn(Optional.of(plainSchema)).when(plainSchemaDAO).findById(key); + } + + private static AnyTypeClass anyTypeClass(final String key) { + AnyTypeClass anyTypeClass = mock(AnyTypeClass.class); + when(anyTypeClass.getKey()).thenReturn(key); + return anyTypeClass; + } + + @Test + public void indexNames() { + assertThat(ElasticsearchUtils.getAnyIndex("Master", AnyTypeKind.USER)).isEqualTo("master_user"); + assertThat(ElasticsearchUtils.getAnyIndex("TWO", AnyTypeKind.GROUP)).isEqualTo("two_group"); + assertThat(ElasticsearchUtils.getAnyIndex("Master", AnyTypeKind.ANY_OBJECT)). + isEqualTo("master_any_object"); + assertThat(ElasticsearchUtils.getRealmIndex("Master")).isEqualTo("master_realm"); + assertThat(ElasticsearchUtils.getAuditIndex("Master")).isEqualTo("master_audit"); + } + + @Test + public void documentUser() { + plainSchema("aLong", AttrSchemaType.Long); + plainSchema("aString", AttrSchemaType.String); + plainSchema("binary", AttrSchemaType.Binary); + plainSchema("encrypted", AttrSchemaType.Encrypted); + + AnyType anyType = mock(AnyType.class); + when(anyType.getKey()).thenReturn("USER"); + Realm realm = mock(Realm.class); + when(realm.getKey()).thenReturn("realm-key"); + User manager = mock(User.class); + when(manager.getKey()).thenReturn("manager-key"); + AnyTypeClass auxClass = anyTypeClass("aux"); + + User user = mock(User.class); + when(user.getKey()).thenReturn("user-key"); + when(user.getRealm()).thenReturn(realm); + when(user.getType()).thenReturn(anyType); + doReturn(List.of(auxClass)).when(user).getAuxClasses(); + when(user.getuManager()).thenReturn(manager); + when(user.getUsername()).thenReturn("rossi"); + when(user.getToken()).thenReturn("token"); + when(user.getFailedLogins()).thenReturn(3); + when(user.isSuspended()).thenReturn(true); + when(user.isMustChangePassword()).thenReturn(false); + + Role role = mock(Role.class); + when(role.getKey()).thenReturn("role-key"); + when(userDAO.findAllRoles(user)).thenReturn(List.of(role)); + when(userDAO.findAllGroupKeys(user)).thenReturn(Set.of("group-key")); + when(userDAO.findAllResourceKeys("user-key")).thenReturn(Set.of("resource-key")); + + AnyObject rightEnd = mock(AnyObject.class); + when(rightEnd.getKey()).thenReturn("right-end-key"); + RelationshipType relationshipType = mock(RelationshipType.class); + when(relationshipType.getKey()).thenReturn("relationship-type-key"); + URelationship relationship = mock(URelationship.class); + when(relationship.getRightEnd()).thenReturn(rightEnd); + when(relationship.getType()).thenReturn(relationshipType); + doReturn(List.of(relationship)).when(user).getRelationships(); + + when(user.getPlainAttrs()).thenReturn(List.of( + plainAttr("aLong", 42L), + plainAttr("aString", "one", "two"), + plainAttr("binary", "not-indexed".getBytes()), + plainAttr("encrypted", "not-indexed"), + plainAttr("unknown", "not-indexed"))); + + UMembership membership = mock(UMembership.class); + doReturn(List.of(membership)).when(user).getMemberships(); + when(user.getPlainAttrs(membership)).thenReturn(List.of(plainAttr("aString", "membership"))); + + Map document = utils.document(user); + + assertThat(document). + containsEntry("realm", "realm-key"). + containsEntry("anyType", "USER"). + containsEntry("username", "rossi"). + containsEntry("token", "token"). + containsEntry("failedLogins", 3). + containsEntry("suspended", true). + containsEntry("mustChangePassword", false). + containsEntry("uManager", "manager-key"). + containsEntry("auxClasses", List.of("aux")). + containsEntry("roles", List.of("role-key")). + containsEntry("memberships", Set.of("group-key")). + containsEntry("relationships", List.of("right-end-key")). + containsEntry("relationshipTypes", List.of("relationship-type-key")). + containsEntry("resources", Set.of("resource-key")). + containsEntry("aLong", 42L). + containsEntry("aString", List.of("one", "two", "membership")). + doesNotContainKeys("gManager", "binary", "encrypted", "unknown"); + } + + @Test + public void documentGroup() { + AnyType anyType = mock(AnyType.class); + when(anyType.getKey()).thenReturn("GROUP"); + Realm realm = mock(Realm.class); + when(realm.getKey()).thenReturn("realm-key"); + + Group group = mock(Group.class); + when(group.getKey()).thenReturn("group-key"); + when(group.getRealm()).thenReturn(realm); + when(group.getType()).thenReturn(anyType); + when(group.getAuxClasses()).thenReturn(List.of()); + when(group.getName()).thenReturn("citizens"); + + User userMember = mock(User.class); + when(userMember.getKey()).thenReturn("user-key"); + UMembership uMembership = mock(UMembership.class); + when(uMembership.getLeftEnd()).thenReturn(userMember); + when(groupDAO.findUMemberships(eq(group), any(Pageable.class))).thenReturn(List.of(uMembership)); + + AnyObject anyObjectMember = mock(AnyObject.class); + when(anyObjectMember.getKey()).thenReturn("any-object-key"); + AMembership aMembership = mock(AMembership.class); + when(aMembership.getLeftEnd()).thenReturn(anyObjectMember); + when(groupDAO.findAMemberships(group)).thenReturn(List.of(aMembership)); + + AnyObject rightEnd = mock(AnyObject.class); + when(rightEnd.getKey()).thenReturn("right-end-key"); + RelationshipType relationshipType = mock(RelationshipType.class); + when(relationshipType.getKey()).thenReturn("relationship-type-key"); + GRelationship relationship = mock(GRelationship.class); + when(relationship.getRightEnd()).thenReturn(rightEnd); + when(relationship.getType()).thenReturn(relationshipType); + doReturn(List.of(relationship)).when(group).getRelationships(); + + when(groupDAO.findAllResourceKeys("group-key")).thenReturn(Set.of("resource-key")); + when(group.getPlainAttrs()).thenReturn(List.of()); + + Map document = utils.document(group); + + assertThat(document). + containsEntry("realm", "realm-key"). + containsEntry("anyType", "GROUP"). + containsEntry("name", "citizens"). + containsEntry("relationships", List.of("right-end-key")). + containsEntry("relationshipTypes", List.of("relationship-type-key")). + containsEntry("resources", Set.of("resource-key")). + containsEntry("members", Set.of("user-key", "any-object-key")). + doesNotContainKeys("uManager", "gManager"); + } + + @Test + public void documentAnyObject() { + AnyType anyType = mock(AnyType.class); + when(anyType.getKey()).thenReturn("DEVICE"); + Realm realm = mock(Realm.class); + when(realm.getKey()).thenReturn("realm-key"); + + AnyObject anyObject = mock(AnyObject.class); + when(anyObject.getKey()).thenReturn("any-object-key"); + when(anyObject.getRealm()).thenReturn(realm); + when(anyObject.getType()).thenReturn(anyType); + when(anyObject.getAuxClasses()).thenReturn(List.of()); + when(anyObject.getName()).thenReturn("printer"); + + when(anyObjectDAO.findAllGroupKeys(anyObject)).thenReturn(Set.of("group-key")); + when(anyObjectDAO.findAllRelationships(anyObject)).thenReturn(List.of()); + when(anyObjectDAO.findAllResourceKeys("any-object-key")).thenReturn(Set.of("resource-key")); + when(anyObject.getPlainAttrs()).thenReturn(List.of()); + + Map document = utils.document(anyObject); + + assertThat(document). + containsEntry("realm", "realm-key"). + containsEntry("anyType", "DEVICE"). + containsEntry("name", "printer"). + containsEntry("memberships", Set.of("group-key")). + containsEntry("relationships", List.of()). + containsEntry("relationshipTypes", List.of()). + containsEntry("resources", Set.of("resource-key")). + doesNotContainKeys("uManager", "gManager"); + } + + @Test + public void documentRealm() { + plainSchema("aString", AttrSchemaType.String); + + Realm realm = mock(Realm.class); + when(realm.getName()).thenReturn("citizens"); + when(realm.getFullPath()).thenReturn("/citizens"); + doReturn(Set.of(anyTypeClass("aux"))).when(realm).getAnyTypeClasses(); + ExternalResource resource = mock(ExternalResource.class); + when(resource.getKey()).thenReturn("resource-key"); + doReturn(List.of(resource)).when(realm).getResources(); + when(realm.getPlainAttrs()).thenReturn(List.of(plainAttr("aString", "value"))); + + Map document = utils.document(realm); + + assertThat(document). + containsEntry("name", "citizens"). + containsEntry("parent_id", null). + containsEntry("fullPath", "/citizens"). + containsEntry("auxClasses", List.of("aux")). + containsEntry("resources", List.of("resource-key")). + containsEntry("aString", "value"); + } + + @Test + public void documentAuditEvent() { + OffsetDateTime when = OffsetDateTime.now(); + AuditEvent auditEvent = mock(AuditEvent.class); + when(auditEvent.getKey()).thenReturn("audit-key"); + when(auditEvent.getOpEvent()).thenReturn("LOGIC/UserLogic/create[SUCCESS]"); + when(auditEvent.getWho()).thenReturn("admin"); + when(auditEvent.getWhen()).thenReturn(when); + when(auditEvent.getBefore()).thenReturn("before"); + when(auditEvent.getInputs()).thenReturn(List.of("input")); + when(auditEvent.getOutput()).thenReturn("output"); + when(auditEvent.getThrowable()).thenReturn("throwable"); + + Map document = utils.document(auditEvent); + + assertThat(document). + containsEntry("key", "audit-key"). + containsEntry("opEvent", "LOGIC/UserLogic/create[SUCCESS]"). + containsEntry("who", "admin"). + containsEntry("when", when). + containsEntry("before", "before"). + containsEntry("inputs", List.of("input")). + containsEntry("output", "output"). + containsEntry("throwable", "throwable"); + } +} diff --git a/ext/elasticsearch/client-elasticsearch/src/test/java/org/apache/syncope/ext/elasticsearch/client/SyncopeElasticsearchHealthContributorTest.java b/ext/elasticsearch/client-elasticsearch/src/test/java/org/apache/syncope/ext/elasticsearch/client/SyncopeElasticsearchHealthContributorTest.java new file mode 100644 index 00000000000..dde3b953143 --- /dev/null +++ b/ext/elasticsearch/client-elasticsearch/src/test/java/org/apache/syncope/ext/elasticsearch/client/SyncopeElasticsearchHealthContributorTest.java @@ -0,0 +1,120 @@ +/* + * 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.ext.elasticsearch.client; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.HealthStatus; +import co.elastic.clients.elasticsearch.cluster.ElasticsearchClusterClient; +import co.elastic.clients.elasticsearch.cluster.HealthResponse; +import java.io.IOException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.boot.health.contributor.Health; +import org.springframework.boot.health.contributor.Status; + +@ExtendWith(MockitoExtension.class) +public class SyncopeElasticsearchHealthContributorTest { + + @Mock + private ElasticsearchClient client; + + @Mock + private ElasticsearchClusterClient cluster; + + private SyncopeElasticsearchHealthContributor contributor; + + @BeforeEach + public void setupContributor() { + contributor = new SyncopeElasticsearchHealthContributor(client); + } + + private static HealthResponse healthResponse(final HealthStatus status) { + return HealthResponse.of(b -> b. + clusterName("syncope"). + status(status). + timedOut(false). + numberOfNodes(2). + numberOfDataNodes(2). + activePrimaryShards(3). + activeShards(6). + relocatingShards(0). + initializingShards(0). + unassignedShards(0). + unassignedPrimaryShards(0). + delayedUnassignedShards(0). + numberOfPendingTasks(1). + numberOfInFlightFetch(0). + taskMaxWaitingInQueueMillis(0). + activeShardsPercentAsNumber(100)); + } + + @Test + public void up() throws Exception { + when(client.cluster()).thenReturn(cluster); + when(cluster.health()).thenReturn(healthResponse(HealthStatus.Green)); + + Health health = contributor.health(); + + assertThat(health.getStatus()).isEqualTo(Status.UP); + assertThat(health.getDetails()). + containsEntry("cluster_name", "syncope"). + containsEntry("status", "green"). + containsEntry("timed_out", false). + containsEntry("number_of_nodes", 2). + containsEntry("number_of_data_nodes", 2). + containsEntry("active_primary_shards", 3). + containsEntry("active_shards_percent_as_number", 100.0); + } + + @Test + public void upWithYellowStatus() throws Exception { + when(client.cluster()).thenReturn(cluster); + when(cluster.health()).thenReturn(healthResponse(HealthStatus.Yellow)); + + assertThat(contributor.health().getStatus()).isEqualTo(Status.UP); + } + + @Test + public void downWithRedStatus() throws Exception { + when(client.cluster()).thenReturn(cluster); + when(cluster.health()).thenReturn(healthResponse(HealthStatus.Red)); + + Health health = contributor.health(); + + assertThat(health.getStatus()).isEqualTo(Status.DOWN); + assertThat(health.getDetails()).containsEntry("status", "red"); + } + + @Test + public void downOnFailure() throws Exception { + when(client.cluster()).thenReturn(cluster); + when(cluster.health()).thenThrow(new IOException("boom")); + + Health health = contributor.health(); + + assertThat(health.getStatus()).isEqualTo(Status.DOWN); + assertThat(health.getDetails()).containsKey("error"); + } +} diff --git a/ext/elasticsearch/persistence/src/test/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAuditEventDAOTest.java b/ext/elasticsearch/persistence/src/test/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAuditEventDAOTest.java new file mode 100644 index 00000000000..7e3f38f9b0f --- /dev/null +++ b/ext/elasticsearch/persistence/src/test/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAuditEventDAOTest.java @@ -0,0 +1,196 @@ +/* + * 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.core.persistence.elasticsearch.dao; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.ShardStatistics; +import co.elastic.clients.elasticsearch._types.SortOptions; +import co.elastic.clients.elasticsearch._types.SortOrder; +import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery; +import co.elastic.clients.elasticsearch._types.query_dsl.Query; +import co.elastic.clients.elasticsearch.core.CountRequest; +import co.elastic.clients.elasticsearch.core.CountResponse; +import co.elastic.clients.elasticsearch.core.SearchRequest; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Hit; +import java.io.IOException; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Stream; +import org.apache.syncope.common.lib.to.AuditEventTO; +import org.apache.syncope.common.lib.types.OpEvent; +import org.apache.syncope.core.persistence.api.entity.AuditEvent; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.apache.syncope.ext.elasticsearch.client.ElasticsearchIndexManager; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.security.core.context.SecurityContextHolder; + +@ExtendWith(MockitoExtension.class) +public class ElasticsearchAuditEventDAOTest { + + @Mock + private ElasticsearchIndexManager indexManager; + + @Mock + private ElasticsearchClient client; + + private ElasticsearchAuditEventDAO auditEventDAO; + + @BeforeEach + public void setupAuditEventDAO() { + SecurityContextHolder.clearContext(); + auditEventDAO = new ElasticsearchAuditEventDAO(indexManager, client, 500); + } + + @AfterEach + public void clearSecurityContext() { + SecurityContextHolder.clearContext(); + } + + @Test + public void save() throws Exception { + AuditEvent auditEvent = mock(AuditEvent.class); + + assertThat(auditEventDAO.save(auditEvent)).isSameAs(auditEvent); + + verify(indexManager).audit(AuthContextUtils.getDomain(), auditEvent); + } + + @Test + public void saveFailure() throws Exception { + AuditEvent auditEvent = mock(AuditEvent.class); + doThrow(new IOException("boom")).when(indexManager).audit(anyString(), any(AuditEvent.class)); + + assertThatThrownBy(() -> auditEventDAO.save(auditEvent)). + isInstanceOf(IllegalStateException.class). + hasMessage("Could not index audit event"). + hasCauseInstanceOf(IOException.class); + } + + @Test + public void count() throws Exception { + when(client.count(any(CountRequest.class))).thenReturn(new CountResponse.Builder(). + count(3). + shards(new ShardStatistics.Builder().failed(0).successful(1).total(1).build()). + build()); + + assertThat(auditEventDAO.count( + null, null, null, + OpEvent.CategoryType.LOGIC, "UserLogic", null, "create", OpEvent.Outcome.SUCCESS, + null, null)). + isEqualTo(3L); + + ArgumentCaptor captor = ArgumentCaptor.forClass(CountRequest.class); + verify(client).count(captor.capture()); + assertThat(captor.getValue().index()).containsExactly("master_audit"); + } + + @Test + public void countFailure() throws Exception { + when(client.count(any(CountRequest.class))).thenThrow(new IOException("boom")); + + assertThat(auditEventDAO.count( + null, null, null, null, null, null, null, null, null, null)). + isZero(); + } + + @Test + public void search() throws Exception { + AuditEventTO auditEventTO = new AuditEventTO(); + Hit hit = Hit.of(b -> b.index("master_audit").id("1").source(auditEventTO)); + when(client.search(any(SearchRequest.class), eq(AuditEventTO.class))).thenReturn(SearchResponse.of(b -> b. + took(1). + timedOut(false). + shards(new ShardStatistics.Builder().failed(0).successful(1).total(1).build()). + hits(h -> h.hits(List.of(hit))))); + + assertThat(auditEventDAO.search( + null, null, null, null, null, null, null, null, null, null, Pageable.unpaged())). + containsExactly(auditEventTO); + + ArgumentCaptor captor = ArgumentCaptor.forClass(SearchRequest.class); + verify(client).search(captor.capture(), eq(AuditEventTO.class)); + assertThat(captor.getValue().index()).containsExactly("master_audit"); + assertThat(captor.getValue().from()).isZero(); + assertThat(captor.getValue().size()).isEqualTo(500); + } + + @Test + public void searchFailure() throws Exception { + when(client.search(any(SearchRequest.class), eq(AuditEventTO.class))).thenThrow(new IOException("boom")); + + assertThat(auditEventDAO.search( + null, null, null, null, null, null, null, null, null, null, Pageable.unpaged())). + isEmpty(); + } + + @Test + public void sortBuilders() { + List sortOptions = auditEventDAO.sortBuilders(Stream.of( + Sort.Order.asc("id"), Sort.Order.desc("when"))); + + assertThat(sortOptions).hasSize(2); + assertThat(sortOptions.get(0).field().field()).isEqualTo("key"); + assertThat(sortOptions.get(0).field().order()).isEqualTo(SortOrder.Asc); + assertThat(sortOptions.get(1).field().field()).isEqualTo("when"); + assertThat(sortOptions.get(1).field().order()).isEqualTo(SortOrder.Desc); + } + + @Test + public void query4invalidEntityKey() { + assertThatThrownBy(() -> auditEventDAO.getQuery( + "not-a-uuid", null, null, null, null, null, null, null, null, null)). + isInstanceOf(IllegalArgumentException.class). + hasMessage("Invalid entityKey: not-a-uuid"); + } + + @Test + public void query() { + Query query = auditEventDAO.getQuery( + UUID.randomUUID().toString(), + Set.of("rossi"), + Set.of("admin"), + OpEvent.CategoryType.LOGIC, "UserLogic", null, "create", OpEvent.Outcome.SUCCESS, + null, null); + + assertThat(query._kind()).isEqualTo(Query.Kind.Bool); + + // multi-match on entityKey, multi-match on username, term on who, regexp on opEvent + assertThat(((BoolQuery) query._get()).filter()).hasSize(4); + } +} diff --git a/ext/elasticsearch/provisioning-java/pom.xml b/ext/elasticsearch/provisioning-java/pom.xml index b79c8e312a6..e2eaf1d519f 100644 --- a/ext/elasticsearch/provisioning-java/pom.xml +++ b/ext/elasticsearch/provisioning-java/pom.xml @@ -49,6 +49,28 @@ under the License. syncope-ext-elasticsearch-client ${project.version} + + + + org.slf4j + slf4j-simple + test + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-junit-jupiter + test + + + org.assertj + assertj-core + test + diff --git a/ext/elasticsearch/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindexTest.java b/ext/elasticsearch/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindexTest.java new file mode 100644 index 00000000000..0fe89dc5f94 --- /dev/null +++ b/ext/elasticsearch/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindexTest.java @@ -0,0 +1,172 @@ +/* + * 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.core.provisioning.java.job; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.mapping.TypeMapping; +import co.elastic.clients.elasticsearch.indices.IndexSettings; +import co.elastic.clients.transport.ElasticsearchTransport; +import java.io.IOException; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.RealmDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.provisioning.api.event.JobStatusEvent; +import org.apache.syncope.core.provisioning.api.job.JobExecutionContext; +import org.apache.syncope.core.provisioning.api.job.JobExecutionException; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.apache.syncope.ext.elasticsearch.client.ElasticsearchIndexManager; +import org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.security.core.context.SecurityContextHolder; + +@ExtendWith(MockitoExtension.class) +public class ElasticsearchReindexTest { + + @Mock + private ElasticsearchClient client; + + @Mock + private ElasticsearchTransport transport; + + @Mock + private ElasticsearchIndexManager indexManager; + + @Mock + private ElasticsearchUtils utils; + + @Mock + private UserDAO userDAO; + + @Mock + private GroupDAO groupDAO; + + @Mock + private AnyObjectDAO anyObjectDAO; + + @Mock + private RealmDAO realmDAO; + + @Mock + private ApplicationEventPublisher publisher; + + @Mock + private SchedTask task; + + private ElasticsearchReindex job; + + @BeforeEach + public void setupJob() { + SecurityContextHolder.clearContext(); + + job = new ElasticsearchReindex(); + job.client = client; + job.indexManager = indexManager; + job.utils = utils; + job.userDAO = userDAO; + job.groupDAO = groupDAO; + job.anyObjectDAO = anyObjectDAO; + job.realmDAO = realmDAO; + job.publisher = publisher; + job.task = task; + } + + @AfterEach + public void clearSecurityContext() { + SecurityContextHolder.clearContext(); + } + + private static JobExecutionContext context(final boolean dryRun) { + return new JobExecutionContext("Master", "reindexJob", "admin", dryRun); + } + + @Test + public void dryRun() throws JobExecutionException { + assertThat(job.doExecute(context(true))).isEqualTo("SUCCESS"); + + verifyNoInteractions(indexManager, utils, userDAO, groupDAO, anyObjectDAO, realmDAO, publisher); + } + + @Test + public void execute() throws Exception { + IndexSettings settings = new IndexSettings.Builder().numberOfShards("1").numberOfReplicas("1").build(); + TypeMapping anyMapping = new TypeMapping.Builder().build(); + TypeMapping realmMapping = new TypeMapping.Builder().build(); + TypeMapping auditMapping = new TypeMapping.Builder().build(); + when(indexManager.defaultSettings()).thenReturn(settings); + when(indexManager.defaultAnyMapping()).thenReturn(anyMapping); + when(indexManager.defaultRealmMapping()).thenReturn(realmMapping); + when(indexManager.defaultAuditMapping()).thenReturn(auditMapping); + + when(realmDAO.count()).thenReturn(2L); + when(userDAO.count()).thenReturn(3L); + when(groupDAO.count()).thenReturn(4L); + when(anyObjectDAO.count()).thenReturn(5L); + + // needed to let BulkIngester be created out of the mocked client + when(client._transport()).thenReturn(transport); + + String result = job.doExecute(context(false)); + + assertThat(result).isEqualTo("Indexes created:\n" + + " master_realm [2]\n" + + " master_user [3]\n" + + " master_group [4]\n" + + " master_any_object [5]\n" + + " master_audit"); + + verify(indexManager).createRealmIndex("Master", settings, realmMapping); + verify(indexManager).createAnyIndex("Master", AnyTypeKind.USER, settings, anyMapping); + verify(indexManager).createAnyIndex("Master", AnyTypeKind.GROUP, settings, anyMapping); + verify(indexManager).createAnyIndex("Master", AnyTypeKind.ANY_OBJECT, settings, anyMapping); + verify(indexManager).createAuditIndex("Master", settings, auditMapping); + verify(publisher, atLeastOnce()).publishEvent(any(JobStatusEvent.class)); + } + + @Test + public void executeFailure() throws Exception { + when(indexManager.defaultSettings()).thenReturn(new IndexSettings.Builder().build()); + when(indexManager.defaultRealmMapping()).thenReturn(new TypeMapping.Builder().build()); + doThrow(new IOException("boom")).when(indexManager). + createRealmIndex(anyString(), any(IndexSettings.class), any(TypeMapping.class)); + + assertThatThrownBy(() -> job.doExecute(context(false))). + isInstanceOf(JobExecutionException.class). + hasMessage("While rebuilding index for domain " + AuthContextUtils.getDomain()). + hasCauseInstanceOf(IOException.class); + } +} diff --git a/ext/flowable/client-console/pom.xml b/ext/flowable/client-console/pom.xml index 1e4ddcfe4ea..542d345a39c 100644 --- a/ext/flowable/client-console/pom.xml +++ b/ext/flowable/client-console/pom.xml @@ -55,7 +55,8 @@ under the License. syncope-client-idrepo-console ${project.version} - + + org.flowable flowable-ui-modeler-frontend @@ -66,6 +67,21 @@ under the License. flowable-ui-modeler-logic test + + org.apache.wicket + wicket-tester + test + + + org.mockito + mockito-junit-jupiter + test + + + org.junit.jupiter + junit-jupiter + test + diff --git a/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/AbstractFlowableConsoleTest.java b/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/AbstractFlowableConsoleTest.java new file mode 100644 index 00000000000..0832c2018d3 --- /dev/null +++ b/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/AbstractFlowableConsoleTest.java @@ -0,0 +1,425 @@ +/* + * 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.console; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; +import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; +import com.giffing.wicket.spring.boot.starter.app.classscanner.candidates.WicketClassCandidatesHolder; +import com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.general.GeneralSettingsProperties; +import com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.boot.actuator.WicketEndpointRepositoryDefault; +import java.io.IOException; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.cache.Cache; +import javax.cache.CacheManager; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.cxf.jaxrs.client.Client; +import org.apache.syncope.client.console.commons.AccessPolicyConfProvider; +import org.apache.syncope.client.console.commons.AnyDirectoryPanelAdditionalActionLinksProvider; +import org.apache.syncope.client.console.commons.AnyDirectoryPanelAdditionalActionsProvider; +import org.apache.syncope.client.console.commons.AnyWizardBuilderAdditionalSteps; +import org.apache.syncope.client.console.commons.ExternalResourceProvider; +import org.apache.syncope.client.console.commons.ImplementationInfoProvider; +import org.apache.syncope.client.console.commons.PolicyTabProvider; +import org.apache.syncope.client.console.commons.StatusProvider; +import org.apache.syncope.client.console.init.ClassPathScanImplementationLookup; +import org.apache.syncope.client.console.pages.Dashboard; +import org.apache.syncope.client.console.pages.Login; +import org.apache.syncope.client.console.rest.BpmnProcessRestClient; +import org.apache.syncope.client.console.rest.UserRequestRestClient; +import org.apache.syncope.client.console.rest.UserWorkflowTaskRestClient; +import org.apache.syncope.client.console.wizards.any.UserFormFinalizer; +import org.apache.syncope.client.lib.AuthenticationHandler; +import org.apache.syncope.client.lib.SyncopeAnonymousClient; +import org.apache.syncope.client.lib.SyncopeClient; +import org.apache.syncope.client.lib.SyncopeClientFactoryBean; +import org.apache.syncope.client.ui.commons.DynamicMenuStringResourceLoader; +import org.apache.syncope.common.keymaster.client.api.ConfParamOps; +import org.apache.syncope.common.keymaster.client.api.DomainOps; +import org.apache.syncope.common.keymaster.client.api.ServiceOps; +import org.apache.syncope.common.keymaster.client.api.model.JPADomain; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.collections.CircularFifoQueue; +import org.apache.syncope.common.lib.info.NumbersInfo; +import org.apache.syncope.common.lib.info.PlatformInfo; +import org.apache.syncope.common.lib.info.SystemInfo; +import org.apache.syncope.common.lib.to.AnyTypeTO; +import org.apache.syncope.common.lib.to.PlainSchemaTO; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.common.lib.types.FlowableEntitlement; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.common.rest.api.beans.SchemaQuery; +import org.apache.syncope.common.rest.api.service.AnyTypeService; +import org.apache.syncope.common.rest.api.service.FIQLQueryService; +import org.apache.syncope.common.rest.api.service.SchemaService; +import org.apache.syncope.common.rest.api.service.SyncopeService; +import org.apache.wicket.request.resource.IResource; +import org.apache.wicket.util.tester.FormTester; +import org.apache.wicket.util.tester.WicketTester; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.mockito.Mockito; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * Wicket test harness for the Flowable console extensions: it boots the IdRepo console application context, replaces + * the Flowable REST clients with mocks and exposes a {@link WicketTester} instance. + */ +public abstract class AbstractFlowableConsoleTest { + + @Import(IdRepoConsoleContext.class) + @Configuration(proxyBeanMethods = false) + public static class SyncopeConsoleWebApplicationTestConfig { + + @Bean + public ConsoleProperties consoleProperties() { + ConsoleProperties props = new ConsoleProperties(); + + props.getSecurityHeaders().put("X-XSS-Protection", "1; mode=block"); + props.setAnonymousUser("anonymousUser"); + + return props; + } + + @Bean + public ServiceOps selfServiceOps() { + return mock(ServiceOps.class); + } + + @Bean + public DomainOps domainOps() { + DomainOps domainOps = mock(DomainOps.class); + when(domainOps.list()).thenReturn(List.of(new JPADomain.Builder(SyncopeConstants.MASTER_DOMAIN).build())); + return domainOps; + } + + @Bean + public ConfParamOps confParamOps() { + ConfParamOps confParamOps = mock(ConfParamOps.class); + when(confParamOps.get(anyString(), anyString(), anyBoolean(), eq(boolean.class))).thenReturn(false); + return confParamOps; + } + + @Bean + public GeneralSettingsProperties generalSettingsProperties() { + return new GeneralSettingsProperties(); + } + + @Bean + public List configurations() { + return List.of(); + } + + @Bean + public WicketClassCandidatesHolder wicketClassCandidatesHolder() { + return new WicketClassCandidatesHolder(); + } + + @Bean + public WicketEndpointRepository wicketEndpointRepository() { + return new WicketEndpointRepositoryDefault(); + } + + @Bean + public ClassPathScanImplementationLookup classPathScanImplementationLookup() { + ClassPathScanImplementationLookup lookup = new ClassPathScanImplementationLookup( + Set.of(), consoleProperties()); + lookup.load(); + return lookup; + } + + @Bean + public CacheManager cacheManager() { + CacheManager cacheManager = mock(CacheManager.class); + when(cacheManager.createCache(anyString(), any())).thenAnswer(ic -> mock(Cache.class)); + return cacheManager; + } + + @Bean + public DynamicMenuStringResourceLoader dynamicMenuStringResourceLoader() { + return new DynamicMenuStringResourceLoader(); + } + + @Bean + public BpmnProcessRestClient bpmnProcessRestClient() { + return mock(BpmnProcessRestClient.class); + } + + @Bean + public UserRequestRestClient userRequestRestClient() { + UserRequestRestClient restClient = mock(UserRequestRestClient.class); + when(restClient.countForms(isNull())).thenReturn(0L); + when(restClient.listForms(isNull(), anyInt(), anyInt(), any())).thenReturn(List.of()); + when(restClient.countRequests(isNull())).thenReturn(0L); + when(restClient.listRequests(isNull(), anyInt(), anyInt(), any())).thenReturn(List.of()); + return restClient; + } + + @Bean + public UserWorkflowTaskRestClient userWorkflowTaskRestClient() { + return mock(UserWorkflowTaskRestClient.class); + } + } + + public static class TestSyncopeWebApplication extends SyncopeWebApplication { + + public TestSyncopeWebApplication( + final ConsoleProperties props, + final ClassPathScanImplementationLookup lookup, + final ServiceOps serviceOps, + final ExternalResourceProvider resourceProvider, + final AnyDirectoryPanelAdditionalActionsProvider anyDirectoryPanelAdditionalActionsProvider, + final AnyDirectoryPanelAdditionalActionLinksProvider anyDirectoryPanelAdditionalActionLinksProvider, + final AnyWizardBuilderAdditionalSteps anyWizardBuilderAdditionalSteps, + final StatusProvider statusProvider, + final ImplementationInfoProvider implementationInfoProvider, + final AccessPolicyConfProvider accessPolicyConfProvider, + final List policyTabProviders, + final List userFormFinalizers, + final List resources, + final Cache loggedoutSessionIdCache, + final Cache destroyedSessionIdCache, + final DynamicMenuStringResourceLoader dynamicMenuStringResourceLoader) { + + super(props, lookup, serviceOps, resourceProvider, anyDirectoryPanelAdditionalActionsProvider, + anyDirectoryPanelAdditionalActionLinksProvider, anyWizardBuilderAdditionalSteps, statusProvider, + implementationInfoProvider, accessPolicyConfProvider, policyTabProviders, userFormFinalizers, + resources, loggedoutSessionIdCache, destroyedSessionIdCache, dynamicMenuStringResourceLoader); + } + + public interface SyncopeServiceClient extends SyncopeService, Client { + } + + public interface AnyTypeServiceClient extends AnyTypeService, Client { + } + + public interface SchemaServiceClient extends SchemaService, Client { + } + + public interface FIQLQueryServiceClient extends FIQLQueryService, Client { + } + + private SyncopeService getSyncopeService() { + SyncopeServiceClient service = mock(SyncopeServiceClient.class); + when(service.type(anyString())).thenReturn(service); + when(service.accept(anyString())).thenReturn(service); + + return service; + } + + private SchemaService getSchemaService() { + SchemaServiceClient service = mock(SchemaServiceClient.class); + + when(service.type(anyString())).thenReturn(service); + when(service.accept(anyString())).thenReturn(service); + + PlainSchemaTO firstname = new PlainSchemaTO(); + firstname.setKey("firstname"); + firstname.setType(AttrSchemaType.String); + firstname.setAnyTypeClass("minimal user"); + firstname.setMandatoryCondition("false"); + when(service.search(any(SchemaQuery.class))).thenReturn(List.of(firstname)); + return service; + } + + private AnyTypeService getAnyTypeService() { + AnyTypeServiceClient service = mock(AnyTypeServiceClient.class); + + when(service.type(anyString())).thenReturn(service); + when(service.accept(anyString())).thenReturn(service); + + AnyTypeTO anyTypeTO = new AnyTypeTO(); + anyTypeTO.setKey("123456"); + anyTypeTO.setKind(AnyTypeKind.USER); + + when(service.read(anyString())).thenReturn(anyTypeTO); + return service; + } + + private FIQLQueryService getFIQLQueryService() { + FIQLQueryServiceClient service = mock(FIQLQueryServiceClient.class); + + when(service.type(anyString())).thenReturn(service); + when(service.accept(anyString())).thenReturn(service); + + return service; + } + + private UserTO getUserTO() { + UserTO userTO = new UserTO(); + userTO.setUsername("username"); + return userTO; + } + + @Override + public SyncopeClientFactoryBean newClientFactory() { + SyncopeClient client = mock(SyncopeClient.class); + SyncopeAnonymousClient anonymousClient = mock(SyncopeAnonymousClient.class); + + when(client.self()).thenReturn(new SyncopeClient.Self(getUserTO(), Map.of( + IdRepoEntitlement.USER_READ, Set.of(SyncopeConstants.ROOT_REALM), + FlowableEntitlement.BPMN_PROCESS_GET, Set.of(SyncopeConstants.ROOT_REALM), + FlowableEntitlement.BPMN_PROCESS_SET, Set.of(SyncopeConstants.ROOT_REALM), + FlowableEntitlement.USER_REQUEST_LIST, Set.of(SyncopeConstants.ROOT_REALM), + FlowableEntitlement.USER_REQUEST_FORM_LIST, Set.of(SyncopeConstants.ROOT_REALM), + FlowableEntitlement.USER_REQUEST_FORM_CLAIM, Set.of(SyncopeConstants.ROOT_REALM), + FlowableEntitlement.USER_REQUEST_CANCEL, Set.of(SyncopeConstants.ROOT_REALM)), + List.of())); + + when(anonymousClient.gitAndBuildInfo()).thenReturn(Pair.of("", "")); + when(anonymousClient.platform()).thenReturn(new PlatformInfo( + new HashSet<>(), + new ArrayList<>(), + new ArrayList<>(), + new ArrayList<>(), + new ArrayList<>(), + new HashSet<>(), + new HashSet<>(), + new HashSet<>())); + when(anonymousClient.numbers()).thenAnswer(ic -> { + NumbersInfo numbersInfo = new NumbersInfo( + 0, + Map.of(), + Map.of(), + 0, + Map.of(), + null, + 0, + Map.of(), + null, + 0, + Map.of(), + 0, + 0, + new HashMap<>()); + + numbersInfo.confCompleteness().put(NumbersInfo.ConfItem.RESOURCE.name(), false); + numbersInfo.confCompleteness().put(NumbersInfo.ConfItem.ACCOUNT_POLICY.name(), false); + numbersInfo.confCompleteness().put(NumbersInfo.ConfItem.PASSWORD_POLICY.name(), false); + numbersInfo.confCompleteness().put(NumbersInfo.ConfItem.NOTIFICATION.name(), false); + numbersInfo.confCompleteness().put(NumbersInfo.ConfItem.PULL_TASK.name(), false); + numbersInfo.confCompleteness().put(NumbersInfo.ConfItem.ANY_TYPE.name(), false); + numbersInfo.confCompleteness().put(NumbersInfo.ConfItem.SECURITY_QUESTION.name(), false); + numbersInfo.confCompleteness().put(NumbersInfo.ConfItem.ROLE.name(), false); + + return numbersInfo; + }); + when(anonymousClient.system()).thenReturn(new SystemInfo( + null, + null, + null, + 0, + 0, + new CircularFifoQueue<>())); + + SyncopeService syncopeService = getSyncopeService(); + when(client.getService(SyncopeService.class)).thenReturn(syncopeService); + + SchemaService schemaService = getSchemaService(); + when(client.getService(SchemaService.class)).thenReturn(schemaService); + + AnyTypeService anyTypeService = getAnyTypeService(); + when(client.getService(AnyTypeService.class)).thenReturn(anyTypeService); + + FIQLQueryService fiqlQueryService = getFIQLQueryService(); + when(client.getService(FIQLQueryService.class)).thenReturn(fiqlQueryService); + + SyncopeClientFactoryBean clientFactory = mock(SyncopeClientFactoryBean.class); + when(clientFactory.setDomain(any())).thenReturn(clientFactory); + when(clientFactory.create(any(AuthenticationHandler.class))).thenReturn(client); + when(clientFactory.create(anyString(), anyString())).thenReturn(client); + when(clientFactory.createAnonymous(anyString(), isNull())).thenReturn(anonymousClient); + + return clientFactory; + } + } + + protected static ConsoleProperties PROPS; + + protected static WicketTester TESTER; + + protected static BpmnProcessRestClient BPMN_PROCESS_REST_CLIENT; + + protected static UserRequestRestClient USER_REQUEST_REST_CLIENT; + + protected static UserWorkflowTaskRestClient USER_WORKFLOW_TASK_REST_CLIENT; + + protected BpmnProcessRestClient bpmnProcessRestClient; + + protected UserRequestRestClient userRequestRestClient; + + protected UserWorkflowTaskRestClient userWorkflowTaskRestClient; + + @BeforeAll + public static void setupTester() throws IOException { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(SyncopeConsoleWebApplicationTestConfig.class); + ctx.register(TestSyncopeWebApplication.class); + ctx.refresh(); + + PROPS = ctx.getBean(ConsoleProperties.class); + BPMN_PROCESS_REST_CLIENT = ctx.getBean(BpmnProcessRestClient.class); + USER_REQUEST_REST_CLIENT = ctx.getBean(UserRequestRestClient.class); + USER_WORKFLOW_TASK_REST_CLIENT = ctx.getBean(UserWorkflowTaskRestClient.class); + TESTER = new WicketTester(ctx.getBean(SyncopeWebApplication.class)); + } + + @BeforeEach + public void loginAndResetMocks() { + TESTER.startPage(Login.class); + FormTester formTester = TESTER.newFormTester("login"); + formTester.setValue("username", "username"); + formTester.setValue("password", "password"); + formTester.submit("submit"); + TESTER.assertRenderedPage(Dashboard.class); + + Mockito.reset(BPMN_PROCESS_REST_CLIENT); + bpmnProcessRestClient = BPMN_PROCESS_REST_CLIENT; + Mockito.reset(USER_REQUEST_REST_CLIENT); + userRequestRestClient = USER_REQUEST_REST_CLIENT; + Mockito.reset(USER_WORKFLOW_TASK_REST_CLIENT); + userWorkflowTaskRestClient = USER_WORKFLOW_TASK_REST_CLIENT; + + when(userRequestRestClient.countForms(isNull())).thenReturn(0L); + when(userRequestRestClient.listForms(isNull(), anyInt(), anyInt(), any())) + .thenReturn(List.of()); + when(userRequestRestClient.countRequests(isNull())).thenReturn(0L); + when(userRequestRestClient.listRequests(isNull(), anyInt(), anyInt(), any())) + .thenReturn(List.of()); + } +} diff --git a/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/pages/FlowablePageTest.java b/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/pages/FlowablePageTest.java new file mode 100644 index 00000000000..3eddf1f91f2 --- /dev/null +++ b/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/pages/FlowablePageTest.java @@ -0,0 +1,70 @@ +/* + * 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.console.pages; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; +import org.apache.syncope.client.console.AbstractFlowableConsoleTest; +import org.apache.syncope.common.lib.to.BpmnProcess; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.junit.jupiter.api.Test; + +class FlowablePageTest extends AbstractFlowableConsoleTest { + + @Test + void renderFlowablePage() { + BpmnProcess process = new BpmnProcess(); + process.setKey("userRequest"); + process.setName("User request"); + when(bpmnProcessRestClient.getDefinitions()).thenReturn(new ArrayList<>(List.of(process))); + + TESTER.startPage(Flowable.class); + + TESTER.assertRenderedPage(Flowable.class); + assertNotNull(TESTER.getComponentFromLastRenderedPage("body:content:bpmnProcessesPanel")); + TESTER.assertContains("userRequest"); + verify(bpmnProcessRestClient, atLeastOnce()).getDefinitions(); + } + + @Test + void modelerPopupPageRedirectsToModeler() { + TESTER.startPage(FlowableModelerPopupPage.class); + + TESTER.assertRenderedPage(FlowableModelerPopupPage.class); + assertTrue(TESTER.getLastResponseAsString().contains("flowable-modeler/modeler.html")); + } + + @Test + void renderUserRequestsPage() { + TESTER.startPage(UserRequests.class); + + TESTER.assertRenderedPage(UserRequests.class); + WebMarkupContainer content = (WebMarkupContainer) TESTER.getComponentFromLastRenderedPage("body:content"); + assertEquals("userRequests", content.getMarkupId()); + + verify(userRequestRestClient, atLeastOnce()).countForms(null); + } +} diff --git a/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/panels/UserRequestFormPanelTest.java b/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/panels/UserRequestFormPanelTest.java new file mode 100644 index 00000000000..a619677c430 --- /dev/null +++ b/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/panels/UserRequestFormPanelTest.java @@ -0,0 +1,71 @@ +/* + * 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.console.panels; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.concurrent.atomic.AtomicBoolean; +import org.apache.syncope.client.console.AbstractFlowableConsoleTest; +import org.apache.syncope.common.lib.to.UserRequestForm; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.junit.jupiter.api.Test; + +class UserRequestFormPanelTest extends AbstractFlowableConsoleTest { + + private UserRequestFormPanel panel(final UserRequestForm form, final AtomicBoolean viewed) { + return new UserRequestFormPanel("panel", form) { + + private static final long serialVersionUID = 1L; + + @Override + protected void viewDetails(final AjaxRequestTarget target) { + viewed.set(true); + } + }; + } + + @Test + void userDetailsVisibleAndClickable() { + UserRequestForm form = new UserRequestForm(); + form.setUserTO(new UserTO()); + + AtomicBoolean viewed = new AtomicBoolean(); + TESTER.startComponentInPage(panel(form, viewed)); + + TESTER.assertVisible("panel:userDetails"); + TESTER.assertEnabled("panel:userDetails"); + + TESTER.clickLink("panel:userDetails"); + assertTrue(viewed.get()); + } + + @Test + void userDetailsHiddenWithoutUser() { + UserRequestForm form = new UserRequestForm(); + form.setUserTO(null); + + AtomicBoolean viewed = new AtomicBoolean(); + TESTER.startComponentInPage(panel(form, viewed)); + + TESTER.assertInvisible("panel:userDetails"); + assertFalse(viewed.get()); + } +} diff --git a/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/resources/BpmnProcessGETResourceTest.java b/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/resources/BpmnProcessGETResourceTest.java new file mode 100644 index 00000000000..aa928cf88f0 --- /dev/null +++ b/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/resources/BpmnProcessGETResourceTest.java @@ -0,0 +1,98 @@ +/* + * 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.console.resources; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.NotFoundException; +import jakarta.ws.rs.core.MediaType; +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import org.apache.syncope.client.console.AbstractFlowableConsoleTest; +import org.apache.syncope.client.ui.commons.Constants; +import org.apache.syncope.common.lib.to.BpmnProcess; +import org.apache.wicket.mock.MockRequestParameters; +import org.apache.wicket.request.Request; +import org.apache.wicket.request.Response; +import org.apache.wicket.request.mapper.parameter.PageParameters; +import org.apache.wicket.request.resource.IResource; +import org.apache.wicket.request.resource.ResourceReference; +import org.junit.jupiter.api.Test; + +class BpmnProcessGETResourceTest extends AbstractFlowableConsoleTest { + + private static final String DEFINITION = "{\"process\":\"definition\"}"; + + private ResourceReference reference(final IResource resource) { + return new ResourceReference("bpmnProcessGET") { + + private static final long serialVersionUID = 1L; + + @Override + public IResource getResource() { + return resource; + } + }; + } + + private BpmnProcess process() { + BpmnProcess process = new BpmnProcess(); + process.setKey("process"); + process.setModelId("model"); + when(bpmnProcessRestClient.getDefinitions()).thenReturn(new ArrayList<>(List.of(process))); + when(bpmnProcessRestClient.getDefinition(MediaType.APPLICATION_JSON_TYPE, "process")).thenReturn( + new ByteArrayInputStream(DEFINITION.getBytes(StandardCharsets.UTF_8))); + return process; + } + + @Test + void definitionWrittenToResponse() { + process(); + + TESTER.getRequest().setParameter(Constants.MODEL_ID_PARAM, "model"); + TESTER.startResourceReference( + reference(new BpmnProcessGETResource(bpmnProcessRestClient)), + new PageParameters()); + + assertEquals(200, TESTER.getLastResponse().getStatus()); + assertEquals(DEFINITION, TESTER.getLastResponseAsString()); + verify(bpmnProcessRestClient).getDefinition(MediaType.APPLICATION_JSON_TYPE, "process"); + } + + @Test + void unknownModelId() { + when(bpmnProcessRestClient.getDefinitions()).thenReturn(new ArrayList<>()); + + MockRequestParameters parameters = new MockRequestParameters(); + parameters.setParameterValue(Constants.MODEL_ID_PARAM, "unknown"); + + Request request = mock(Request.class); + when(request.getQueryParameters()).thenReturn(parameters); + + BpmnProcessGETResource resource = new BpmnProcessGETResource(bpmnProcessRestClient); + assertThrows(NotFoundException.class, + () -> resource.respond(new IResource.Attributes(request, mock(Response.class)))); + } +} diff --git a/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/resources/BpmnProcessPUTResourceTest.java b/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/resources/BpmnProcessPUTResourceTest.java new file mode 100644 index 00000000000..a71cec7e68c --- /dev/null +++ b/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/resources/BpmnProcessPUTResourceTest.java @@ -0,0 +1,103 @@ +/* + * 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.console.resources; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.MediaType; +import java.util.ArrayList; +import java.util.List; +import org.apache.syncope.client.console.AbstractFlowableConsoleTest; +import org.apache.syncope.client.ui.commons.Constants; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.BpmnProcess; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.wicket.request.mapper.parameter.PageParameters; +import org.apache.wicket.request.resource.IResource; +import org.apache.wicket.request.resource.ResourceReference; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class BpmnProcessPUTResourceTest extends AbstractFlowableConsoleTest { + + private static final String DEFINITION = "{\"process\":\"updated\"}"; + + private BpmnProcess process; + + private ResourceReference reference; + + @BeforeEach + void setup() { + process = new BpmnProcess(); + process.setKey("process"); + process.setModelId("model"); + when(bpmnProcessRestClient.getDefinitions()).thenReturn(new ArrayList<>(List.of(process))); + + reference = new ResourceReference("bpmnProcessPUT") { + + private static final long serialVersionUID = 1L; + + @Override + public IResource getResource() { + return new BpmnProcessPUTResource(bpmnProcessRestClient); + } + }; + } + + @Test + void definitionUpdated() { + TESTER.getRequest().setParameter(Constants.MODEL_ID_PARAM, "model"); + TESTER.getRequest().getPostParameters().setParameterValue("json_xml", DEFINITION); + + TESTER.startResourceReference(reference, new PageParameters()); + + assertEquals(204, TESTER.getLastResponse().getStatus()); + verify(bpmnProcessRestClient).setDefinition(MediaType.APPLICATION_JSON_TYPE, "process", DEFINITION); + } + + @Test + void missingDefinition() { + TESTER.getRequest().setParameter(Constants.MODEL_ID_PARAM, "model"); + + TESTER.startResourceReference(reference, new PageParameters()); + + assertEquals(400, TESTER.getLastResponse().getStatus()); + } + + @Test + void failureWhileUpdating() { + doThrow(SyncopeClientException.build(ClientExceptionType.NotFound)). + when(bpmnProcessRestClient).setDefinition( + eq(MediaType.APPLICATION_JSON_TYPE), anyString(), anyString()); + + TESTER.getRequest().setParameter(Constants.MODEL_ID_PARAM, "model"); + TESTER.getRequest().getPostParameters().setParameterValue("json_xml", DEFINITION); + + TESTER.startResourceReference(reference, new PageParameters()); + + assertEquals(400, TESTER.getLastResponse().getStatus()); + verify(bpmnProcessRestClient).setDefinition(any(), anyString(), anyString()); + } +} diff --git a/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/rest/BpmnProcessRestClientTest.java b/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/rest/BpmnProcessRestClientTest.java new file mode 100644 index 00000000000..db011c21f56 --- /dev/null +++ b/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/rest/BpmnProcessRestClientTest.java @@ -0,0 +1,74 @@ +/* + * 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.console.rest; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import org.apache.syncope.common.lib.to.BpmnProcess; +import org.apache.syncope.common.rest.api.service.BpmnProcessService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class BpmnProcessRestClientTest { + + private BpmnProcessService service; + + private BpmnProcessRestClient client; + + @BeforeEach + void setup() { + service = mock(BpmnProcessService.class); + client = new TestBpmnProcessRestClient(service); + } + + @Test + void getDefinitions() { + List definitions = List.of(new BpmnProcess()); + when(service.list()).thenReturn(definitions); + + assertSame(definitions, client.getDefinitions()); + } + + @Test + void deleteDefinition() { + client.deleteDefinition("key"); + + verify(service).delete("key"); + } + + private static final class TestBpmnProcessRestClient extends BpmnProcessRestClient { + + private static final long serialVersionUID = 1L; + + private final BpmnProcessService service; + + private TestBpmnProcessRestClient(final BpmnProcessService service) { + this.service = service; + } + + @Override + protected T getService(final Class serviceClass) { + return serviceClass.cast(service); + } + } +} diff --git a/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/rest/UserRequestRestClientTest.java b/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/rest/UserRequestRestClientTest.java new file mode 100644 index 00000000000..baf445e764e --- /dev/null +++ b/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/rest/UserRequestRestClientTest.java @@ -0,0 +1,186 @@ +/* + * 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.console.rest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.apache.syncope.client.lib.batch.BatchRequest; +import org.apache.syncope.client.lib.batch.BatchResponse; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.PagedResult; +import org.apache.syncope.common.lib.to.UserRequest; +import org.apache.syncope.common.lib.to.UserRequestForm; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.ExecStatus; +import org.apache.syncope.common.rest.api.batch.BatchRequestItem; +import org.apache.syncope.common.rest.api.batch.BatchResponseItem; +import org.apache.syncope.common.rest.api.beans.UserRequestQuery; +import org.apache.syncope.common.rest.api.service.UserRequestService; +import org.apache.wicket.extensions.markup.html.repeater.util.SortParam; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +class UserRequestRestClientTest { + + private UserRequestService service; + + private UserRequestRestClient client; + + @BeforeEach + void setup() { + service = mock(UserRequestService.class); + client = new TestUserRequestRestClient(service); + } + + @Test + void countRequests() { + PagedResult result = new PagedResult<>(); + result.setTotalCount(5); + when(service.listRequests(any())).thenReturn(result); + + assertEquals(5, client.countRequests("keyword")); + + ArgumentCaptor query = ArgumentCaptor.forClass(UserRequestQuery.class); + verify(service).listRequests(query.capture()); + assertEquals("keyword", query.getValue().getUser()); + assertEquals(1, query.getValue().getPage()); + assertEquals(1, query.getValue().getSize()); + } + + @Test + void countRequestsOnFailure() { + when(service.listRequests(any())).thenThrow(new IllegalStateException("failure")); + + assertEquals(0, client.countRequests("keyword")); + } + + @Test + void listRequests() { + UserRequest userRequest = new UserRequest(); + PagedResult result = new PagedResult<>(); + result.getResult().add(userRequest); + when(service.listRequests(any())).thenReturn(result); + + List requests = client.listRequests("keyword", 2, 10, new SortParam<>("key", true)); + + assertEquals(List.of(userRequest), requests); + + ArgumentCaptor query = ArgumentCaptor.forClass(UserRequestQuery.class); + verify(service).listRequests(query.capture()); + assertEquals("keyword", query.getValue().getUser()); + assertEquals(2, query.getValue().getPage()); + assertEquals(10, query.getValue().getSize()); + assertEquals("key ASC", query.getValue().getOrderBy()); + } + + @Test + void listRequestsOnSyncopeClientException() { + when(service.listRequests(any())). + thenThrow(SyncopeClientException.build(ClientExceptionType.InvalidRequest)); + + assertTrue(client.listRequests("keyword", 1, 10, new SortParam<>("key", true)).isEmpty()); + } + + @Test + void getForm() { + when(service.listForms(any())).thenReturn(new PagedResult<>()); + assertEquals(Optional.empty(), client.getForm("userKey")); + + UserRequestForm form = new UserRequestForm(); + PagedResult result = new PagedResult<>(); + result.getResult().add(form); + when(service.listForms(any())).thenReturn(result); + + assertSame(form, client.getForm("userKey").orElseThrow()); + } + + @Test + void claimAndUnclaimForm() { + UserRequestForm form = new UserRequestForm(); + when(service.claimForm("task")).thenReturn(form); + when(service.unclaimForm("task")).thenReturn(form); + + assertSame(form, client.claimForm("task")); + assertSame(form, client.unclaimForm("task")); + } + + @Test + void submitForm() { + UserRequestForm form = new UserRequestForm(); + + client.submitForm(form); + + verify(service).submitForm(form); + } + + @Test + void batch() throws IOException { + BatchRequestItem requestItem = new BatchRequestItem(); + requestItem.setRequestURI("users/userKey"); + BatchRequest batchRequest = mock(BatchRequest.class); + when(batchRequest.getItems()).thenReturn(List.of(requestItem)); + + BatchResponseItem responseItem = new BatchResponseItem(); + responseItem.setStatus(200); + BatchResponse batchResponse = mock(BatchResponse.class); + when(batchResponse.getItems()).thenReturn(List.of(responseItem)); + when(batchRequest.commit()).thenReturn(batchResponse); + + assertEquals(Map.of("userKey", ExecStatus.SUCCESS.name()), client.batch(batchRequest)); + } + + @Test + void batchOnFailure() throws IOException { + BatchRequest batchRequest = mock(BatchRequest.class); + when(batchRequest.getItems()).thenReturn(List.of()); + + BatchResponse batchResponse = mock(BatchResponse.class); + when(batchResponse.getItems()).thenThrow(new IOException("failure")); + when(batchRequest.commit()).thenReturn(batchResponse); + + assertTrue(client.batch(batchRequest).isEmpty()); + } + + private static final class TestUserRequestRestClient extends UserRequestRestClient { + + private static final long serialVersionUID = 1L; + + private final UserRequestService service; + + private TestUserRequestRestClient(final UserRequestService service) { + this.service = service; + } + + @Override + protected T getService(final Class serviceClass) { + return serviceClass.cast(service); + } + } +} diff --git a/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/rest/UserWorkflowTaskRestClientTest.java b/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/rest/UserWorkflowTaskRestClientTest.java new file mode 100644 index 00000000000..c759f3f54a2 --- /dev/null +++ b/ext/flowable/client-console/src/test/java/org/apache/syncope/client/console/rest/UserWorkflowTaskRestClientTest.java @@ -0,0 +1,78 @@ +/* + * 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.console.rest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.to.WorkflowTaskExecInput; +import org.apache.syncope.common.rest.api.service.UserWorkflowTaskService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +class UserWorkflowTaskRestClientTest { + + private UserWorkflowTaskService service; + + private UserWorkflowTaskRestClient client; + + @BeforeEach + void setup() { + service = mock(UserWorkflowTaskService.class); + client = new TestUserWorkflowTaskRestClient(service); + } + + @Test + void executeNextTask() { + UserTO userTO = new UserTO(); + userTO.setKey("userKey"); + + UserTO updated = new UserTO(); + updated.setKey("userKey"); + when(service.executeNextTask(any())).thenReturn(updated); + + assertSame(updated, client.executeNextTask(userTO)); + + ArgumentCaptor input = ArgumentCaptor.forClass(WorkflowTaskExecInput.class); + verify(service).executeNextTask(input.capture()); + assertEquals("userKey", input.getValue().getUserKey()); + } + + private static final class TestUserWorkflowTaskRestClient extends UserWorkflowTaskRestClient { + + private static final long serialVersionUID = 1L; + + private final UserWorkflowTaskService service; + + private TestUserWorkflowTaskRestClient(final UserWorkflowTaskService service) { + this.service = service; + } + + @Override + protected T getService(final Class serviceClass) { + return serviceClass.cast(service); + } + } +} diff --git a/ext/flowable/client-enduser/pom.xml b/ext/flowable/client-enduser/pom.xml index bc9493f6ca0..d0b22d6b403 100644 --- a/ext/flowable/client-enduser/pom.xml +++ b/ext/flowable/client-enduser/pom.xml @@ -53,6 +53,23 @@ under the License. jakarta.servlet jakarta.servlet-api + + + + org.apache.wicket + wicket-tester + test + + + org.mockito + mockito-junit-jupiter + test + + + org.junit.jupiter + junit-jupiter + test + diff --git a/ext/flowable/client-enduser/src/test/java/org/apache/syncope/client/enduser/AbstractFlowableEnduserTest.java b/ext/flowable/client-enduser/src/test/java/org/apache/syncope/client/enduser/AbstractFlowableEnduserTest.java new file mode 100644 index 00000000000..ca88aeac8ed --- /dev/null +++ b/ext/flowable/client-enduser/src/test/java/org/apache/syncope/client/enduser/AbstractFlowableEnduserTest.java @@ -0,0 +1,359 @@ +/* + * 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.enduser; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.giffing.wicket.spring.boot.context.extensions.WicketApplicationInitConfiguration; +import com.giffing.wicket.spring.boot.context.extensions.boot.actuator.WicketEndpointRepository; +import com.giffing.wicket.spring.boot.starter.app.classscanner.candidates.WicketClassCandidatesHolder; +import com.giffing.wicket.spring.boot.starter.configuration.extensions.core.settings.general.GeneralSettingsProperties; +import com.giffing.wicket.spring.boot.starter.configuration.extensions.external.spring.boot.actuator.WicketEndpointRepositoryDefault; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.apache.cxf.jaxrs.client.Client; +import org.apache.syncope.client.enduser.init.ClassPathScanImplementationLookup; +import org.apache.syncope.client.enduser.pages.Dashboard; +import org.apache.syncope.client.enduser.pages.Login; +import org.apache.syncope.client.enduser.rest.BpmnProcessRestClient; +import org.apache.syncope.client.enduser.rest.UserRequestRestClient; +import org.apache.syncope.client.lib.AuthenticationHandler; +import org.apache.syncope.client.lib.SyncopeAnonymousClient; +import org.apache.syncope.client.lib.SyncopeClient; +import org.apache.syncope.client.lib.SyncopeClientFactoryBean; +import org.apache.syncope.client.ui.commons.DynamicMenuStringResourceLoader; +import org.apache.syncope.client.ui.commons.rest.AnonymousRestClient; +import org.apache.syncope.common.keymaster.client.api.ConfParamOps; +import org.apache.syncope.common.keymaster.client.api.DomainOps; +import org.apache.syncope.common.keymaster.client.api.ServiceOps; +import org.apache.syncope.common.keymaster.client.api.model.JPADomain; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.collections.CircularFifoQueue; +import org.apache.syncope.common.lib.info.NumbersInfo; +import org.apache.syncope.common.lib.info.PlatformInfo; +import org.apache.syncope.common.lib.info.SystemInfo; +import org.apache.syncope.common.lib.to.AnyTypeTO; +import org.apache.syncope.common.lib.to.PlainSchemaTO; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.common.rest.api.beans.SchemaQuery; +import org.apache.syncope.common.rest.api.service.AnyTypeService; +import org.apache.syncope.common.rest.api.service.SchemaService; +import org.apache.syncope.common.rest.api.service.SyncopeService; +import org.apache.wicket.request.resource.IResource; +import org.apache.wicket.util.tester.FormTester; +import org.apache.wicket.util.tester.WicketTester; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.mockito.Mockito; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ResourceLoader; + +/** + * Wicket test harness for the Flowable enduser extensions: it boots the IdRepo enduser application context, replaces + * the Flowable REST clients with mocks and exposes a {@link WicketTester} instance. + */ +public abstract class AbstractFlowableEnduserTest { + + @ImportAutoConfiguration + @Configuration(proxyBeanMethods = false) + public static class SyncopeEnduserWebApplicationTestConfig { + + @Bean + public EnduserProperties enduserProperties() { + EnduserProperties enduserProperties = new EnduserProperties(); + + enduserProperties.getSecurityHeaders().put("X-XSS-Protection", "1; mode=block"); + + enduserProperties.setAdminUser("admin"); + enduserProperties.setAnonymousUser("anonymousUser"); + + return enduserProperties; + } + + @Bean + public ServiceOps selfServiceOps() { + return mock(ServiceOps.class); + } + + @Bean + public DomainOps domainOps() { + DomainOps domainOps = mock(DomainOps.class); + when(domainOps.list()).thenReturn(List.of(new JPADomain.Builder(SyncopeConstants.MASTER_DOMAIN).build())); + return domainOps; + } + + @Bean + public ConfParamOps confParamOps() { + ConfParamOps confParamOps = mock(ConfParamOps.class); + when(confParamOps.get(anyString(), anyString(), anyBoolean(), eq(boolean.class))).thenReturn(false); + return confParamOps; + } + + @Bean + public AnonymousRestClient anonymousRestClient() { + return mock(AnonymousRestClient.class); + } + + @Bean + public GeneralSettingsProperties generalSettingsProperties() { + return new GeneralSettingsProperties(); + } + + @Bean + public List configurations() { + return List.of(); + } + + @Bean + public WicketClassCandidatesHolder wicketClassCandidatesHolder() { + return new WicketClassCandidatesHolder(); + } + + @Bean + public WicketEndpointRepository wicketEndpointRepository() { + return new WicketEndpointRepositoryDefault(); + } + + @Bean + public ClassPathScanImplementationLookup classPathScanImplementationLookup() { + ClassPathScanImplementationLookup lookup = new ClassPathScanImplementationLookup(); + lookup.load(); + return lookup; + } + + @Bean + public DynamicMenuStringResourceLoader dynamicMenuStringResourceLoader() { + return new DynamicMenuStringResourceLoader(); + } + + @Bean + public UserRequestRestClient userRequestRestClient() { + UserRequestRestClient restClient = mock(UserRequestRestClient.class); + when(restClient.countRequests()).thenReturn(0L); + when(restClient.listRequests(anyInt(), anyInt(), any())).thenReturn(List.of()); + when(restClient.getForm(anyString())).thenReturn(Optional.empty()); + return restClient; + } + + @Bean + public BpmnProcessRestClient bpmnProcessRestClient() { + return mock(BpmnProcessRestClient.class); + } + } + + public static class TestSyncopeWebApplication extends SyncopeWebApplication { + + public TestSyncopeWebApplication( + final ResourceLoader resourceLoader, + final EnduserProperties props, + final ClassPathScanImplementationLookup lookup, + final ServiceOps serviceOps, + final List resources, + final DynamicMenuStringResourceLoader dynamicMenuStringResourceLoader) { + + super(resourceLoader, props, lookup, serviceOps, resources, dynamicMenuStringResourceLoader); + } + + public interface SyncopeServiceClient extends SyncopeService, Client { + } + + public interface AnyTypeServiceClient extends AnyTypeService, Client { + } + + public interface SchemaServiceClient extends SchemaService, Client { + } + + private SyncopeService getSyncopeService() { + SyncopeServiceClient service = mock(SyncopeServiceClient.class); + when(service.type(anyString())).thenReturn(service); + when(service.accept(anyString())).thenReturn(service); + + return service; + } + + private SchemaService getSchemaService() { + SchemaServiceClient service = mock(SchemaServiceClient.class); + + when(service.type(anyString())).thenReturn(service); + when(service.accept(anyString())).thenReturn(service); + + PlainSchemaTO firstname = new PlainSchemaTO(); + firstname.setKey("firstname"); + firstname.setType(AttrSchemaType.String); + firstname.setAnyTypeClass("minimal user"); + firstname.setMandatoryCondition("false"); + when(service.search(any(SchemaQuery.class))).thenReturn(List.of(firstname)); + return service; + } + + private AnyTypeService getAnyTypeService() { + AnyTypeServiceClient service = mock(AnyTypeServiceClient.class); + + when(service.type(anyString())).thenReturn(service); + when(service.accept(anyString())).thenReturn(service); + + AnyTypeTO anyTypeTO = new AnyTypeTO(); + anyTypeTO.setKey("123456"); + anyTypeTO.setKind(AnyTypeKind.USER); + + when(service.read(anyString())).thenReturn(anyTypeTO); + return service; + } + + private UserTO getUserTO() { + UserTO userTO = new UserTO(); + userTO.setUsername("username"); + return userTO; + } + + @Override + public SyncopeClientFactoryBean newClientFactory() { + SyncopeClient client = mock(SyncopeClient.class); + SyncopeAnonymousClient anonymousClient = mock(SyncopeAnonymousClient.class); + + when(client.jwtInfo()). + thenReturn(Optional.of(new SyncopeClient.JwtInfo("", OffsetDateTime.now()))); + + when(client.self()).thenReturn(new SyncopeClient.Self(getUserTO(), Map.of(), List.of())); + + when(anonymousClient.platform()).thenReturn(new PlatformInfo( + new HashSet<>(), + new ArrayList<>(), + new ArrayList<>(), + new ArrayList<>(), + new ArrayList<>(), + new HashSet<>(), + new HashSet<>(), + new HashSet<>())); + when(anonymousClient.numbers()).thenAnswer(ic -> { + NumbersInfo numbersInfo = new NumbersInfo( + 0, + Map.of(), + Map.of(), + 0, + Map.of(), + null, + 0, + Map.of(), + null, + 0, + Map.of(), + 0, + 0, + new HashMap<>()); + + numbersInfo.confCompleteness().put(NumbersInfo.ConfItem.RESOURCE.name(), false); + numbersInfo.confCompleteness().put(NumbersInfo.ConfItem.ACCOUNT_POLICY.name(), false); + numbersInfo.confCompleteness().put(NumbersInfo.ConfItem.PASSWORD_POLICY.name(), false); + numbersInfo.confCompleteness().put(NumbersInfo.ConfItem.NOTIFICATION.name(), false); + numbersInfo.confCompleteness().put(NumbersInfo.ConfItem.PULL_TASK.name(), false); + numbersInfo.confCompleteness().put(NumbersInfo.ConfItem.ANY_TYPE.name(), false); + numbersInfo.confCompleteness().put(NumbersInfo.ConfItem.SECURITY_QUESTION.name(), false); + numbersInfo.confCompleteness().put(NumbersInfo.ConfItem.ROLE.name(), false); + + return numbersInfo; + }); + when(anonymousClient.system()).thenReturn(new SystemInfo( + null, + null, + null, + 0, + 0, + new CircularFifoQueue<>())); + + SyncopeService syncopeService = getSyncopeService(); + when(client.getService(SyncopeService.class)).thenReturn(syncopeService); + + SchemaService schemaService = getSchemaService(); + when(client.getService(SchemaService.class)).thenReturn(schemaService); + + AnyTypeService anyTypeService = getAnyTypeService(); + when(client.getService(AnyTypeService.class)).thenReturn(anyTypeService); + + SyncopeClientFactoryBean clientFactory = mock(SyncopeClientFactoryBean.class); + when(clientFactory.setDomain(any())).thenReturn(clientFactory); + when(clientFactory.create(any(AuthenticationHandler.class))).thenReturn(client); + when(clientFactory.create(anyString(), anyString())).thenReturn(client); + when(clientFactory.createAnonymous(anyString(), isNull())).thenReturn(anonymousClient); + + return clientFactory; + } + } + + protected static EnduserProperties PROPS; + + protected static WicketTester TESTER; + + protected static UserRequestRestClient USER_REQUEST_REST_CLIENT; + + protected static BpmnProcessRestClient BPMN_PROCESS_REST_CLIENT; + + protected UserRequestRestClient userRequestRestClient; + + protected BpmnProcessRestClient bpmnProcessRestClient; + + @BeforeAll + public static void setupTester() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(SyncopeEnduserWebApplicationTestConfig.class); + ctx.register(TestSyncopeWebApplication.class); + ctx.refresh(); + + PROPS = ctx.getBean(EnduserProperties.class); + USER_REQUEST_REST_CLIENT = ctx.getBean(UserRequestRestClient.class); + BPMN_PROCESS_REST_CLIENT = ctx.getBean(BpmnProcessRestClient.class); + TESTER = new WicketTester(ctx.getBean(SyncopeWebApplication.class)); + } + + @BeforeEach + public void loginAndResetMocks() { + TESTER.startPage(Login.class); + FormTester formTester = TESTER.newFormTester("login"); + formTester.setValue("username", "username"); + formTester.setValue("password", "password"); + formTester.submit("submit"); + TESTER.assertRenderedPage(Dashboard.class); + + Mockito.reset(USER_REQUEST_REST_CLIENT); + userRequestRestClient = USER_REQUEST_REST_CLIENT; + Mockito.reset(BPMN_PROCESS_REST_CLIENT); + bpmnProcessRestClient = BPMN_PROCESS_REST_CLIENT; + + when(userRequestRestClient.countRequests()).thenReturn(0L); + when(userRequestRestClient.listRequests(anyInt(), anyInt(), any())).thenReturn(List.of()); + when(userRequestRestClient.getForm(anyString())).thenReturn(Optional.empty()); + } +} diff --git a/ext/flowable/client-enduser/src/test/java/org/apache/syncope/client/enduser/pages/FlowablePageTest.java b/ext/flowable/client-enduser/src/test/java/org/apache/syncope/client/enduser/pages/FlowablePageTest.java new file mode 100644 index 00000000000..4f5eabc3af6 --- /dev/null +++ b/ext/flowable/client-enduser/src/test/java/org/apache/syncope/client/enduser/pages/FlowablePageTest.java @@ -0,0 +1,90 @@ +/* + * 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.enduser.pages; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import org.apache.syncope.client.enduser.AbstractFlowableEnduserTest; +import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel; +import org.apache.syncope.common.lib.to.BpmnProcess; +import org.apache.syncope.common.lib.to.UserRequest; +import org.junit.jupiter.api.Test; + +class FlowablePageTest extends AbstractFlowableEnduserTest { + + private static final String CONTENT = "body:contentWrapper:content"; + + private BpmnProcess process(final String key, final boolean userWorkflow) { + BpmnProcess process = new BpmnProcess(); + process.setKey(key); + process.setName(key); + process.setUserWorkflow(userWorkflow); + return process; + } + + @Test + void renderPage() { + when(bpmnProcessRestClient.getDefinitions()).thenReturn(new ArrayList<>(List.of( + process("userWorkflow", true), + process("regular", false)))); + + TESTER.startPage(Flowable.class); + + TESTER.assertRenderedPage(Flowable.class); + assertNotNull(TESTER.getComponentFromLastRenderedPage(CONTENT + ":bpmnProcesses")); + TESTER.assertComponent(CONTENT + ":bpmnProcesses", AjaxTextFieldPanel.class); + TESTER.assertDisabled(CONTENT + ":start"); + + verify(bpmnProcessRestClient).getDefinitions(); + verify(userRequestRestClient).countRequests(); + } + + @Test + void activeRequestsListed() { + UserRequest request = new UserRequest(); + request.setExecutionId("executionId"); + request.setActivityId("activityId"); + request.setStartTime(Date.from(OffsetDateTime.parse("2024-01-01T10:15:30Z").toInstant())); + request.setTaskId("task"); + request.setHasForm(false); + + when(bpmnProcessRestClient.getDefinitions()).thenReturn(new ArrayList<>()); + when(userRequestRestClient.countRequests()).thenReturn(1L); + when(userRequestRestClient.listRequests(anyInt(), anyInt(), any())). + thenReturn(List.of(request)); + + TESTER.startPage(Flowable.class); + + TESTER.assertRenderedPage(Flowable.class); + TESTER.assertContains("executionId"); + TESTER.assertContains("activityId"); + + verify(userRequestRestClient, atLeastOnce()). + listRequests(anyInt(), anyInt(), any()); + } +} diff --git a/ext/flowable/client-enduser/src/test/java/org/apache/syncope/client/enduser/panels/UserRequestDetailsTest.java b/ext/flowable/client-enduser/src/test/java/org/apache/syncope/client/enduser/panels/UserRequestDetailsTest.java new file mode 100644 index 00000000000..c5a8b8495fc --- /dev/null +++ b/ext/flowable/client-enduser/src/test/java/org/apache/syncope/client/enduser/panels/UserRequestDetailsTest.java @@ -0,0 +1,141 @@ +/* + * 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.enduser.panels; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.OffsetDateTime; +import java.util.Date; +import java.util.Optional; +import org.apache.syncope.client.enduser.AbstractFlowableEnduserTest; +import org.apache.syncope.client.ui.commons.Constants; +import org.apache.syncope.client.ui.commons.panels.NotificationPanel; +import org.apache.syncope.common.lib.form.FormProperty; +import org.apache.syncope.common.lib.form.FormPropertyType; +import org.apache.syncope.common.lib.to.PropagationStatus; +import org.apache.syncope.common.lib.to.ProvisioningResult; +import org.apache.syncope.common.lib.to.UserRequest; +import org.apache.syncope.common.lib.to.UserRequestForm; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.ExecStatus; +import org.apache.wicket.markup.html.WebMarkupContainer; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.util.tester.FormTester; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class UserRequestDetailsTest extends AbstractFlowableEnduserTest { + + private WebMarkupContainer container; + + private NotificationPanel notificationPanel; + + private UserRequest userRequest; + + @BeforeEach + void setup() { + container = new WebMarkupContainer("container"); + notificationPanel = new NotificationPanel(Constants.FEEDBACK); + + userRequest = new UserRequest(); + userRequest.setExecutionId("execution"); + userRequest.setActivityId("activity"); + userRequest.setStartTime(Date.from(OffsetDateTime.parse("2024-01-01T10:15:30Z").toInstant())); + userRequest.setTaskId("task"); + } + + private UserRequestDetails details() { + return new UserRequestDetails("details", userRequest, container, notificationPanel); + } + + private UserRequestForm form() { + FormProperty property = new FormProperty(); + property.setId("property"); + property.setName("Property"); + property.setType(FormPropertyType.String); + property.setReadable(true); + property.setWritable(true); + + UserRequestForm form = new UserRequestForm(); + form.setTaskId("task"); + form.setBpmnProcess("bpmnProcess"); + form.setUserTO(new UserTO()); + form.getProperties().add(property); + return form; + } + + @Test + void requestDetailsShownWithoutForm() { + userRequest.setHasForm(false); + + TESTER.startComponentInPage(details()); + + TESTER.assertLabel("details:fragContainer:executionId", "execution"); + TESTER.assertLabel("details:fragContainer:activityId", "activity"); + } + + @Test + void formShownAndSubmitted() { + userRequest.setHasForm(true); + UserRequestForm form = form(); + when(userRequestRestClient.getForm("task")).thenReturn(Optional.of(form)); + when(userRequestRestClient.submitForm(any(UserRequestForm.class))).thenReturn(new ProvisioningResult<>()); + + TESTER.startComponentInPage(details()); + + TESTER.assertComponent("details:fragContainer:userRequestWrapForm", Form.class); + + FormTester formTester = TESTER.newFormTester("details:fragContainer:userRequestWrapForm"); + formTester.submit("submit"); + + verify(userRequestRestClient).claimForm("task"); + verify(userRequestRestClient).submitForm(form); + } + + @Test + void requestCancelled() { + userRequest.setHasForm(false); + + TESTER.startComponentInPage(details()); + TESTER.clickLink("details:delete"); + + verify(userRequestRestClient).cancelRequest("execution", null); + } + + @Test + void submissionWithFailedPropagationIsNotified() { + userRequest.setHasForm(true); + UserRequestForm form = form(); + when(userRequestRestClient.getForm("task")).thenReturn(Optional.of(form)); + + ProvisioningResult result = new ProvisioningResult<>(); + PropagationStatus status = new PropagationStatus(); + status.setStatus(ExecStatus.FAILURE); + result.getPropagationStatuses().add(status); + when(userRequestRestClient.submitForm(any(UserRequestForm.class))).thenReturn(result); + + TESTER.startComponentInPage(details()); + TESTER.newFormTester("details:fragContainer:userRequestWrapForm").submit("submit"); + + assertFalse(TESTER.getSession().getFeedbackMessages().isEmpty()); + } +} diff --git a/ext/flowable/client-enduser/src/test/java/org/apache/syncope/client/enduser/rest/BpmnProcessRestClientTest.java b/ext/flowable/client-enduser/src/test/java/org/apache/syncope/client/enduser/rest/BpmnProcessRestClientTest.java new file mode 100644 index 00000000000..2e13b80c650 --- /dev/null +++ b/ext/flowable/client-enduser/src/test/java/org/apache/syncope/client/enduser/rest/BpmnProcessRestClientTest.java @@ -0,0 +1,66 @@ +/* + * 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.enduser.rest; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; +import org.apache.syncope.common.lib.to.BpmnProcess; +import org.apache.syncope.common.rest.api.service.BpmnProcessService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class BpmnProcessRestClientTest { + + private BpmnProcessService service; + + private BpmnProcessRestClient client; + + @BeforeEach + void setup() { + service = mock(BpmnProcessService.class); + client = new TestBpmnProcessRestClient(service); + } + + @Test + void getDefinitions() { + List definitions = List.of(new BpmnProcess()); + when(service.list()).thenReturn(definitions); + + assertSame(definitions, client.getDefinitions()); + } + + private static final class TestBpmnProcessRestClient extends BpmnProcessRestClient { + + private static final long serialVersionUID = 1L; + + private final BpmnProcessService service; + + private TestBpmnProcessRestClient(final BpmnProcessService service) { + this.service = service; + } + + @Override + protected T getService(final Class serviceClass) { + return serviceClass.cast(service); + } + } +} diff --git a/ext/flowable/client-enduser/src/test/java/org/apache/syncope/client/enduser/rest/UserRequestRestClientTest.java b/ext/flowable/client-enduser/src/test/java/org/apache/syncope/client/enduser/rest/UserRequestRestClientTest.java new file mode 100644 index 00000000000..0553cd5291d --- /dev/null +++ b/ext/flowable/client-enduser/src/test/java/org/apache/syncope/client/enduser/rest/UserRequestRestClientTest.java @@ -0,0 +1,194 @@ +/* + * 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.enduser.rest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.GenericType; +import jakarta.ws.rs.core.Response; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.client.enduser.SyncopeEnduserSession; +import org.apache.syncope.common.lib.to.PagedResult; +import org.apache.syncope.common.lib.to.ProvisioningResult; +import org.apache.syncope.common.lib.to.UserRequest; +import org.apache.syncope.common.lib.to.UserRequestForm; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.rest.api.beans.UserRequestQuery; +import org.apache.syncope.common.rest.api.service.UserRequestService; +import org.apache.wicket.extensions.markup.html.repeater.util.SortParam; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; +import org.mockito.MockedStatic; + +class UserRequestRestClientTest { + + private UserRequestService service; + + private UserRequestRestClient client; + + @BeforeEach + void setup() { + service = mock(UserRequestService.class); + client = new TestUserRequestRestClient(service); + } + + @Test + void countRequests() { + UserTO selfTO = new UserTO(); + selfTO.setUsername("username"); + + PagedResult result = new PagedResult<>(); + result.setTotalCount(3); + + try (MockedStatic session = mockStatic(SyncopeEnduserSession.class)) { + SyncopeEnduserSession enduserSession = mock(SyncopeEnduserSession.class); + session.when(SyncopeEnduserSession::get).thenReturn(enduserSession); + when(enduserSession.getSelfTO()).thenReturn(selfTO); + when(service.listRequests(any())).thenReturn(result); + + assertEquals(3, client.countRequests()); + + ArgumentCaptor query = ArgumentCaptor.forClass(UserRequestQuery.class); + verify(service).listRequests(query.capture()); + assertEquals("username", query.getValue().getUser()); + } + } + + @Test + void listRequests() { + UserTO selfTO = new UserTO(); + selfTO.setUsername("username"); + + UserRequest userRequest = new UserRequest(); + PagedResult result = new PagedResult<>(); + result.getResult().add(userRequest); + + try (MockedStatic session = mockStatic(SyncopeEnduserSession.class)) { + SyncopeEnduserSession enduserSession = mock(SyncopeEnduserSession.class); + session.when(SyncopeEnduserSession::get).thenReturn(enduserSession); + when(enduserSession.getSelfTO()).thenReturn(selfTO); + when(service.listRequests(any())).thenReturn(result); + + assertEquals(List.of(userRequest), client.listRequests(2, 10, new SortParam<>("key", false))); + + ArgumentCaptor query = ArgumentCaptor.forClass(UserRequestQuery.class); + verify(service).listRequests(query.capture()); + assertEquals("username", query.getValue().getUser()); + assertEquals(2, query.getValue().getPage()); + assertEquals(10, query.getValue().getSize()); + assertEquals("key DESC", query.getValue().getOrderBy()); + } + } + + @Test + void countForms() { + PagedResult result = new PagedResult<>(); + result.setTotalCount(2); + when(service.listForms(any())).thenReturn(result); + + assertEquals(2, client.countForms()); + } + + @Test + void listForms() { + UserRequestForm form = new UserRequestForm(); + PagedResult result = new PagedResult<>(); + result.getResult().add(form); + when(service.listForms(any())).thenReturn(result); + + assertEquals(List.of(form), client.listForms(3, 15, new SortParam<>("bpmnProcess", true))); + + ArgumentCaptor query = ArgumentCaptor.forClass(UserRequestQuery.class); + verify(service).listForms(query.capture()); + assertEquals(3, query.getValue().getPage()); + assertEquals(15, query.getValue().getSize()); + assertEquals("bpmnProcess ASC", query.getValue().getOrderBy()); + } + + @Test + void getForm() { + UserTO selfTO = new UserTO(); + selfTO.setUsername("username"); + + try (MockedStatic session = mockStatic(SyncopeEnduserSession.class)) { + SyncopeEnduserSession enduserSession = mock(SyncopeEnduserSession.class); + session.when(SyncopeEnduserSession::get).thenReturn(enduserSession); + when(enduserSession.getSelfTO()).thenReturn(selfTO); + + UserRequestForm form = new UserRequestForm(); + when(service.getForm("username", "task")).thenReturn(form); + assertEquals(Optional.of(form), client.getForm("task")); + + when(service.getForm("username", "task")).thenReturn(null); + assertEquals(Optional.empty(), client.getForm("task")); + } + } + + @Test + void submitForm() { + UserRequestForm form = new UserRequestForm(); + Response response = mock(Response.class); + ProvisioningResult result = new ProvisioningResult<>(); + when(service.submitForm(form)).thenReturn(response); + when(response.readEntity( + ArgumentMatchers.>>any())).thenReturn(result); + + assertSame(result, client.submitForm(form)); + } + + @Test + void startRequest() { + client.startRequest("process"); + + verify(service).startRequest("process", null, null); + } + + @Test + void claimForm() { + UserRequestForm form = new UserRequestForm(); + when(service.claimForm("task")).thenReturn(form); + + assertSame(form, client.claimForm("task")); + } + + private static final class TestUserRequestRestClient extends UserRequestRestClient { + + private static final long serialVersionUID = 1L; + + private final UserRequestService service; + + private TestUserRequestRestClient(final UserRequestService service) { + this.service = service; + } + + @Override + protected T getService(final Class serviceClass) { + return serviceClass.cast(service); + } + } +} diff --git a/ext/flowable/common-lib/src/test/java/org/apache/syncope/common/lib/to/UserRequestFormTest.java b/ext/flowable/common-lib/src/test/java/org/apache/syncope/common/lib/to/UserRequestFormTest.java new file mode 100644 index 00000000000..88246f2928a --- /dev/null +++ b/ext/flowable/common-lib/src/test/java/org/apache/syncope/common/lib/to/UserRequestFormTest.java @@ -0,0 +1,78 @@ +/* + * 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.common.lib.to; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.Date; +import org.junit.jupiter.api.Test; + +class UserRequestFormTest { + + @Test + void datesAreCopied() { + UserRequestForm form = new UserRequestForm(); + + Date createTime = new Date(1_000L); + form.setCreateTime(createTime); + createTime.setTime(2_000L); + + Date dueDate = new Date(3_000L); + form.setDueDate(dueDate); + dueDate.setTime(4_000L); + + assertEquals(1_000L, form.getCreateTime().getTime()); + assertEquals(3_000L, form.getDueDate().getTime()); + + Date returnedCreateTime = form.getCreateTime(); + assertNotSame(form.getCreateTime(), returnedCreateTime); + returnedCreateTime.setTime(5_000L); + assertEquals(1_000L, form.getCreateTime().getTime()); + + form.setCreateTime(null); + form.setDueDate(null); + assertNull(form.getCreateTime()); + assertNull(form.getDueDate()); + } + + @Test + void equality() { + UserRequestForm first = form("process", "task"); + UserRequestForm second = form("process", "task"); + UserRequestForm different = form("process", "other"); + + assertEquals(first, second); + assertEquals(first.hashCode(), second.hashCode()); + assertNotEquals(first, different); + assertNotEquals(first, null); + assertNotEquals(first, "not a form"); + } + + private static UserRequestForm form(final String bpmnProcess, final String taskId) { + UserRequestForm form = new UserRequestForm(); + form.setBpmnProcess(bpmnProcess); + form.setTaskId(taskId); + form.setUsername("username"); + form.setCreateTime(new Date(1_000L)); + return form; + } +} diff --git a/ext/flowable/common-lib/src/test/java/org/apache/syncope/common/lib/types/FlowableEntitlementTest.java b/ext/flowable/common-lib/src/test/java/org/apache/syncope/common/lib/types/FlowableEntitlementTest.java new file mode 100644 index 00000000000..35bc16feb42 --- /dev/null +++ b/ext/flowable/common-lib/src/test/java/org/apache/syncope/common/lib/types/FlowableEntitlementTest.java @@ -0,0 +1,52 @@ +/* + * 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.common.lib.types; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class FlowableEntitlementTest { + + @Test + void values() { + assertEquals( + List.of( + FlowableEntitlement.BPMN_PROCESS_DELETE, + FlowableEntitlement.BPMN_PROCESS_GET, + FlowableEntitlement.BPMN_PROCESS_SET, + FlowableEntitlement.USER_REQUEST_CANCEL, + FlowableEntitlement.USER_REQUEST_FORM_CLAIM, + FlowableEntitlement.USER_REQUEST_FORM_LIST, + FlowableEntitlement.USER_REQUEST_FORM_SUBMIT, + FlowableEntitlement.USER_REQUEST_FORM_UNCLAIM, + FlowableEntitlement.USER_REQUEST_LIST, + FlowableEntitlement.USER_REQUEST_START, + FlowableEntitlement.WORKFLOW_TASK_LIST), + List.copyOf(FlowableEntitlement.values())); + + assertTrue(FlowableEntitlement.values().contains(FlowableEntitlement.USER_REQUEST_FORM_CLAIM)); + assertThrows( + UnsupportedOperationException.class, + () -> FlowableEntitlement.values().add("unexpected")); + } +} diff --git a/ext/flowable/flowable-bpmn/pom.xml b/ext/flowable/flowable-bpmn/pom.xml index 4ca9dd5fbec..54f880f5d13 100644 --- a/ext/flowable/flowable-bpmn/pom.xml +++ b/ext/flowable/flowable-bpmn/pom.xml @@ -70,6 +70,18 @@ under the License. syncope-core-workflow-java ${project.version} + + + + org.mockito + mockito-junit-jupiter + test + + + org.junit.jupiter + junit-jupiter + test + diff --git a/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/FlowableLiquibaseEnvironmentPostProcessorTest.java b/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/FlowableLiquibaseEnvironmentPostProcessorTest.java new file mode 100644 index 00000000000..c337f2e9265 --- /dev/null +++ b/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/FlowableLiquibaseEnvironmentPostProcessorTest.java @@ -0,0 +1,59 @@ +/* + * 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.core.flowable; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.StandardEnvironment; + +class FlowableLiquibaseEnvironmentPostProcessorTest { + + private static final String LIQUIBASE_PROPERTY = "spring.liquibase.enabled"; + + private final FlowableLiquibaseEnvironmentPostProcessor postProcessor = + new FlowableLiquibaseEnvironmentPostProcessor(); + + @Test + void defaultDisabled() { + StandardEnvironment environment = new StandardEnvironment(); + + postProcessor.postProcessEnvironment(environment, null); + + assertTrue(environment.containsProperty(LIQUIBASE_PROPERTY)); + assertEquals("false", environment.getProperty(LIQUIBASE_PROPERTY)); + assertTrue(environment.getPropertySources().contains("flowable-liquibase-override")); + } + + @Test + void keepExplicitValue() { + StandardEnvironment environment = new StandardEnvironment(); + environment.getPropertySources().addFirst( + new MapPropertySource("test", Map.of(LIQUIBASE_PROPERTY, "true"))); + + postProcessor.postProcessEnvironment(environment, null); + + assertEquals("true", environment.getProperty(LIQUIBASE_PROPERTY)); + assertFalse(environment.getPropertySources().contains("flowable-liquibase-override")); + } +} diff --git a/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/impl/FlowableBpmnProcessManagerTest.java b/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/impl/FlowableBpmnProcessManagerTest.java new file mode 100644 index 00000000000..aecfa312eee --- /dev/null +++ b/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/impl/FlowableBpmnProcessManagerTest.java @@ -0,0 +1,128 @@ +/* + * 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.core.flowable.impl; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import java.io.OutputStream; +import java.util.List; +import org.apache.syncope.common.lib.to.BpmnProcess; +import org.apache.syncope.common.lib.types.BpmnProcessFormat; +import org.apache.syncope.core.flowable.support.DomainProcessEngine; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.workflow.api.WorkflowException; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ModelQuery; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.repository.ProcessDefinitionQuery; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class FlowableBpmnProcessManagerTest { + + private DomainProcessEngine engine; + + private RepositoryService repositoryService; + + private ProcessDefinitionQuery processDefinitionQuery; + + private FlowableBpmnProcessManager manager; + + @BeforeEach + void setup() { + engine = mock(DomainProcessEngine.class); + repositoryService = mock(RepositoryService.class); + processDefinitionQuery = mock(ProcessDefinitionQuery.class); + + when(engine.getRepositoryService()).thenReturn(repositoryService); + when(repositoryService.createProcessDefinitionQuery()).thenReturn(processDefinitionQuery); + when(processDefinitionQuery.latestVersion()).thenReturn(processDefinitionQuery); + when(processDefinitionQuery.processDefinitionKey(anyString())).thenReturn(processDefinitionQuery); + + manager = new FlowableBpmnProcessManager(engine); + } + + @Test + void getProcesses() { + ProcessDefinition procDef = mock(ProcessDefinition.class); + when(procDef.getKey()).thenReturn(FlowableRuntimeUtils.WF_PROCESS_ID); + when(procDef.getName()).thenReturn("User workflow"); + when(procDef.getDeploymentId()).thenReturn("deployment"); + + ModelQuery modelQuery = mock(ModelQuery.class); + Model model = mock(Model.class); + when(repositoryService.createModelQuery()).thenReturn(modelQuery); + when(modelQuery.deploymentId("deployment")).thenReturn(modelQuery); + when(modelQuery.singleResult()).thenReturn(model); + when(model.getId()).thenReturn("model"); + + when(processDefinitionQuery.list()).thenReturn(List.of(procDef)); + + List processes = manager.getProcesses(); + + assertEquals(1, processes.size()); + assertEquals(FlowableRuntimeUtils.WF_PROCESS_ID, processes.getFirst().getKey()); + assertEquals("User workflow", processes.getFirst().getName()); + assertEquals("model", processes.getFirst().getModelId()); + assertTrue(processes.getFirst().isUserWorkflow()); + } + + @Test + void exportProcessNotFound() { + when(processDefinitionQuery.singleResult()).thenReturn(null); + + assertThrows( + NotFoundException.class, + () -> manager.exportProcess("missing", BpmnProcessFormat.XML, mock(OutputStream.class))); + } + + @Test + void deleteMainProcess() { + assertThrows(WorkflowException.class, () -> manager.deleteProcess(FlowableRuntimeUtils.WF_PROCESS_ID)); + + verifyNoInteractions(repositoryService); + } + + @Test + void deleteProcess() { + ProcessDefinition procDef = mock(ProcessDefinition.class); + when(procDef.getDeploymentId()).thenReturn("deployment"); + + ModelQuery modelQuery = mock(ModelQuery.class); + Model model = mock(Model.class); + when(repositoryService.createModelQuery()).thenReturn(modelQuery); + when(modelQuery.deploymentId("deployment")).thenReturn(modelQuery); + when(modelQuery.singleResult()).thenReturn(model); + when(model.getId()).thenReturn("model"); + when(processDefinitionQuery.list()).thenReturn(List.of(procDef)); + + manager.deleteProcess("process"); + + verify(repositoryService).deleteModel("model"); + verify(repositoryService).deleteDeployment("deployment", true); + } +} diff --git a/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/impl/FlowableDeployUtilsTest.java b/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/impl/FlowableDeployUtilsTest.java new file mode 100644 index 00000000000..2f34c4ae7a5 --- /dev/null +++ b/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/impl/FlowableDeployUtilsTest.java @@ -0,0 +1,97 @@ +/* + * 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.core.flowable.impl; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.nio.charset.StandardCharsets; +import org.apache.syncope.core.workflow.api.WorkflowException; +import org.flowable.engine.ProcessEngine; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.DeploymentBuilder; +import org.junit.jupiter.api.Test; + +class FlowableDeployUtilsTest { + + private static final byte[] VALID_DEFINITION = """ + + + + + + + """.getBytes(StandardCharsets.UTF_8); + + private static final byte[] DEFINITION_WITH_EXPRESSION = """ + + + + + + + """.getBytes(StandardCharsets.UTF_8); + + @Test + void deployDefinition() { + ProcessEngine engine = mock(ProcessEngine.class); + RepositoryService repositoryService = mock(RepositoryService.class); + DeploymentBuilder builder = mock(DeploymentBuilder.class); + Deployment deployment = mock(Deployment.class); + + when(engine.getRepositoryService()).thenReturn(repositoryService); + when(repositoryService.createDeployment()).thenReturn(builder); + when(builder.addBytes("test.bpmn20.xml", VALID_DEFINITION)).thenReturn(builder); + when(builder.deploy()).thenReturn(deployment); + + assertSame(deployment, FlowableDeployUtils.deployDefinition(engine, "test.bpmn20.xml", VALID_DEFINITION)); + + verify(builder).addBytes("test.bpmn20.xml", VALID_DEFINITION); + verify(builder).deploy(); + } + + @Test + void deployDefinitionWithExpression() { + ProcessEngine engine = mock(ProcessEngine.class); + + WorkflowException exception = assertThrows( + WorkflowException.class, + () -> FlowableDeployUtils.deployDefinition(engine, "test.bpmn20.xml", DEFINITION_WITH_EXPRESSION)); + + assertSame(WorkflowException.class, exception.getClass()); + } + + @Test + void deployDefinitionWithMalformedXml() { + ProcessEngine engine = mock(ProcessEngine.class); + + assertThrows( + WorkflowException.class, + () -> FlowableDeployUtils.deployDefinition( + engine, "test.bpmn20.xml", " split = FlowableRuntimeUtils.splitProcBusinessKey("process:user"); + assertEquals("process", split.getLeft()); + assertEquals("user", split.getRight()); + + assertThrows(WorkflowException.class, () -> FlowableRuntimeUtils.splitProcBusinessKey("process")); + assertThrows(WorkflowException.class, () -> FlowableRuntimeUtils.splitProcBusinessKey("process:user:extra")); + } + + @Test + void throwExceptionWithoutCause() { + FlowableException flowableException = new FlowableException("failure"); + + WorkflowException actual = assertThrows( + WorkflowException.class, + () -> FlowableRuntimeUtils.throwException(flowableException, "default message")); + + assertEquals("default message", actual.getMessage()); + assertSame(flowableException, actual.getCause()); + } + + @Test + void throwExceptionWithSyncopeClientException() { + SyncopeClientException cause = SyncopeClientException.build(ClientExceptionType.InvalidRequest); + + assertSame( + cause, + assertThrows( + SyncopeClientException.class, + () -> FlowableRuntimeUtils.throwException(new FlowableException("failure", cause), "default"))); + } + + @Test + void throwExceptionWithValidationExceptions() { + ParsingValidationException parsing = new ParsingValidationException("parsing", null); + assertSame( + parsing, + assertThrows( + ParsingValidationException.class, + () -> FlowableRuntimeUtils.throwException( + new FlowableException("failure", parsing), "default"))); + + InvalidEntityException invalid = new InvalidEntityException( + FlowableRuntimeUtilsTest.class, EntityViolationType.Standard, "invalid"); + assertSame( + invalid, + assertThrows( + InvalidEntityException.class, + () -> FlowableRuntimeUtils.throwException( + new FlowableException("failure", invalid), "default"))); + } + + @Test + void throwExceptionWithPersistenceCause() { + RuntimeException persistence = new NotFoundException("not found"); + + assertSame( + persistence, + assertThrows( + RuntimeException.class, + () -> FlowableRuntimeUtils.throwException( + new FlowableException("failure", persistence), "default"))); + } + + @Test + void throwExceptionWithGenericCause() { + IllegalStateException cause = new IllegalStateException("generic"); + + FlowableException flowableException = new FlowableException("failure", cause); + + WorkflowException actual = assertThrows( + WorkflowException.class, + () -> FlowableRuntimeUtils.throwException(flowableException, "default")); + + assertInstanceOf(FlowableException.class, actual.getCause()); + assertSame(flowableException, actual.getCause()); + } +} diff --git a/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/impl/FlowableUserRequestHandlerTest.java b/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/impl/FlowableUserRequestHandlerTest.java new file mode 100644 index 00000000000..28285e9ce05 --- /dev/null +++ b/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/impl/FlowableUserRequestHandlerTest.java @@ -0,0 +1,190 @@ +/* + * 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.core.flowable.impl; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import java.util.Date; +import java.util.Optional; +import org.apache.syncope.common.lib.form.FormPropertyType; +import org.apache.syncope.common.lib.to.UserRequest; +import org.apache.syncope.core.flowable.support.DomainProcessEngine; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.provisioning.api.data.UserDataBinder; +import org.apache.syncope.core.workflow.api.WorkflowException; +import org.flowable.engine.ManagementService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.form.FormType; +import org.flowable.engine.impl.persistence.entity.ExecutionEntity; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskQuery; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class FlowableUserRequestHandlerTest { + + private DomainProcessEngine engine; + + private RuntimeService runtimeService; + + private FlowableUserRequestHandler handler; + + @BeforeEach + void setup() { + engine = mock(DomainProcessEngine.class); + runtimeService = mock(RuntimeService.class); + when(engine.getRuntimeService()).thenReturn(runtimeService); + + handler = new FlowableUserRequestHandler( + mock(UserDataBinder.class), + "admin", + engine, + mock(UserDAO.class), + mock(EntityFactory.class)); + } + + @Test + void createProcessInstanceQuery() { + ManagementService managementService = mock(ManagementService.class); + when(engine.getManagementService()).thenReturn(managementService); + when(managementService.getTableName(ExecutionEntity.class)).thenReturn("ACT_RU_EXECUTION"); + + String all = handler.createProcessInstanceQuery(null).toString(); + assertTrue(all.contains("FROM ACT_RU_EXECUTION")); + assertTrue(all.contains("BUSINESS_KEY_ NOT LIKE 'userWorkflow:%'")); + assertTrue(all.contains("PARENT_ID_ IS NULL")); + + String forUser = handler.createProcessInstanceQuery("userKey").toString(); + assertTrue(forUser.contains("AND BUSINESS_KEY_ LIKE '%:userKey'")); + } + + @Test + void getUserRequest() { + UserDAO userDAO = mock(UserDAO.class); + when(userDAO.findUsername("userKey")).thenReturn(Optional.of("username")); + + TaskService taskService = mock(TaskService.class); + TaskQuery taskQuery = mock(TaskQuery.class); + Task task = mock(Task.class); + when(engine.getTaskService()).thenReturn(taskService); + when(taskService.createTaskQuery()).thenReturn(taskQuery); + when(taskQuery.processInstanceId("processInstanceId")).thenReturn(taskQuery); + when(taskQuery.singleResult()).thenReturn(task); + + when(task.getTaskDefinitionKey()).thenReturn("activity"); + when(task.getId()).thenReturn("taskId"); + when(task.getFormKey()).thenReturn("formKey"); + + ProcessInstance processInstance = mock(ProcessInstance.class); + when(processInstance.getBusinessKey()).thenReturn("process:userKey"); + when(processInstance.getStartTime()).thenReturn(new Date(1_000L)); + when(processInstance.getId()).thenReturn("executionId"); + when(processInstance.getProcessInstanceId()).thenReturn("processInstanceId"); + + FlowableUserRequestHandler customHandler = new FlowableUserRequestHandler( + mock(UserDataBinder.class), + "admin", + engine, + userDAO, + mock(EntityFactory.class)); + + UserRequest userRequest = customHandler.getUserRequest(processInstance); + + assertEquals("process", userRequest.getBpmnProcess()); + assertEquals("username", userRequest.getUsername()); + assertEquals("executionId", userRequest.getExecutionId()); + assertEquals("activity", userRequest.getActivityId()); + assertEquals("taskId", userRequest.getTaskId()); + assertTrue(userRequest.getHasForm()); + assertEquals(1_000L, userRequest.getStartTime().getTime()); + } + + @Test + void getUserRequestWithoutForm() { + UserDAO userDAO = mock(UserDAO.class); + when(userDAO.findUsername("userKey")).thenReturn(Optional.empty()); + + TaskService taskService = mock(TaskService.class); + TaskQuery taskQuery = mock(TaskQuery.class); + Task task = mock(Task.class); + when(engine.getTaskService()).thenReturn(taskService); + when(taskService.createTaskQuery()).thenReturn(taskQuery); + when(taskQuery.processInstanceId("processInstanceId")).thenReturn(taskQuery); + when(taskQuery.singleResult()).thenReturn(task); + when(task.getFormKey()).thenReturn(null); + + ProcessInstance processInstance = mock(ProcessInstance.class); + when(processInstance.getBusinessKey()).thenReturn("process:userKey"); + when(processInstance.getProcessInstanceId()).thenReturn("processInstanceId"); + + FlowableUserRequestHandler customHandler = new FlowableUserRequestHandler( + mock(UserDataBinder.class), + "admin", + engine, + userDAO, + mock(EntityFactory.class)); + + UserRequest userRequest = customHandler.getUserRequest(processInstance); + + assertNull(userRequest.getUsername()); + assertFalse(userRequest.getHasForm()); + } + + @Test + void fromFlowableFormType() { + assertEquals(FormPropertyType.String, from("string")); + assertEquals(FormPropertyType.Long, from("long")); + assertEquals(FormPropertyType.Enum, from("enum")); + assertEquals(FormPropertyType.Date, from("date")); + assertEquals(FormPropertyType.Boolean, from("boolean")); + assertEquals(FormPropertyType.Dropdown, from("dropdown")); + assertEquals(FormPropertyType.Password, from("password")); + assertEquals(FormPropertyType.String, from("unknown")); + + FormType noName = mock(FormType.class); + when(noName.getName()).thenReturn(null); + assertEquals(FormPropertyType.String, FlowableUserRequestHandler.fromFlowableFormType(noName)); + } + + @Test + void cancelMainProcess() { + ProcessInstance processInstance = mock(ProcessInstance.class); + when(processInstance.getProcessDefinitionKey()).thenReturn(FlowableRuntimeUtils.WF_PROCESS_ID); + + assertThrows(WorkflowException.class, () -> handler.cancel(processInstance, "reason")); + + verifyNoInteractions(runtimeService); + } + + private static FormPropertyType from(final String flowableType) { + FormType formType = mock(FormType.class); + when(formType.getName()).thenReturn(flowableType); + return FlowableUserRequestHandler.fromFlowableFormType(formType); + } +} diff --git a/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/impl/FlowableWorkflowUtilsTest.java b/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/impl/FlowableWorkflowUtilsTest.java new file mode 100644 index 00000000000..aea0bbcfcb1 --- /dev/null +++ b/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/impl/FlowableWorkflowUtilsTest.java @@ -0,0 +1,96 @@ +/* + * 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.core.flowable.impl; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.syncope.common.lib.to.MembershipTO; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.core.flowable.support.DomainProcessEngine; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.engine.runtime.ProcessInstanceQuery; +import org.junit.jupiter.api.Test; + +class FlowableWorkflowUtilsTest { + + @Test + void isUserIngroup() { + UserTO user = new UserTO(); + user.getMemberships().add(new MembershipTO.Builder("groupKey").groupName("group").build()); + + assertTrue(FlowableWorkflowUtils.isUserIngroup(user, "group")); + assertFalse(FlowableWorkflowUtils.isUserIngroup(user, "other")); + assertFalse(FlowableWorkflowUtils.isUserIngroup(user, null)); + assertFalse(FlowableWorkflowUtils.isUserIngroup(new UserTO(), "group")); + } + + @Test + void workflowVariables() { + String userKey = "userKey"; + String procInstId = "procInstId"; + + DomainProcessEngine engine = mock(DomainProcessEngine.class); + RuntimeService runtimeService = mock(RuntimeService.class); + ProcessInstanceQuery query = mock(ProcessInstanceQuery.class); + ProcessInstance processInstance = mock(ProcessInstance.class); + User user = mock(User.class); + + when(engine.getRuntimeService()).thenReturn(runtimeService); + when(runtimeService.createProcessInstanceQuery()).thenReturn(query); + when(query.processInstanceBusinessKey(FlowableRuntimeUtils.getWFProcBusinessKey(userKey))).thenReturn(query); + when(query.singleResult()).thenReturn(processInstance); + when(processInstance.getId()).thenReturn(procInstId); + when(user.getKey()).thenReturn(userKey); + when(runtimeService.getVariable(procInstId, "variable")).thenReturn("value"); + + FlowableWorkflowUtils utils = new FlowableWorkflowUtils(engine); + + utils.setUserWorkflowVariable("variable", "value", user); + verify(runtimeService).setVariable(procInstId, "variable", "value"); + + assertEquals("value", utils.getUserWorkflowVariable("variable", user)); + } + + @Test + void workflowVariablesWithoutProcessInstance() { + DomainProcessEngine engine = mock(DomainProcessEngine.class); + RuntimeService runtimeService = mock(RuntimeService.class); + ProcessInstanceQuery query = mock(ProcessInstanceQuery.class); + User user = mock(User.class); + + when(engine.getRuntimeService()).thenReturn(runtimeService); + when(runtimeService.createProcessInstanceQuery()).thenReturn(query); + when(query.processInstanceBusinessKey(anyString())).thenReturn(query); + when(query.singleResult()).thenReturn(null); + when(user.getKey()).thenReturn("userKey"); + + FlowableWorkflowUtils utils = new FlowableWorkflowUtils(engine); + + assertNull(utils.getUserWorkflowVariable("variable", user)); + } +} diff --git a/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/support/DropdownFormTypeTest.java b/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/support/DropdownFormTypeTest.java new file mode 100644 index 00000000000..edbdbc98c9e --- /dev/null +++ b/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/support/DropdownFormTypeTest.java @@ -0,0 +1,43 @@ +/* + * 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.core.flowable.support; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.apache.syncope.core.flowable.api.DropdownValueProvider; +import org.junit.jupiter.api.Test; + +class DropdownFormTypeTest { + + private final DropdownFormType formType = new DropdownFormType("myProvider"); + + @Test + void information() { + assertEquals("myProvider", formType.getInformation(DropdownValueProvider.NAME)); + assertNull(formType.getInformation("other")); + } + + @Test + void conversions() { + assertEquals("value", formType.convertFormValueToModelValue("value")); + assertEquals("42", formType.convertModelValueToFormValue(42)); + assertNull(formType.convertModelValueToFormValue(null)); + } +} diff --git a/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/support/SyncopeEntitiesVariableTypeTest.java b/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/support/SyncopeEntitiesVariableTypeTest.java new file mode 100644 index 00000000000..119ea6f4363 --- /dev/null +++ b/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/support/SyncopeEntitiesVariableTypeTest.java @@ -0,0 +1,38 @@ +/* + * 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.core.flowable.support; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; + +import org.apache.syncope.core.persistence.api.entity.Entity; +import org.junit.jupiter.api.Test; + +class SyncopeEntitiesVariableTypeTest { + + private final SyncopeEntitiesVariableType variableType = new SyncopeEntitiesVariableType(); + + @Test + void isAbleToStore() { + assertTrue(variableType.isAbleToStore(mock(Entity.class))); + assertFalse(variableType.isAbleToStore("not an entity")); + assertFalse(variableType.isAbleToStore(null)); + } +} diff --git a/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/support/SyncopeTaskFormHandlerTest.java b/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/support/SyncopeTaskFormHandlerTest.java new file mode 100644 index 00000000000..ddd4c61954d --- /dev/null +++ b/ext/flowable/flowable-bpmn/src/test/java/org/apache/syncope/core/flowable/support/SyncopeTaskFormHandlerTest.java @@ -0,0 +1,88 @@ +/* + * 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.core.flowable.support; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; + +import java.util.Optional; +import org.apache.syncope.core.flowable.api.DropdownValueProvider; +import org.flowable.bpmn.model.FormProperty; +import org.flowable.bpmn.model.FormValue; +import org.flowable.common.engine.impl.el.ExpressionManager; +import org.flowable.engine.form.AbstractFormType; +import org.junit.jupiter.api.Test; + +class SyncopeTaskFormHandlerTest { + + private final ExpressionManager expressionManager = mock(ExpressionManager.class); + + @Test + void dropdown() { + FormProperty formProperty = new FormProperty(); + formProperty.setType("dropdown"); + FormValue provider = new FormValue(); + provider.setId(DropdownValueProvider.NAME); + provider.setName("myProvider"); + formProperty.getFormValues().add(provider); + + Optional formType = + SyncopeTaskFormHandler.parseFormPropertyType(formProperty, expressionManager); + + DropdownFormType dropdown = assertInstanceOf(DropdownFormType.class, formType.orElseThrow()); + assertEquals("dropdown", dropdown.getName()); + assertEquals("myProvider", dropdown.getInformation(DropdownValueProvider.NAME)); + } + + @Test + void dropdownWithoutValueProvider() { + FormProperty formProperty = new FormProperty(); + formProperty.setType("dropdown"); + + assertTrue(SyncopeTaskFormHandler.parseFormPropertyType(formProperty, expressionManager).isEmpty()); + + FormValue wrongProvider = new FormValue(); + wrongProvider.setId("notTheExpectedId"); + formProperty.getFormValues().add(wrongProvider); + + assertTrue(SyncopeTaskFormHandler.parseFormPropertyType(formProperty, expressionManager).isEmpty()); + } + + @Test + void password() { + FormProperty formProperty = new FormProperty(); + formProperty.setType("password"); + + AbstractFormType formType = SyncopeTaskFormHandler.parseFormPropertyType(formProperty, expressionManager). + orElseThrow(); + + assertInstanceOf(PasswordFormType.class, formType); + assertEquals("password", formType.getName()); + } + + @Test + void notCustom() { + FormProperty formProperty = new FormProperty(); + formProperty.setType("string"); + + assertTrue(SyncopeTaskFormHandler.parseFormPropertyType(formProperty, expressionManager).isEmpty()); + } +} diff --git a/ext/flowable/logic/pom.xml b/ext/flowable/logic/pom.xml index d902f16214e..c11c14075a8 100644 --- a/ext/flowable/logic/pom.xml +++ b/ext/flowable/logic/pom.xml @@ -48,6 +48,18 @@ under the License. syncope-ext-flowable-bpmn ${project.version} + + + + org.mockito + mockito-junit-jupiter + test + + + org.junit.jupiter + junit-jupiter + test + diff --git a/ext/flowable/logic/src/test/java/org/apache/syncope/core/logic/BpmnProcessLogicTest.java b/ext/flowable/logic/src/test/java/org/apache/syncope/core/logic/BpmnProcessLogicTest.java new file mode 100644 index 00000000000..97b92446981 --- /dev/null +++ b/ext/flowable/logic/src/test/java/org/apache/syncope/core/logic/BpmnProcessLogicTest.java @@ -0,0 +1,74 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.OutputStream; +import java.lang.reflect.Method; +import java.util.List; +import org.apache.syncope.common.lib.to.BpmnProcess; +import org.apache.syncope.common.lib.types.BpmnProcessFormat; +import org.apache.syncope.core.flowable.api.BpmnProcessManager; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class BpmnProcessLogicTest { + + private BpmnProcessManager bpmnProcessManager; + + private BpmnProcessLogic logic; + + @BeforeEach + void setup() { + bpmnProcessManager = mock(BpmnProcessManager.class); + logic = new BpmnProcessLogic(bpmnProcessManager); + } + + @Test + void delegate() { + List processes = List.of(new BpmnProcess()); + when(bpmnProcessManager.getProcesses()).thenReturn(processes); + + OutputStream os = mock(OutputStream.class); + + assertSame(processes, logic.list()); + + logic.exportDefinition("key", BpmnProcessFormat.JSON, os); + verify(bpmnProcessManager).exportProcess("key", BpmnProcessFormat.JSON, os); + + logic.exportDiagram("key", os); + verify(bpmnProcessManager).exportDiagram("key", os); + + logic.importDefinition("key", BpmnProcessFormat.XML, "definition"); + verify(bpmnProcessManager).importProcess("key", BpmnProcessFormat.XML, "definition"); + + logic.delete("key"); + verify(bpmnProcessManager).deleteProcess("key"); + } + + @Test + void resolveReference() { + assertThrows(UnresolvedReferenceException.class, () -> logic.resolveReference((Method) null)); + } +} diff --git a/ext/flowable/logic/src/test/java/org/apache/syncope/core/logic/UserRequestLogicTest.java b/ext/flowable/logic/src/test/java/org/apache/syncope/core/logic/UserRequestLogicTest.java new file mode 100644 index 00000000000..46960ed8dc8 --- /dev/null +++ b/ext/flowable/logic/src/test/java/org/apache/syncope/core/logic/UserRequestLogicTest.java @@ -0,0 +1,295 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import java.util.Set; +import org.apache.commons.io.output.NullOutputStream; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.request.UserUR; +import org.apache.syncope.common.lib.to.ProvisioningResult; +import org.apache.syncope.common.lib.to.UserRequest; +import org.apache.syncope.common.lib.to.UserRequestForm; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.to.WorkflowTaskExecInput; +import org.apache.syncope.common.lib.types.BpmnProcessFormat; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.FlowableEntitlement; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.core.flowable.api.BpmnProcessManager; +import org.apache.syncope.core.flowable.api.UserRequestHandler; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.PropagationByResource; +import org.apache.syncope.core.provisioning.api.UserWorkflowResult; +import org.apache.syncope.core.provisioning.api.data.UserDataBinder; +import org.apache.syncope.core.provisioning.api.propagation.PropagationManager; +import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter; +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.apache.syncope.core.spring.security.SyncopeGrantedAuthority; +import org.flowable.engine.runtime.ProcessInstance; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +class UserRequestLogicTest { + + private BpmnProcessManager bpmnProcessManager; + + private UserRequestHandler userRequestHandler; + + private PropagationManager propagationManager; + + private PropagationTaskExecutor taskExecutor; + + private UserDataBinder binder; + + private UserDAO userDAO; + + private UserRequestLogic logic; + + @BeforeEach + void setup() { + bpmnProcessManager = mock(BpmnProcessManager.class); + userRequestHandler = mock(UserRequestHandler.class); + propagationManager = mock(PropagationManager.class); + taskExecutor = mock(PropagationTaskExecutor.class); + binder = mock(UserDataBinder.class); + userDAO = mock(UserDAO.class); + + logic = new UserRequestLogic( + bpmnProcessManager, userRequestHandler, propagationManager, taskExecutor, binder, userDAO); + } + + @Test + void securityChecks() { + AuthContextUtils.callAs( + "master", + "user", + List.of(), + () -> { + UserRequestLogic.securityChecks("user", FlowableEntitlement.USER_REQUEST_LIST, "not allowed"); + return null; + }); + + AuthContextUtils.callAs( + "master", + "admin", + List.of(new SyncopeGrantedAuthority(FlowableEntitlement.USER_REQUEST_LIST, "/")), + () -> { + UserRequestLogic.securityChecks("user", FlowableEntitlement.USER_REQUEST_LIST, "not allowed"); + return null; + }); + + SyncopeClientException exception = assertThrows( + SyncopeClientException.class, + () -> AuthContextUtils.callAs( + "master", + "admin", + List.of(), + () -> { + UserRequestLogic.securityChecks( + "user", FlowableEntitlement.USER_REQUEST_LIST, "not allowed"); + return null; + })); + + assertEquals(ClientExceptionType.DelegatedAdministration, exception.getType()); + assertEquals(Set.of("not allowed"), exception.getElements()); + } + + @Test + void listRequests() { + Page page = Page.empty(); + when(userRequestHandler.getUserRequests(null, Pageable.unpaged())).thenReturn(page); + + AuthContextUtils.callAs( + "master", + "admin", + List.of(new SyncopeGrantedAuthority(FlowableEntitlement.USER_REQUEST_LIST, "/")), + () -> { + assertSame(page, logic.listRequests(null, Pageable.unpaged())); + return null; + }); + + User user = mock(User.class); + when(user.getUsername()).thenReturn("user"); + doReturn(Optional.of(user)).when(userDAO).findById("userKey"); + when(userRequestHandler.getUserRequests("userKey", Pageable.unpaged())).thenReturn(page); + + AuthContextUtils.callAs( + "master", + "user", + List.of(), + () -> { + assertSame(page, logic.listRequests("userKey", Pageable.unpaged())); + return null; + }); + } + + @Test + void listRequestsNotFound() { + doReturn(Optional.empty()).when(userDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.listRequests("missing", Pageable.unpaged())); + } + + @Test + void startRequest() { + User user = mock(User.class); + WorkflowTaskExecInput input = new WorkflowTaskExecInput(); + UserRequest userRequest = new UserRequest(); + doReturn(Optional.of(user)).when(userDAO).findByUsername("user"); + when(userDAO.authFind("userKey")).thenReturn(user); + when(userRequestHandler.start("process", user, input)).thenReturn(userRequest); + + AuthContextUtils.callAs( + "master", + "user", + List.of(), + () -> { + assertSame(userRequest, logic.startRequest("process", input)); + return null; + }); + + AuthContextUtils.callAs( + "master", + "admin", + List.of(new SyncopeGrantedAuthority(FlowableEntitlement.USER_REQUEST_START, "/")), + () -> { + assertSame(userRequest, logic.startRequest("process", "userKey", input)); + return null; + }); + + verify(bpmnProcessManager, times(2)).exportProcess( + "process", BpmnProcessFormat.XML, NullOutputStream.INSTANCE); + } + + @Test + void claimFormDenied() { + UserRequestForm form = new UserRequestForm(); + form.setUsername("user"); + when(userRequestHandler.claimForm("task")).thenReturn(form); + + SyncopeClientException exception = assertThrows( + SyncopeClientException.class, + () -> AuthContextUtils.callAs( + "master", + "admin", + List.of(), + () -> logic.claimForm("task"))); + + assertEquals(ClientExceptionType.DelegatedAdministration, exception.getType()); + } + + @Test + void cancelRequest() { + ProcessInstance processInstance = mock(ProcessInstance.class); + when(userRequestHandler.parse("execution")).thenReturn(Pair.of(processInstance, "userKey")); + when(userDAO.findUsername("userKey")).thenReturn(Optional.of("user")); + + AuthContextUtils.callAs( + "master", + "user", + List.of(), + () -> { + logic.cancelRequest("execution", "reason"); + return null; + }); + + verify(userRequestHandler).cancel(processInstance, "reason"); + } + + @Test + void submitFormWithoutPropagation() { + UserRequestForm form = new UserRequestForm(); + form.setUsername("user"); + + UserUR userUR = new UserUR.Builder("userKey").build(); + UserWorkflowResult wfResult = new UserWorkflowResult<>( + userUR, + new UserWorkflowResult.PropagationInfo(new PropagationByResource<>(), null), + Set.of()); + when(userRequestHandler.submitForm(form)).thenReturn(wfResult); + doReturn(Optional.empty()).when(userDAO).findById("userKey"); + + AuthContextUtils.callAs( + "master", + "user", + List.of(), + () -> { + ProvisioningResult result = logic.submitForm(form, false); + assertEquals("userKey", result.getEntity().getKey()); + assertEquals(0, result.getPropagationStatuses().size()); + return null; + }); + } + + @Test + void submitFormWithPropagation() { + UserRequestForm form = new UserRequestForm(); + form.setUsername("user"); + + UserUR userUR = new UserUR.Builder("userKey").build(); + PropagationByResource propByRes = new PropagationByResource<>(); + propByRes.add(ResourceOperation.CREATE, "resource"); + UserWorkflowResult wfResult = new UserWorkflowResult<>( + userUR, + new UserWorkflowResult.PropagationInfo(propByRes, null), + Set.of()); + when(userRequestHandler.submitForm(form)).thenReturn(wfResult); + + UserTO userTO = new UserTO(); + userTO.setKey("userKey"); + User user = mock(User.class); + doReturn(Optional.of(user)).when(userDAO).findById("userKey"); + when(binder.getUserTO("userKey")).thenReturn(userTO); + when(propagationManager.getUserUpdateTasks(any())).thenReturn(List.of()); + PropagationReporter reporter = mock(PropagationReporter.class); + when(reporter.getStatuses()).thenReturn(List.of()); + when(taskExecutor.execute(any(), eq(true), eq("user"))).thenReturn(reporter); + + AuthContextUtils.callAs( + "master", + "user", + List.of(), + () -> { + ProvisioningResult result = logic.submitForm(form, true); + assertSame(userTO, result.getEntity()); + return null; + }); + + verify(taskExecutor).execute(any(), eq(true), eq("user")); + } +} diff --git a/ext/flowable/logic/src/test/java/org/apache/syncope/core/logic/UserWorkflowTaskLogicTest.java b/ext/flowable/logic/src/test/java/org/apache/syncope/core/logic/UserWorkflowTaskLogicTest.java new file mode 100644 index 00000000000..fa18a172cd8 --- /dev/null +++ b/ext/flowable/logic/src/test/java/org/apache/syncope/core/logic/UserWorkflowTaskLogicTest.java @@ -0,0 +1,118 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.Set; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.to.WorkflowTask; +import org.apache.syncope.common.lib.to.WorkflowTaskExecInput; +import org.apache.syncope.core.flowable.api.WorkflowTaskManager; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.PropagationByResource; +import org.apache.syncope.core.provisioning.api.UserWorkflowResult; +import org.apache.syncope.core.provisioning.api.data.UserDataBinder; +import org.apache.syncope.core.provisioning.api.propagation.PropagationManager; +import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter; +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class UserWorkflowTaskLogicTest { + + private WorkflowTaskManager wfTaskManager; + + private PropagationManager propagationManager; + + private PropagationTaskExecutor taskExecutor; + + private UserDataBinder binder; + + private UserDAO userDAO; + + private UserWorkflowTaskLogic logic; + + @BeforeEach + void setup() { + wfTaskManager = mock(WorkflowTaskManager.class); + propagationManager = mock(PropagationManager.class); + taskExecutor = mock(PropagationTaskExecutor.class); + binder = mock(UserDataBinder.class); + userDAO = mock(UserDAO.class); + + logic = new UserWorkflowTaskLogic(wfTaskManager, propagationManager, taskExecutor, binder, userDAO); + } + + @Test + void getAvailableTasks() { + User user = mock(User.class); + when(user.getKey()).thenReturn("userKey"); + when(userDAO.authFind("userKey")).thenReturn(user); + + List tasks = List.of(new WorkflowTask()); + when(wfTaskManager.getAvailableTasks("userKey")).thenReturn(tasks); + + assertSame(tasks, logic.getAvailableTasks("userKey")); + } + + @Test + void executeNextTask() { + WorkflowTaskExecInput input = new WorkflowTaskExecInput(); + UserWorkflowResult wfResult = new UserWorkflowResult<>( + "userKey", + new UserWorkflowResult.PropagationInfo(new PropagationByResource<>(), null), + Set.of("task")); + when(wfTaskManager.executeNextTask(input)).thenReturn(wfResult); + when(propagationManager.getUserUpdateTasks(any())).thenReturn(List.of()); + + PropagationReporter reporter = mock(PropagationReporter.class); + when(taskExecutor.execute(any(), eq(false), eq("admin"))).thenReturn(reporter); + + UserTO userTO = new UserTO(); + userTO.setKey("userKey"); + when(binder.getUserTO("userKey")).thenReturn(userTO); + + AuthContextUtils.callAs( + "master", + "admin", + List.of(), + () -> { + assertSame(userTO, logic.executeNextTask(input)); + return null; + }); + + verify(taskExecutor).execute(any(), eq(false), eq("admin")); + } + + @Test + void resolveReference() { + assertThrows(UnresolvedReferenceException.class, () -> logic.resolveReference((Method) null)); + } +} diff --git a/ext/flowable/rest-api/pom.xml b/ext/flowable/rest-api/pom.xml index 0c27d21b9cd..28d2ba594c7 100644 --- a/ext/flowable/rest-api/pom.xml +++ b/ext/flowable/rest-api/pom.xml @@ -48,6 +48,13 @@ under the License. syncope-ext-flowable-common-lib ${project.version} + + + + org.junit.jupiter + junit-jupiter + test + @@ -57,6 +64,9 @@ under the License. org.apache.maven.plugins maven-javadoc-plugin true + + none + attach-javadocs diff --git a/ext/flowable/rest-api/src/test/java/org/apache/syncope/common/rest/api/beans/UserRequestQueryTest.java b/ext/flowable/rest-api/src/test/java/org/apache/syncope/common/rest/api/beans/UserRequestQueryTest.java new file mode 100644 index 00000000000..3702d5e7dcb --- /dev/null +++ b/ext/flowable/rest-api/src/test/java/org/apache/syncope/common/rest/api/beans/UserRequestQueryTest.java @@ -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. + */ +package org.apache.syncope.common.rest.api.beans; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; + +class UserRequestQueryTest { + + @Test + void builder() { + UserRequestQuery query = new UserRequestQuery.Builder(). + user("userKey"). + page(2). + size(50). + orderBy("bpmnProcess DESC"). + build(); + + assertEquals("userKey", query.getUser()); + assertEquals(2, query.getPage()); + assertEquals(50, query.getSize()); + assertEquals("bpmnProcess DESC", query.getOrderBy()); + } + + @Test + void defaultPaging() { + UserRequestQuery query = new UserRequestQuery.Builder().page(0).size(0).build(); + + assertEquals(1, query.getPage()); + assertEquals(1, query.getSize()); + assertNull(query.getOrderBy()); + } +} diff --git a/ext/flowable/rest-cxf/pom.xml b/ext/flowable/rest-cxf/pom.xml index b2421a2871f..74d2b58268b 100644 --- a/ext/flowable/rest-cxf/pom.xml +++ b/ext/flowable/rest-cxf/pom.xml @@ -59,6 +59,23 @@ under the License. syncope-ext-flowable-logic ${project.version} + + + + jakarta.servlet + jakarta.servlet-api + test + + + org.mockito + mockito-junit-jupiter + test + + + org.junit.jupiter + junit-jupiter + test + diff --git a/ext/flowable/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/BpmnProcessServiceImplTest.java b/ext/flowable/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/BpmnProcessServiceImplTest.java new file mode 100644 index 00000000000..4e48767312f --- /dev/null +++ b/ext/flowable/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/BpmnProcessServiceImplTest.java @@ -0,0 +1,135 @@ +/* + * 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.core.rest.cxf.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.StreamingOutput; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.List; +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.syncope.common.lib.to.BpmnProcess; +import org.apache.syncope.common.lib.types.BpmnProcessFormat; +import org.apache.syncope.common.rest.api.RESTHeaders; +import org.apache.syncope.core.logic.BpmnProcessLogic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class BpmnProcessServiceImplTest { + + private BpmnProcessLogic logic; + + private MessageContext messageContext; + + private HttpHeaders httpHeaders; + + private BpmnProcessServiceImpl service; + + @BeforeEach + void setup() { + logic = mock(BpmnProcessLogic.class); + messageContext = mock(MessageContext.class); + httpHeaders = mock(HttpHeaders.class); + when(messageContext.getHttpHeaders()).thenReturn(httpHeaders); + + service = new BpmnProcessServiceImpl(logic); + service.messageContext = messageContext; + } + + @Test + void list() { + List processes = List.of(new BpmnProcess()); + when(logic.list()).thenReturn(processes); + + assertSame(processes, service.list()); + } + + @Test + void getAsJson() throws IOException { + when(httpHeaders.getAcceptableMediaTypes()).thenReturn(List.of(MediaType.APPLICATION_JSON_TYPE)); + + Response response = service.get("key"); + + assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType()); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + ((StreamingOutput) response.getEntity()).write(os); + + verify(logic).exportDefinition("key", BpmnProcessFormat.JSON, os); + } + + @Test + void getAsXml() throws IOException { + when(httpHeaders.getAcceptableMediaTypes()).thenReturn(List.of(MediaType.APPLICATION_XML_TYPE)); + + Response response = service.get("key"); + + assertEquals(MediaType.APPLICATION_XML_TYPE, response.getMediaType()); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + ((StreamingOutput) response.getEntity()).write(os); + + verify(logic).exportDefinition("key", BpmnProcessFormat.XML, os); + } + + @Test + void setAsJson() { + when(httpHeaders.getMediaType()).thenReturn(MediaType.APPLICATION_JSON_TYPE); + + service.set("key", "definition"); + + verify(logic).importDefinition("key", BpmnProcessFormat.JSON, "definition"); + } + + @Test + void setAsXml() { + when(httpHeaders.getMediaType()).thenReturn(MediaType.APPLICATION_XML_TYPE); + + service.set("key", "definition"); + + verify(logic).importDefinition("key", BpmnProcessFormat.XML, "definition"); + } + + @Test + void exportDiagram() throws IOException { + Response response = service.exportDiagram("key"); + + assertEquals(MediaType.valueOf(RESTHeaders.MEDIATYPE_IMAGE_PNG), response.getMediaType()); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + ((StreamingOutput) response.getEntity()).write(os); + + verify(logic).exportDiagram("key", os); + } + + @Test + void delete() { + service.delete("key"); + + verify(logic).delete("key"); + } +} diff --git a/ext/flowable/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/UserRequestServiceImplTest.java b/ext/flowable/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/UserRequestServiceImplTest.java new file mode 100644 index 00000000000..d77effad439 --- /dev/null +++ b/ext/flowable/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/UserRequestServiceImplTest.java @@ -0,0 +1,174 @@ +/* + * 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.core.rest.cxf.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; +import java.util.Optional; +import java.util.UUID; +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.apache.syncope.common.lib.to.PagedResult; +import org.apache.syncope.common.lib.to.ProvisioningResult; +import org.apache.syncope.common.lib.to.UserRequest; +import org.apache.syncope.common.lib.to.UserRequestForm; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.to.WorkflowTaskExecInput; +import org.apache.syncope.common.rest.api.beans.UserRequestQuery; +import org.apache.syncope.core.logic.UserRequestLogic; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +class UserRequestServiceImplTest { + + private UserRequestLogic logic; + + private UserDAO userDAO; + + private UriInfo uriInfo; + + private MessageContext messageContext; + + private UserRequestServiceImpl service; + + @BeforeEach + void setup() { + logic = mock(UserRequestLogic.class); + userDAO = mock(UserDAO.class); + uriInfo = mock(UriInfo.class); + messageContext = mock(MessageContext.class); + + when(uriInfo.getPathParameters(true)).thenReturn(new MultivaluedHashMap<>()); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(UriBuilder.fromPath("http://localhost/flowable")); + when(uriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<>()); + + service = new UserRequestServiceImpl(logic, userDAO); + service.uriInfo = uriInfo; + service.messageContext = messageContext; + } + + @Test + void listRequests() { + UserRequestQuery query = new UserRequestQuery.Builder().page(1).size(10).build(); + when(logic.listRequests(isNull(), any(Pageable.class))).thenReturn(Page.empty()); + + PagedResult result = service.listRequests(query); + + assertEquals(0, result.getTotalCount()); + assertEquals(1, result.getPage()); + verify(logic).listRequests(isNull(), any(Pageable.class)); + } + + @Test + void listRequestsForUser() { + UserRequestQuery query = new UserRequestQuery.Builder().user("username").page(1).size(10).build(); + when(userDAO.findKey("username")).thenReturn(Optional.of("userKey")); + when(logic.listRequests(any(), any(Pageable.class))).thenReturn(Page.empty()); + + service.listRequests(query); + + verify(logic).listRequests(any(), any(Pageable.class)); + } + + @Test + void startRequestWithoutUser() { + WorkflowTaskExecInput input = new WorkflowTaskExecInput(); + UserRequest userRequest = new UserRequest(); + when(logic.startRequest("process", input)).thenReturn(userRequest); + + assertSame(userRequest, service.startRequest("process", null, input)); + } + + @Test + void startRequestWithKey() { + String userKey = UUID.randomUUID().toString(); + WorkflowTaskExecInput input = new WorkflowTaskExecInput(); + UserRequest userRequest = new UserRequest(); + when(logic.startRequest("process", userKey, input)).thenReturn(userRequest); + + assertSame(userRequest, service.startRequest("process", userKey, input)); + } + + @Test + void startRequestWithUsername() { + WorkflowTaskExecInput input = new WorkflowTaskExecInput(); + UserRequest userRequest = new UserRequest(); + when(userDAO.findKey("username")).thenReturn(Optional.of("userKey")); + when(logic.startRequest("process", "userKey", input)).thenReturn(userRequest); + + assertSame(userRequest, service.startRequest("process", "username", input)); + } + + @Test + void cancelRequest() { + service.cancelRequest("execution", "reason"); + + verify(logic).cancelRequest("execution", "reason"); + } + + @Test + void claimAndUnclaimForm() { + UserRequestForm form = new UserRequestForm(); + when(logic.claimForm("task")).thenReturn(form); + when(logic.unclaimForm("task")).thenReturn(form); + + assertSame(form, service.claimForm("task")); + assertSame(form, service.unclaimForm("task")); + } + + @Test + void getForm() { + UserRequestForm form = new UserRequestForm(); + when(userDAO.findKey("username")).thenReturn(Optional.of("userKey")); + when(logic.getForm("userKey", "task")).thenReturn(form); + + assertSame(form, service.getForm("username", "task")); + } + + @Test + void submitForm() { + HttpServletRequest request = mock(HttpServletRequest.class); + when(messageContext.getHttpServletRequest()).thenReturn(request); + + UserRequestForm form = new UserRequestForm(); + ProvisioningResult result = new ProvisioningResult<>(); + UserTO userTO = new UserTO(); + userTO.setKey("userKey"); + result.setEntity(userTO); + when(logic.submitForm(form, false)).thenReturn(result); + + Response response = service.submitForm(form); + + assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + assertSame(result, response.getEntity()); + } +} diff --git a/ext/oidcc4ui/client-common-ui/pom.xml b/ext/oidcc4ui/client-common-ui/pom.xml index a8e8df38321..82f8b16b7e0 100644 --- a/ext/oidcc4ui/client-common-ui/pom.xml +++ b/ext/oidcc4ui/client-common-ui/pom.xml @@ -55,6 +55,23 @@ under the License. syncope-client-idrepo-common-ui ${project.version} + + + + org.apache.wicket + wicket-tester + test + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-junit-jupiter + test + diff --git a/ext/oidcc4ui/client-common-ui/src/test/java/org/apache/syncope/client/ui/commons/panels/AbstractOIDCSSOLoginFormPanelTest.java b/ext/oidcc4ui/client-common-ui/src/test/java/org/apache/syncope/client/ui/commons/panels/AbstractOIDCSSOLoginFormPanelTest.java new file mode 100644 index 00000000000..26a9f0ec4f8 --- /dev/null +++ b/ext/oidcc4ui/client-common-ui/src/test/java/org/apache/syncope/client/ui/commons/panels/AbstractOIDCSSOLoginFormPanelTest.java @@ -0,0 +1,133 @@ +/* + * 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.ui.commons.panels; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; +import org.apache.syncope.client.ui.commons.BaseSession; +import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel; +import org.apache.syncope.common.lib.to.OIDCC4UIProviderTO; +import org.apache.syncope.common.rest.api.service.OIDCC4UIProviderService; +import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; +import org.apache.wicket.behavior.AbstractAjaxBehavior; +import org.apache.wicket.markup.html.form.DropDownChoice; +import org.apache.wicket.markup.html.form.FormComponent; +import org.apache.wicket.util.tester.WicketTester; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class AbstractOIDCSSOLoginFormPanelTest { + + private WicketTester tester; + + private BaseSession session; + + private OIDCC4UIProviderService service; + + @BeforeEach + void setUp() { + service = mock(OIDCC4UIProviderService.class); + session = mock(BaseSession.class); + when(session.getAnonymousService(OIDCC4UIProviderService.class)).thenReturn(service); + + tester = new WicketTester(); + } + + @AfterEach + void tearDown() { + tester.destroy(); + } + + private static OIDCC4UIProviderTO provider(final String name) { + OIDCC4UIProviderTO provider = new OIDCC4UIProviderTO(); + provider.setName(name); + return provider; + } + + private OIDCSSOLoginFormPanelStub panel(final OIDCC4UIProviderTO... providers) { + when(service.list()).thenReturn(List.of(providers)); + OIDCSSOLoginFormPanelStub panel = + tester.startComponentInPage(new OIDCSSOLoginFormPanelStub("panel", session)); + // do not follow the redirects scheduled by the panel itself + tester.setFollowRedirects(false); + return panel; + } + + private static AjaxDropDownChoicePanel ops(final OIDCSSOLoginFormPanelStub panel) { + return (AjaxDropDownChoicePanel) panel.get("ops"); + } + + private static AbstractAjaxBehavior onChangeBehavior(final AjaxDropDownChoicePanel ops) { + DropDownChoice choice = (DropDownChoice) ops.getField(); + List behaviors = + choice.getBehaviors(AjaxFormComponentUpdatingBehavior.class); + assertEquals(1, behaviors.size()); + return (AbstractAjaxBehavior) behaviors.getFirst(); + } + + @Test + void rendersAvailableProviders() { + OIDCSSOLoginFormPanelStub panel = panel(provider("op1"), provider("op2")); + + AjaxDropDownChoicePanel ops = ops(panel); + assertTrue(ops.isVisible()); + assertEquals(2, ((DropDownChoice) ops.getField()).getChoices().size()); + + tester.assertContains("value=\"op1\""); + tester.assertContains("value=\"op2\""); + } + + @Test + void hidesDropDownWhenNoProviderAvailable() { + assertFalse(ops(panel()).isVisible()); + } + + @Test + void selectingProviderRedirectsToLogin() { + OIDCSSOLoginFormPanelStub panel = panel(provider("op1")); + panel.setReauth(true); + + AjaxDropDownChoicePanel ops = ops(panel); + FormComponent field = ops.getField(); + tester.getRequest().getPostParameters().setParameterValue(field.getInputName(), "op1"); + + tester.executeBehavior(onChangeBehavior(ops)); + + String location = tester.getLastResponse().getRedirectLocation(); + assertNotNull(location); + assertTrue(location.contains("oidcc4ui/login?op=op1&reauth=true"), location); + } + + @Test + void noSelectionDoesNotRedirect() { + AjaxDropDownChoicePanel ops = ops(panel(provider("op1"))); + + tester.executeBehavior(onChangeBehavior(ops)); + + assertNull(tester.getLastResponse().getRedirectLocation()); + } +} diff --git a/ext/oidcc4ui/client-common-ui/src/test/java/org/apache/syncope/client/ui/commons/panels/OIDCSSOLoginFormPanelStub.java b/ext/oidcc4ui/client-common-ui/src/test/java/org/apache/syncope/client/ui/commons/panels/OIDCSSOLoginFormPanelStub.java new file mode 100644 index 00000000000..0ffcd0a245c --- /dev/null +++ b/ext/oidcc4ui/client-common-ui/src/test/java/org/apache/syncope/client/ui/commons/panels/OIDCSSOLoginFormPanelStub.java @@ -0,0 +1,33 @@ +/* + * 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.ui.commons.panels; + +import org.apache.syncope.client.ui.commons.BaseSession; + +/** + * Concrete panel used to exercise {@link AbstractOIDCSSOLoginFormPanel} with WicketTester. + */ +public class OIDCSSOLoginFormPanelStub extends AbstractOIDCSSOLoginFormPanel { + + private static final long serialVersionUID = -6216360493678233558L; + + public OIDCSSOLoginFormPanelStub(final String id, final BaseSession session) { + super(id, session); + } +} diff --git a/ext/oidcc4ui/client-common-ui/src/test/resources/org/apache/syncope/client/ui/commons/panels/OIDCSSOLoginFormPanelStub.html b/ext/oidcc4ui/client-common-ui/src/test/resources/org/apache/syncope/client/ui/commons/panels/OIDCSSOLoginFormPanelStub.html new file mode 100644 index 00000000000..ce0dbe8f344 --- /dev/null +++ b/ext/oidcc4ui/client-common-ui/src/test/resources/org/apache/syncope/client/ui/commons/panels/OIDCSSOLoginFormPanelStub.html @@ -0,0 +1,23 @@ + + + + + + diff --git a/ext/oidcc4ui/client-console/pom.xml b/ext/oidcc4ui/client-console/pom.xml index 813b5722cbf..be8c1b5fd3d 100644 --- a/ext/oidcc4ui/client-console/pom.xml +++ b/ext/oidcc4ui/client-console/pom.xml @@ -54,6 +54,23 @@ under the License. syncope-ext-oidcc4ui-client-common-ui ${project.version} + + + + org.apache.wicket + wicket-tester + test + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-junit-jupiter + test + diff --git a/ext/oidcc4ui/client-console/src/test/java/org/apache/syncope/client/console/pages/OIDCClientBeforeLogoutTest.java b/ext/oidcc4ui/client-console/src/test/java/org/apache/syncope/client/console/pages/OIDCClientBeforeLogoutTest.java new file mode 100644 index 00000000000..d24ce37e64b --- /dev/null +++ b/ext/oidcc4ui/client-console/src/test/java/org/apache/syncope/client/console/pages/OIDCClientBeforeLogoutTest.java @@ -0,0 +1,52 @@ +/* + * 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.console.pages; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.wicket.util.tester.WicketTester; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class OIDCClientBeforeLogoutTest { + + private WicketTester tester; + + @BeforeEach + void setUp() { + tester = new WicketTester(); + tester.setFollowRedirects(false); + } + + @AfterEach + void tearDown() { + tester.destroy(); + } + + @Test + void redirectsToBeforeLogoutResource() { + tester.startPage(OIDCClientBeforeLogout.class); + + String location = tester.getLastResponse().getRedirectLocation(); + assertNotNull(location); + assertTrue(location.endsWith("oidcc4ui/before-logout"), location); + } +} diff --git a/ext/oidcc4ui/client-console/src/test/java/org/apache/syncope/client/console/resources/oidcc4ui/ConsoleCodeConsumerResourceTest.java b/ext/oidcc4ui/client-console/src/test/java/org/apache/syncope/client/console/resources/oidcc4ui/ConsoleCodeConsumerResourceTest.java new file mode 100644 index 00000000000..3951d2b8ba4 --- /dev/null +++ b/ext/oidcc4ui/client-console/src/test/java/org/apache/syncope/client/console/resources/oidcc4ui/ConsoleCodeConsumerResourceTest.java @@ -0,0 +1,41 @@ +/* + * 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.console.resources.oidcc4ui; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.apache.syncope.client.console.pages.OIDCClientLogin; +import org.apache.syncope.common.lib.to.UserTO; +import org.junit.jupiter.api.Test; + +class ConsoleCodeConsumerResourceTest { + + @Test + void loginPageIsOIDCClientLogin() { + assertEquals(OIDCClientLogin.class, new ConsoleCodeConsumerResource().getLoginPageClass()); + } + + @Test + void selfRegistrationIsNotSupported() { + ConsoleCodeConsumerResource resource = new ConsoleCodeConsumerResource(); + + assertThrows(UnsupportedOperationException.class, () -> resource.getSelfRegInfo(new UserTO())); + } +} diff --git a/ext/oidcc4ui/client-console/src/test/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilderTest.java b/ext/oidcc4ui/client-console/src/test/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilderTest.java new file mode 100644 index 00000000000..0868a74507a --- /dev/null +++ b/ext/oidcc4ui/client-console/src/test/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilderTest.java @@ -0,0 +1,89 @@ +/* + * 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.console.wizards; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import org.apache.syncope.client.console.panels.OIDCProvidersDirectoryPanel; +import org.apache.syncope.client.console.rest.ImplementationRestClient; +import org.apache.syncope.client.console.rest.OIDCProviderRestClient; +import org.apache.syncope.common.lib.to.OIDCC4UIProviderTO; +import org.apache.wicket.PageReference; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class OIDCProviderWizardBuilderTest { + + private OIDCProviderRestClient oidcProviderRestClient; + + private OIDCProviderWizardBuilder builder; + + @BeforeEach + void setUp() { + oidcProviderRestClient = mock(OIDCProviderRestClient.class); + + builder = new OIDCProviderWizardBuilder( + mock(OIDCProvidersDirectoryPanel.class), + new OIDCC4UIProviderTO(), + mock(ImplementationRestClient.class), + oidcProviderRestClient, + mock(PageReference.class)); + } + + @Test + void createWithoutDiscovery() { + OIDCC4UIProviderTO opTO = new OIDCC4UIProviderTO(); + opTO.setName("op"); + opTO.setHasDiscovery(false); + + assertSame(opTO, builder.onApplyInternal(opTO)); + + verify(oidcProviderRestClient).create(opTO); + verify(oidcProviderRestClient, never()).createFromDiscovery(opTO); + verify(oidcProviderRestClient, never()).update(opTO); + } + + @Test + void createWithDiscovery() { + OIDCC4UIProviderTO opTO = new OIDCC4UIProviderTO(); + opTO.setIssuer("https://op.example.com"); + opTO.setHasDiscovery(true); + + assertSame(opTO, builder.onApplyInternal(opTO)); + + verify(oidcProviderRestClient).createFromDiscovery(opTO); + verify(oidcProviderRestClient, never()).create(opTO); + verify(oidcProviderRestClient, never()).update(opTO); + } + + @Test + void updateExistingProvider() { + OIDCC4UIProviderTO opTO = new OIDCC4UIProviderTO(); + opTO.setKey("key"); + + assertSame(opTO, builder.onApplyInternal(opTO)); + + verify(oidcProviderRestClient).update(opTO); + verify(oidcProviderRestClient, never()).create(opTO); + verify(oidcProviderRestClient, never()).createFromDiscovery(opTO); + } +} diff --git a/ext/oidcc4ui/client-enduser/pom.xml b/ext/oidcc4ui/client-enduser/pom.xml index 50e10b309a1..e14c849cb8c 100644 --- a/ext/oidcc4ui/client-enduser/pom.xml +++ b/ext/oidcc4ui/client-enduser/pom.xml @@ -54,6 +54,18 @@ under the License. syncope-ext-oidcc4ui-client-common-ui ${project.version} + + + + org.apache.wicket + wicket-tester + test + + + org.junit.jupiter + junit-jupiter + test + diff --git a/ext/oidcc4ui/client-enduser/src/test/java/org/apache/syncope/client/enduser/pages/OIDCClientBeforeLogoutTest.java b/ext/oidcc4ui/client-enduser/src/test/java/org/apache/syncope/client/enduser/pages/OIDCClientBeforeLogoutTest.java new file mode 100644 index 00000000000..bf3954ee31a --- /dev/null +++ b/ext/oidcc4ui/client-enduser/src/test/java/org/apache/syncope/client/enduser/pages/OIDCClientBeforeLogoutTest.java @@ -0,0 +1,52 @@ +/* + * 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.enduser.pages; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.wicket.util.tester.WicketTester; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class OIDCClientBeforeLogoutTest { + + private WicketTester tester; + + @BeforeEach + void setUp() { + tester = new WicketTester(); + tester.setFollowRedirects(false); + } + + @AfterEach + void tearDown() { + tester.destroy(); + } + + @Test + void redirectsToBeforeLogoutResource() { + tester.startPage(OIDCClientBeforeLogout.class); + + String location = tester.getLastResponse().getRedirectLocation(); + assertNotNull(location); + assertTrue(location.endsWith("oidcc4ui/before-logout"), location); + } +} diff --git a/ext/oidcc4ui/client-enduser/src/test/java/org/apache/syncope/client/enduser/resources/EnduserCodeConsumerResourceTest.java b/ext/oidcc4ui/client-enduser/src/test/java/org/apache/syncope/client/enduser/resources/EnduserCodeConsumerResourceTest.java new file mode 100644 index 00000000000..4ebb4d0ac2f --- /dev/null +++ b/ext/oidcc4ui/client-enduser/src/test/java/org/apache/syncope/client/enduser/resources/EnduserCodeConsumerResourceTest.java @@ -0,0 +1,55 @@ +/* + * 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.enduser.resources; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.client.enduser.pages.SelfRegistration; +import org.apache.syncope.common.lib.Attr; +import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.wicket.markup.html.WebPage; +import org.apache.wicket.request.mapper.parameter.PageParameters; +import org.junit.jupiter.api.Test; + +class EnduserCodeConsumerResourceTest { + + private static final SyncopeJsonMapper MAPPER = new SyncopeJsonMapper(); + + @Test + void getSelfRegInfoSerializesNewUser() { + UserTO newUser = new UserTO(); + newUser.setUsername("john"); + newUser.getPlainAttrs().add(new Attr.Builder("email").value("john@example.com").build()); + + Pair, PageParameters> selfRegInfo = + new EnduserCodeConsumerResource().getSelfRegInfo(newUser); + + assertEquals(SelfRegistration.class, selfRegInfo.getLeft()); + + String serialized = selfRegInfo.getRight(). + get(SelfRegistration.NEW_USER_PARAM).toOptionalString(); + UserTO deserialized = MAPPER.readValue(serialized, UserTO.class); + assertEquals("john", deserialized.getUsername()); + assertEquals( + "john@example.com", + deserialized.getPlainAttr("email").orElseThrow().getValues().getFirst()); + } +} diff --git a/ext/oidcc4ui/common-lib/pom.xml b/ext/oidcc4ui/common-lib/pom.xml index 570f7dfb25e..4c32ead54c9 100644 --- a/ext/oidcc4ui/common-lib/pom.xml +++ b/ext/oidcc4ui/common-lib/pom.xml @@ -43,6 +43,13 @@ under the License. syncope-common-am-lib ${project.version} + + + + org.junit.jupiter + junit-jupiter + test + diff --git a/ext/oidcc4ui/common-lib/src/test/java/org/apache/syncope/common/lib/oidc/OIDCLoginResponseTest.java b/ext/oidcc4ui/common-lib/src/test/java/org/apache/syncope/common/lib/oidc/OIDCLoginResponseTest.java new file mode 100644 index 00000000000..672de12fb92 --- /dev/null +++ b/ext/oidcc4ui/common-lib/src/test/java/org/apache/syncope/common/lib/oidc/OIDCLoginResponseTest.java @@ -0,0 +1,64 @@ +/* + * 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.common.lib.oidc; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.OffsetDateTime; +import java.util.List; +import org.apache.syncope.common.lib.Attr; +import org.junit.jupiter.api.Test; + +class OIDCLoginResponseTest { + + @Test + void attributesAndSessionData() { + OIDCLoginResponse response = new OIDCLoginResponse(); + response.setUsername("john"); + response.setAccessToken("jwt"); + OffsetDateTime expiration = OffsetDateTime.now().plusMinutes(5); + response.setAccessTokenExpiryTime(expiration); + response.setLogoutSupported(true); + response.setSelfReg(true); + + assertEquals("john", response.getUsername()); + assertEquals("jwt", response.getAccessToken()); + assertEquals(expiration, response.getAccessTokenExpiryTime()); + assertTrue(response.isLogoutSupported()); + assertTrue(response.isSelfReg()); + + Attr email = new Attr.Builder("email").value("john@example.com").build(); + response.getAttrs().add(email); + + assertEquals(email, response.getAttr("email").orElseThrow()); + assertTrue(response.getAttr("firstname").isEmpty()); + } + + @Test + void multipleAttributesAreKept() { + OIDCLoginResponse response = new OIDCLoginResponse(); + response.getAttrs().add(new Attr.Builder("email").value("john@example.com").build()); + response.getAttrs().add(new Attr.Builder("firstname").value("John").build()); + + assertEquals( + List.of("email", "firstname"), + response.getAttrs().stream().map(Attr::getSchema).sorted().toList()); + } +} diff --git a/ext/oidcc4ui/common-lib/src/test/java/org/apache/syncope/common/lib/types/OIDCC4UITypesTest.java b/ext/oidcc4ui/common-lib/src/test/java/org/apache/syncope/common/lib/types/OIDCC4UITypesTest.java new file mode 100644 index 00000000000..40d32fbf37e --- /dev/null +++ b/ext/oidcc4ui/common-lib/src/test/java/org/apache/syncope/common/lib/types/OIDCC4UITypesTest.java @@ -0,0 +1,57 @@ +/* + * 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.common.lib.types; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Map; +import java.util.Set; +import org.junit.jupiter.api.Test; + +class OIDCC4UITypesTest { + + @Test + void entitlements() { + assertEquals( + Set.of( + OIDCC4UIEntitlement.OP_READ, + OIDCC4UIEntitlement.OP_CREATE, + OIDCC4UIEntitlement.OP_UPDATE, + OIDCC4UIEntitlement.OP_DELETE), + OIDCC4UIEntitlement.values()); + assertTrue(OIDCC4UIEntitlement.values().contains(OIDCC4UIEntitlement.OP_READ)); + assertThrows( + UnsupportedOperationException.class, + () -> OIDCC4UIEntitlement.values().add("unexpected")); + } + + @Test + void implementationTypes() { + assertEquals( + Map.of( + OIDCClientImplementationType.OP_ACTIONS, + "org.apache.syncope.core.provisioning.api.OIDCC4UIProviderActions"), + OIDCClientImplementationType.values()); + assertThrows( + UnsupportedOperationException.class, + () -> OIDCClientImplementationType.values().put("unexpected", "value")); + } +} diff --git a/ext/oidcc4ui/logic/pom.xml b/ext/oidcc4ui/logic/pom.xml index 5c21095fff6..a90f736fa47 100644 --- a/ext/oidcc4ui/logic/pom.xml +++ b/ext/oidcc4ui/logic/pom.xml @@ -54,6 +54,28 @@ under the License. org.pac4j pac4j-oidc + + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-junit-jupiter + test + + + com.github.ben-manes.caffeine + jcache + test + + + org.slf4j + slf4j-simple + test + diff --git a/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/OIDCC4UIProviderLogic.java b/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/OIDCC4UIProviderLogic.java index 90526eb144a..54f5123d46f 100644 --- a/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/OIDCC4UIProviderLogic.java +++ b/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/OIDCC4UIProviderLogic.java @@ -75,7 +75,7 @@ public String createFromDiscovery(final OIDCC4UIProviderTO opTO) { @PreAuthorize("hasRole('" + OIDCC4UIEntitlement.OP_CREATE + "')") public String create(final OIDCC4UIProviderTO opTO) { - if (opTO.getConnObjectKeyItem() == null) { + if (opTO.getConnObjectKeyItem().isEmpty()) { Item connObjectKeyItem = new Item(); connObjectKeyItem.setIntAttrName("username"); connObjectKeyItem.setExtAttrName("email"); diff --git a/ext/oidcc4ui/logic/src/test/java/org/apache/syncope/core/logic/OIDCC4UILogicTest.java b/ext/oidcc4ui/logic/src/test/java/org/apache/syncope/core/logic/OIDCC4UILogicTest.java new file mode 100644 index 00000000000..065e38df4c7 --- /dev/null +++ b/ext/oidcc4ui/logic/src/test/java/org/apache/syncope/core/logic/OIDCC4UILogicTest.java @@ -0,0 +1,458 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.crypto.MACSigner; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.PlainJWT; +import com.nimbusds.jwt.SignedJWT; +import java.lang.reflect.Method; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.oidc.OIDCLoginResponse; +import org.apache.syncope.common.lib.oidc.OIDCRequest; +import org.apache.syncope.common.lib.to.Item; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.core.logic.oidc.OIDCClientCache; +import org.apache.syncope.core.logic.oidc.OIDCUserManager; +import org.apache.syncope.core.persistence.api.Encryptor; +import org.apache.syncope.core.persistence.api.EncryptorManager; +import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.OIDCC4UIProviderDAO; +import org.apache.syncope.core.persistence.api.entity.OIDCC4UIProvider; +import org.apache.syncope.core.provisioning.api.data.AccessTokenDataBinder; +import org.apache.syncope.core.spring.security.AuthDataAccessor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.pac4j.core.context.CallContext; +import org.pac4j.core.context.session.SessionStore; +import org.pac4j.core.credentials.Credentials; +import org.pac4j.core.credentials.SessionKeyCredentials; +import org.pac4j.core.credentials.authenticator.Authenticator; +import org.pac4j.core.exception.http.FoundAction; +import org.pac4j.oidc.client.OidcClient; +import org.pac4j.oidc.credentials.OidcCredentials; + +class OIDCC4UILogicTest { + + private static final String KEY = "0123456789abcdef0123456789abcdef"; + + private static Method logoutMethod() { + try { + return OIDCC4UILogic.class.getMethod("createLogoutRequest", String.class, String.class); + } catch (NoSuchMethodException e) { + throw new IllegalStateException(e); + } + } + + private static final Method METHOD = logoutMethod(); + + private OIDCClientCache loginCache; + + private OIDCClientCache logoutCache; + + private AuthDataAccessor authDataAccessor; + + private AccessTokenDataBinder accessTokenDataBinder; + + private OIDCC4UIProviderDAO opDAO; + + private AccessTokenDAO accessTokenDAO; + + private OIDCUserManager userManager; + + private Encryptor encryptor; + + private OIDCC4UILogic logic; + + @BeforeEach + void setUp() { + loginCache = mock(OIDCClientCache.class); + logoutCache = mock(OIDCClientCache.class); + authDataAccessor = mock(AuthDataAccessor.class); + accessTokenDataBinder = mock(AccessTokenDataBinder.class); + opDAO = mock(OIDCC4UIProviderDAO.class); + accessTokenDAO = mock(AccessTokenDAO.class); + userManager = mock(OIDCUserManager.class); + + EncryptorManager encryptorManager = mock(EncryptorManager.class); + encryptor = mock(Encryptor.class); + when(encryptorManager.getInstance()).thenReturn(encryptor); + + logic = new OIDCC4UILogic( + loginCache, + logoutCache, + authDataAccessor, + accessTokenDataBinder, + opDAO, + accessTokenDAO, + userManager, + encryptorManager, + mock(SessionStore.class)); + } + + private static OIDCC4UIProvider provider() { + OIDCC4UIProvider op = mock(OIDCC4UIProvider.class); + when(op.getName()).thenReturn("op"); + when(op.getIssuer()).thenReturn("https://op.example.com"); + when(op.getEndSessionEndpoint()).thenReturn("https://op.example.com/logout"); + when(op.getItems()).thenReturn(List.of()); + when(op.getConnObjectKeyItem()).thenReturn(Optional.of(new Item())); + return op; + } + + private static String signedJwt(final JWTClaimsSet claims) throws Exception { + SignedJWT jwt = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claims); + jwt.sign(new MACSigner(KEY)); + return jwt.serialize(); + } + + private static OidcClient oidcClient() { + OidcClient client = mock(OidcClient.class); + when(client.getCallbackUrl()).thenReturn("https://client.example.com/callback"); + return client; + } + + @Test + void createLoginRequestMissingProvider() { + doReturn(Optional.empty()).when(opDAO).findByName("op"); + + assertThrows(NotFoundException.class, () -> logic.createLoginRequest("https://client/callback", "op", false)); + } + + @Test + void createLoginRequestUsesExistingClient() { + OIDCC4UIProvider op = provider(); + doReturn(Optional.of(op)).when(opDAO).findByName("op"); + + OidcClient client = oidcClient(); + when(loginCache.get("op")).thenReturn(Optional.of(client)); + when(client.getRedirectionAction(any(CallContext.class))). + thenReturn(Optional.of(new FoundAction("https://op.example.com/authorize"))); + + OIDCRequest request = logic.createLoginRequest("https://client.example.com/callback", "op", false); + + assertEquals("noop", request.id()); + assertEquals("https://op.example.com/authorize", request.location()); + verify(loginCache, never()).add(any(), anyString()); + } + + @Test + void createLoginRequestAddsClientWhenMissing() { + OIDCC4UIProvider op = provider(); + doReturn(Optional.of(op)).when(opDAO).findByName("op"); + + OidcClient client = oidcClient(); + when(loginCache.get("op")).thenReturn(Optional.empty()); + when(loginCache.add(op, "https://client.example.com/callback")).thenReturn(client); + when(client.getRedirectionAction(any(CallContext.class))). + thenReturn(Optional.of(new FoundAction("https://op.example.com/authorize"))); + + assertEquals("https://op.example.com/authorize", + logic.createLoginRequest("https://client.example.com/callback", "op", false).location()); + verify(loginCache).add(op, "https://client.example.com/callback"); + } + + @Test + void createLoginRequestFailsWithoutRedirectionAction() { + OIDCC4UIProvider op = provider(); + doReturn(Optional.of(op)).when(opDAO).findByName("op"); + + OidcClient client = oidcClient(); + when(loginCache.get("op")).thenReturn(Optional.of(client)); + when(client.getRedirectionAction(any(CallContext.class))).thenReturn(Optional.empty()); + + SyncopeClientException sce = assertThrows(SyncopeClientException.class, + () -> logic.createLoginRequest("https://client.example.com/callback", "op", false)); + assertEquals(ClientExceptionType.Unknown, sce.getType()); + } + + @Test + void loginReturnsTokensAndAttributes() throws Exception { + OIDCC4UIProvider op = provider(); + Item email = new Item(); + email.setExtAttrName("email"); + when(op.getItems()).thenReturn(List.of(email)); + doReturn(Optional.of(op)).when(opDAO).findByName("op"); + + OidcClient client = oidcClient(); + when(loginCache.get("op")).thenReturn(Optional.of(client)); + + JWTClaimsSet claims = new JWTClaimsSet.Builder(). + subject("sub"). + claim("email", "john@example.com"). + build(); + String idToken = new PlainJWT(claims).serialize(); + + Authenticator authenticator = mock(Authenticator.class); + when(client.getAuthenticator()).thenReturn(authenticator); + doAnswer(invocation -> { + OidcCredentials credentials = invocation.getArgument(1); + credentials.setIdToken(idToken); + return null; + }).when(authenticator).validate(any(CallContext.class), any(Credentials.class)); + + when(userManager.findMatchingUser(eq("sub"), any(Item.class))).thenReturn(List.of("john")); + when(op.isUpdateMatching()).thenReturn(false); + + when(encryptor.encode(anyString(), eq(CipherAlgorithm.AES))).thenReturn("AUTH"); + when(authDataAccessor.getAuthorities("john", null)).thenReturn(Set.of()); + + OffsetDateTime expiration = OffsetDateTime.now().plusMinutes(5); + when(accessTokenDataBinder.create( + eq(Optional.empty()), eq("john"), anyMap(), eq("AUTH"), eq(true))). + thenReturn(new AccessTokenDataBinder.AccessTokenInfo("jwt", expiration)); + + OIDCLoginResponse response = logic.login("requestId", "https://client/callback", "code", "op"); + + assertEquals("john", response.getUsername()); + assertEquals("jwt", response.getAccessToken()); + assertEquals(expiration, response.getAccessTokenExpiryTime()); + assertTrue(response.isLogoutSupported()); + assertFalse(response.isSelfReg()); + assertEquals("john@example.com", response.getAttr("email").orElseThrow().getValues().getFirst()); + assertTrue(response.getAttrs().stream().noneMatch(attr -> "sub".equals(attr.getSchema()))); + } + + @Test + void loginSelfRegistersUnmatchingUser() throws Exception { + OIDCC4UIProvider op = provider(); + doReturn(Optional.of(op)).when(opDAO).findByName("op"); + when(op.isSelfRegUnmatching()).thenReturn(true); + + OidcClient client = oidcClient(); + when(loginCache.get("op")).thenReturn(Optional.of(client)); + + JWTClaimsSet claims = new JWTClaimsSet.Builder().subject("sub").build(); + String idToken = new PlainJWT(claims).serialize(); + + Authenticator authenticator = mock(Authenticator.class); + when(client.getAuthenticator()).thenReturn(authenticator); + doAnswer(invocation -> { + OidcCredentials credentials = invocation.getArgument(1); + credentials.setIdToken(idToken); + return null; + }).when(authenticator).validate(any(CallContext.class), any(Credentials.class)); + + when(userManager.findMatchingUser(eq("sub"), any(Item.class))).thenReturn(List.of()); + + OIDCLoginResponse response = logic.login("requestId", "https://client/callback", "code", "op"); + + assertTrue(response.isSelfReg()); + assertEquals("sub", response.getUsername()); + assertEquals(null, response.getAccessToken()); + verify(accessTokenDataBinder, never()).create(any(), anyString(), anyMap(), any(), eq(true)); + } + + @Test + void loginFailsWhenValidationFails() { + OIDCC4UIProvider op = provider(); + doReturn(Optional.of(op)).when(opDAO).findByName("op"); + + OidcClient client = oidcClient(); + when(loginCache.get("op")).thenReturn(Optional.of(client)); + + Authenticator authenticator = mock(Authenticator.class); + when(client.getAuthenticator()).thenReturn(authenticator); + doAnswer(invocation -> { + throw new IllegalStateException("broken"); + }).when(authenticator).validate(any(CallContext.class), any(Credentials.class)); + + SyncopeClientException sce = assertThrows(SyncopeClientException.class, + () -> logic.login("requestId", "https://client/callback", "code", "op")); + assertEquals(ClientExceptionType.Unknown, sce.getType()); + assertTrue(sce.getElements().contains("broken")); + } + + @Test + void loginFailsWithoutClaims() { + OIDCC4UIProvider op = provider(); + doReturn(Optional.of(op)).when(opDAO).findByName("op"); + + OidcClient client = oidcClient(); + when(loginCache.get("op")).thenReturn(Optional.of(client)); + when(client.getAuthenticator()).thenReturn(mock(Authenticator.class)); + + SyncopeClientException sce = assertThrows(SyncopeClientException.class, + () -> logic.login("requestId", "https://client/callback", "code", "op")); + assertEquals(ClientExceptionType.Unknown, sce.getType()); + assertTrue(sce.getElements().contains("Unable to extract OIDC claims")); + } + + @Test + void loginFailsWhenSeveralUsersMatch() throws Exception { + OIDCC4UIProvider op = provider(); + doReturn(Optional.of(op)).when(opDAO).findByName("op"); + + OidcClient client = oidcClient(); + when(loginCache.get("op")).thenReturn(Optional.of(client)); + + JWTClaimsSet claims = new JWTClaimsSet.Builder().subject("sub").build(); + Authenticator authenticator = mock(Authenticator.class); + when(client.getAuthenticator()).thenReturn(authenticator); + doAnswer(invocation -> { + OidcCredentials credentials = invocation.getArgument(1); + credentials.setIdToken(new PlainJWT(claims).serialize()); + return null; + }).when(authenticator).validate(any(CallContext.class), any(Credentials.class)); + + when(userManager.findMatchingUser(eq("sub"), any(Item.class))).thenReturn(List.of("john", "jane")); + + assertThrows(IllegalArgumentException.class, + () -> logic.login("requestId", "https://client/callback", "code", "op")); + } + + @Test + void createLogoutRequestParsesAccessToken() throws Exception { + OIDCC4UIProvider op = provider(); + doReturn(Optional.of(op)).when(opDAO).findByName("op"); + + OidcClient client = oidcClient(); + when(logoutCache.get("op")).thenReturn(Optional.of(client)); + when(client.getLogoutAction(any(CallContext.class), any(), eq("https://client.example.com/callback"))). + thenReturn(Optional.of(new FoundAction("https://op.example.com/logout"))); + + String accessToken = signedJwt(new JWTClaimsSet.Builder(). + claim(OIDCC4UILogic.JWT_CLAIM_OP_NAME, "op"). + claim(OIDCC4UILogic.JWT_CLAIM_ID_TOKEN, "idToken"). + build()); + + OIDCRequest request = logic.createLogoutRequest(accessToken, "https://client.example.com/callback"); + + assertEquals("noop", request.id()); + assertEquals("https://op.example.com/logout", request.location()); + } + + @Test + void createLogoutRequestRejectsInvalidToken() { + SyncopeClientException sce = assertThrows(SyncopeClientException.class, + () -> logic.createLogoutRequest("not-a-jwt", "https://client.example.com/callback")); + assertEquals(ClientExceptionType.ExecutionError, sce.getType()); + } + + @Test + void createLogoutRequestMissingProvider() throws Exception { + doReturn(Optional.empty()).when(opDAO).findByName("op"); + + String accessToken = signedJwt(new JWTClaimsSet.Builder(). + claim(OIDCC4UILogic.JWT_CLAIM_OP_NAME, "op"). + build()); + + assertThrows(NotFoundException.class, + () -> logic.createLogoutRequest(accessToken, "https://client.example.com/callback")); + } + + @Test + void createLogoutRequestFailsWithoutLogoutAction() throws Exception { + OIDCC4UIProvider op = provider(); + doReturn(Optional.of(op)).when(opDAO).findByName("op"); + + OidcClient client = oidcClient(); + when(logoutCache.get("op")).thenReturn(Optional.of(client)); + when(client.getLogoutAction(any(CallContext.class), any(), anyString())).thenReturn(Optional.empty()); + + String accessToken = signedJwt(new JWTClaimsSet.Builder(). + claim(OIDCC4UILogic.JWT_CLAIM_OP_NAME, "op"). + build()); + + assertThrows(SyncopeClientException.class, + () -> logic.createLogoutRequest(accessToken, "https://client.example.com/callback")); + } + + @Test + void backChannelLogoutDeletesAccessToken() throws Exception { + OIDCC4UIProvider op = provider(); + doReturn(Optional.of(op)).when(opDAO).findByName("op"); + + OidcClient client = oidcClient(); + when(logoutCache.get("op")).thenReturn(Optional.of(client)); + + SessionKeyCredentials credentials = new SessionKeyCredentials(); + credentials.setSessionKey("sessionKey"); + when(client.getCredentials(any(CallContext.class))).thenReturn(Optional.of(credentials)); + + String logoutToken = signedJwt(new JWTClaimsSet.Builder().audience("op").build()); + + logic.backChannelLogout(logoutToken, "https://client.example.com/callback"); + + verify(accessTokenDAO).deleteById("sessionKey"); + } + + @Test + void backChannelLogoutRejectsInvalidToken() { + SyncopeClientException sce = assertThrows(SyncopeClientException.class, + () -> logic.backChannelLogout("not-a-jwt", "https://client.example.com/callback")); + assertEquals(ClientExceptionType.ExecutionError, sce.getType()); + } + + @Test + void backChannelLogoutMissingProvider() throws Exception { + doReturn(Optional.empty()).when(opDAO).findByName("op"); + + String logoutToken = signedJwt(new JWTClaimsSet.Builder().audience("op").build()); + + assertThrows(NotFoundException.class, + () -> logic.backChannelLogout(logoutToken, "https://client.example.com/callback")); + verify(accessTokenDAO, never()).deleteById(anyString()); + } + + @Test + void backChannelLogoutFailsWhenTokenCannotBeValidated() throws Exception { + OIDCC4UIProvider op = provider(); + doReturn(Optional.of(op)).when(opDAO).findByName("op"); + + OidcClient client = oidcClient(); + when(logoutCache.get("op")).thenReturn(Optional.of(client)); + when(client.getCredentials(any(CallContext.class))).thenReturn(Optional.empty()); + + String logoutToken = signedJwt(new JWTClaimsSet.Builder().audience("op").build()); + + SyncopeClientException sce = assertThrows(SyncopeClientException.class, + () -> logic.backChannelLogout(logoutToken, "https://client.example.com/callback")); + assertEquals(ClientExceptionType.Unknown, sce.getType()); + assertTrue(sce.getElements().contains("Could not validate the logout token")); + } + + @Test + void resolveReferenceIsNotSupported() { + assertThrows(UnresolvedReferenceException.class, () -> logic.resolveBeanReference(METHOD, "arg")); + } +} diff --git a/ext/oidcc4ui/logic/src/test/java/org/apache/syncope/core/logic/OIDCC4UIProviderLogicTest.java b/ext/oidcc4ui/logic/src/test/java/org/apache/syncope/core/logic/OIDCC4UIProviderLogicTest.java new file mode 100644 index 00000000000..a6a8256e43b --- /dev/null +++ b/ext/oidcc4ui/logic/src/test/java/org/apache/syncope/core/logic/OIDCC4UIProviderLogicTest.java @@ -0,0 +1,367 @@ +/* + * 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.core.logic; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.to.Item; +import org.apache.syncope.common.lib.to.OIDCC4UIProviderTO; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.OIDCC4UIEntitlement; +import org.apache.syncope.core.logic.oidc.OIDCClientCache; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.OIDCC4UIProviderDAO; +import org.apache.syncope.core.persistence.api.entity.OIDCC4UIProvider; +import org.apache.syncope.core.provisioning.api.data.OIDCC4UIProviderDataBinder; +import org.apache.syncope.core.spring.security.SyncopeGrantedAuthority; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; + +class OIDCC4UIProviderLogicTest { + + private static Method nonReadOnlyMethod() { + try { + return OIDCC4UIProviderLogic.class.getMethod("delete", String.class); + } catch (NoSuchMethodException e) { + throw new IllegalStateException(e); + } + } + + private static final Method METHOD = nonReadOnlyMethod(); + + private OIDCClientCache loginCache; + + private OIDCClientCache logoutCache; + + private OIDCC4UIProviderDAO opDAO; + + private OIDCC4UIProviderDataBinder binder; + + private OIDCC4UIProviderLogic logic; + + @BeforeEach + void setUp() { + loginCache = mock(OIDCClientCache.class); + logoutCache = mock(OIDCClientCache.class); + opDAO = mock(OIDCC4UIProviderDAO.class); + binder = mock(OIDCC4UIProviderDataBinder.class); + + logic = new OIDCC4UIProviderLogic(loginCache, logoutCache, opDAO, binder); + } + + @AfterEach + void tearDown() { + SecurityContextHolder.clearContext(); + } + + private static void authenticate(final String entitlement) { + List authorities = + List.of(new SyncopeGrantedAuthority(entitlement, "master")); + SecurityContextHolder.getContext().setAuthentication( + new UsernamePasswordAuthenticationToken("admin", "password", authorities)); + } + + private static OIDCC4UIProvider provider(final String key, final String name, final String issuer) { + OIDCC4UIProvider op = mock(OIDCC4UIProvider.class); + when(op.getKey()).thenReturn(key); + when(op.getName()).thenReturn(name); + when(op.getIssuer()).thenReturn(issuer); + return op; + } + + @Test + void createAddsDefaultConnObjectKeyItem() { + OIDCC4UIProviderTO opTO = new OIDCC4UIProviderTO(); + opTO.setName("op"); + assertTrue(opTO.getConnObjectKeyItem().isEmpty()); + + OIDCC4UIProvider op = provider("key", "op", "https://op.example.com"); + when(binder.create(opTO)).thenReturn(op); + + assertEquals("key", logic.create(opTO)); + + Item connObjectKeyItem = opTO.getConnObjectKeyItem().orElseThrow(); + assertEquals("username", connObjectKeyItem.getIntAttrName()); + assertEquals("email", connObjectKeyItem.getExtAttrName()); + assertTrue(connObjectKeyItem.isConnObjectKey()); + assertEquals("true", connObjectKeyItem.getMandatoryCondition()); + verify(binder).create(opTO); + } + + @Test + void createKeepsProvidedConnObjectKeyItem() { + OIDCC4UIProviderTO opTO = new OIDCC4UIProviderTO(); + opTO.setName("op"); + Item item = new Item(); + item.setIntAttrName("email"); + opTO.setConnObjectKeyItem(item); + + OIDCC4UIProvider op = provider("key", "op", "https://op.example.com"); + when(binder.create(opTO)).thenReturn(op); + + assertEquals("key", logic.create(opTO)); + + assertEquals("email", opTO.getConnObjectKeyItem().orElseThrow().getIntAttrName()); + verify(binder).create(opTO); + } + + @Test + void createFromDiscoveryImportsMetadataThenCreates() { + OIDCC4UIProviderTO opTO = new OIDCC4UIProviderTO(); + opTO.setIssuer("https://op.example.com"); + + OIDCC4UIProvider op = provider("key", "op", "https://op.example.com"); + when(binder.create(opTO)).thenReturn(op); + + try (MockedStatic mocked = mockStatic(OIDCClientCache.class)) { + assertEquals("key", logic.createFromDiscovery(opTO)); + mocked.verify(() -> OIDCClientCache.importMetadata(opTO)); + } + + verify(binder).create(opTO); + } + + @Test + void createFromDiscoveryWrapsFailures() { + OIDCC4UIProviderTO opTO = new OIDCC4UIProviderTO(); + opTO.setIssuer("https://op.example.com"); + + try (MockedStatic mocked = mockStatic(OIDCClientCache.class)) { + mocked.when(() -> OIDCClientCache.importMetadata(opTO)).thenThrow(new IOException("boom")); + + SyncopeClientException sce = assertThrows(SyncopeClientException.class, + () -> logic.createFromDiscovery(opTO)); + assertEquals(ClientExceptionType.Unknown, sce.getType()); + assertTrue(sce.getElements().contains("boom")); + } + + verify(binder, never()).create(any()); + } + + @Test + void listHidesClientSecretForNonAdmin() { + OIDCC4UIProvider op = provider("key", "op", "https://op.example.com"); + doReturn(List.of(op)).when(opDAO).findAll(); + + OIDCC4UIProviderTO bound = new OIDCC4UIProviderTO(); + bound.setKey("key"); + bound.setClientSecret("secret"); + when(binder.getOIDCProviderTO(op)).thenReturn(bound); + + List providers = logic.list(); + + assertEquals(1, providers.size()); + assertEquals(null, providers.getFirst().getClientSecret()); + } + + @Test + void listKeepsClientSecretForAdmin() { + authenticate(OIDCC4UIEntitlement.OP_READ); + + OIDCC4UIProvider op = provider("key", "op", "https://op.example.com"); + doReturn(List.of(op)).when(opDAO).findAll(); + + OIDCC4UIProviderTO bound = new OIDCC4UIProviderTO(); + bound.setKey("key"); + bound.setClientSecret("secret"); + when(binder.getOIDCProviderTO(op)).thenReturn(bound); + + List providers = logic.list(); + + assertEquals(1, providers.size()); + assertEquals("secret", providers.getFirst().getClientSecret()); + } + + @Test + void readMissingProvider() { + doReturn(Optional.empty()).when(opDAO).findById("missing"); + + assertThrows(NotFoundException.class, () -> logic.read("missing")); + } + + @Test + void readExistingProvider() { + OIDCC4UIProvider op = provider("key", "op", "https://op.example.com"); + OIDCC4UIProviderTO bound = new OIDCC4UIProviderTO(); + bound.setKey("key"); + doReturn(Optional.of(op)).when(opDAO).findById("key"); + when(binder.getOIDCProviderTO(op)).thenReturn(bound); + + assertEquals("key", logic.read("key").getKey()); + } + + @Test + void updateRejectsIssuerMismatch() { + OIDCC4UIProvider op = provider("key", "op", "https://op.example.com"); + doReturn(Optional.of(op)).when(opDAO).findById("key"); + + OIDCC4UIProviderTO opTO = new OIDCC4UIProviderTO(); + opTO.setKey("key"); + opTO.setIssuer("https://other.example.com"); + + SyncopeClientException sce = assertThrows(SyncopeClientException.class, () -> logic.update(opTO)); + assertEquals(ClientExceptionType.InvalidEntity, sce.getType()); + verify(binder, never()).update(any(), any()); + } + + @Test + void updateEvictsCachedClients() { + OIDCC4UIProvider op = provider("key", "op", "https://op.example.com"); + doReturn(Optional.of(op)).when(opDAO).findById("key"); + + OIDCC4UIProviderTO opTO = new OIDCC4UIProviderTO(); + opTO.setKey("key"); + opTO.setIssuer("https://op.example.com"); + + logic.update(opTO); + + verify(binder).update(op, opTO); + verify(loginCache).removeAll("op"); + verify(logoutCache).removeAll("op"); + } + + @Test + void updateMissingProvider() { + doReturn(Optional.empty()).when(opDAO).findById("key"); + + OIDCC4UIProviderTO opTO = new OIDCC4UIProviderTO(); + opTO.setKey("key"); + + assertThrows(NotFoundException.class, () -> logic.update(opTO)); + } + + @Test + void deleteEvictsCachedClients() { + OIDCC4UIProvider op = provider("key", "op", "https://op.example.com"); + doReturn(Optional.of(op)).when(opDAO).findById("key"); + + logic.delete("key"); + + verify(opDAO).deleteById("key"); + verify(loginCache).removeAll("op"); + verify(logoutCache).removeAll("op"); + } + + @Test + void deleteMissingProvider() { + doReturn(Optional.empty()).when(opDAO).findById("key"); + + assertThrows(NotFoundException.class, () -> logic.delete("key")); + verify(opDAO, never()).deleteById(any()); + } + + @Test + void resolveReferenceFromStringArgument() throws Exception { + OIDCC4UIProvider op = provider("key", "op", "https://op.example.com"); + OIDCC4UIProviderTO bound = new OIDCC4UIProviderTO(); + bound.setKey("key"); + doReturn(Optional.of(op)).when(opDAO).findById("key"); + when(binder.getOIDCProviderTO(op)).thenReturn(bound); + + assertEquals("key", logic.resolveBeanReference(METHOD, "key").getKey()); + } + + @Test + void resolveReferenceFromTOArgument() throws Exception { + OIDCC4UIProvider op = provider("key", "op", "https://op.example.com"); + OIDCC4UIProviderTO bound = new OIDCC4UIProviderTO(); + bound.setKey("key"); + doReturn(Optional.of(op)).when(opDAO).findById("key"); + when(binder.getOIDCProviderTO(op)).thenReturn(bound); + + OIDCC4UIProviderTO opTO = new OIDCC4UIProviderTO(); + opTO.setKey("key"); + + assertEquals("key", logic.resolveBeanReference(METHOD, opTO).getKey()); + } + + @Test + void resolveReferenceWithoutKeyFails() { + assertThrows(UnresolvedReferenceException.class, () -> logic.resolveBeanReference(METHOD, 42)); + } + + @Test + void resolveReferenceWithMissingProviderFails() { + doReturn(Optional.empty()).when(opDAO).findById("key"); + + assertThrows(UnresolvedReferenceException.class, () -> logic.resolveBeanReference(METHOD, "key")); + } + + @Test + void resolveReferenceForReadOnlyMethodFails() throws Exception { + Method read = OIDCC4UIProviderLogic.class.getMethod("read", String.class); + + assertThrows(UnresolvedReferenceException.class, () -> logic.resolveBeanReference(read, "key")); + verify(opDAO, never()).findById(eq("key")); + } + + @Test + void listHandlesEmptyDAO() { + doReturn(List.of()).when(opDAO).findAll(); + + assertTrue(logic.list().isEmpty()); + verifyNoInteractionsWithCaches(); + } + + private void verifyNoInteractionsWithCaches() { + verify(loginCache, never()).get(any()); + verify(logoutCache, never()).get(any()); + verify(loginCache, never()).add(any(), any()); + verify(logoutCache, never()).add(any(), any()); + verify(loginCache, never()).removeAll(any()); + verify(logoutCache, never()).removeAll(any()); + } + + @Test + void authenticatedUserWithoutEntitlementGetsNoSecret() { + SecurityContextHolder.getContext().setAuthentication( + new UsernamePasswordAuthenticationToken("user", "password", Set.of())); + + OIDCC4UIProvider op = provider("key", "op", "https://op.example.com"); + doReturn(List.of(op)).when(opDAO).findAll(); + + OIDCC4UIProviderTO bound = new OIDCC4UIProviderTO(); + bound.setKey("key"); + bound.setClientSecret("secret"); + when(binder.getOIDCProviderTO(op)).thenReturn(bound); + + assertEquals(null, logic.list().getFirst().getClientSecret()); + } +} diff --git a/ext/oidcc4ui/logic/src/test/java/org/apache/syncope/core/logic/oidc/JCacheSessionStoreTest.java b/ext/oidcc4ui/logic/src/test/java/org/apache/syncope/core/logic/oidc/JCacheSessionStoreTest.java new file mode 100644 index 00000000000..bd9c8404ab7 --- /dev/null +++ b/ext/oidcc4ui/logic/src/test/java/org/apache/syncope/core/logic/oidc/JCacheSessionStoreTest.java @@ -0,0 +1,185 @@ +/* + * 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.core.logic.oidc; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.security.InvalidKeyException; +import java.util.Map; +import javax.cache.Cache; +import javax.cache.CacheManager; +import javax.cache.Caching; +import javax.cache.configuration.MutableConfiguration; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.core.persistence.api.Encryptor; +import org.apache.syncope.core.persistence.api.EncryptorManager; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.pac4j.core.context.WebContext; + +class JCacheSessionStoreTest { + + private CacheManager cacheManager; + + private Cache> cache; + + private Encryptor encryptor; + + private JCacheSessionStore store; + + @SuppressWarnings("unchecked") + @BeforeEach + void setUp() { + cacheManager = Caching.getCachingProvider( + "com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider"). + getCacheManager(); + MutableConfiguration> configuration = + new MutableConfiguration>(). + setTypes(String.class, (Class>) (Class) Map.class). + setStoreByValue(false). + setReadThrough(false); + cache = cacheManager.createCache("sessionStore", configuration); + + encryptor = mock(Encryptor.class); + EncryptorManager encryptorManager = mock(EncryptorManager.class); + when(encryptorManager.getInstance()).thenReturn(encryptor); + + store = new JCacheSessionStore(cache, encryptorManager); + } + + @AfterEach + void tearDown() { + if (cache != null) { + cacheManager.destroyCache(cache.getName()); + } + cacheManager.close(); + } + + private void encryptsAndDecrypts() throws Exception { + when(encryptor.encode(anyString(), eq(CipherAlgorithm.AES))). + thenAnswer(invocation -> "ENC(" + invocation.getArgument(0) + ')'); + when(encryptor.decode(anyString(), eq(CipherAlgorithm.AES))).thenAnswer(invocation -> { + String value = invocation.getArgument(0); + return value.substring("ENC(".length(), value.length() - 1); + }); + } + + @Test + void createSessionOnlyWhenRequested() { + OIDCC4UIContext context = new OIDCC4UIContext(); + + assertTrue(store.getSessionId(context, false).isEmpty()); + + String id = store.getSessionId(context, true).orElseThrow(); + assertEquals(id, context.getId()); + assertNotNull(cache.get(id)); + } + + @Test + void setAndGet() throws Exception { + encryptsAndDecrypts(); + + OIDCC4UIContext context = new OIDCC4UIContext(); + store.set(context, "key", "value"); + + assertEquals("ENC(value)", cache.get(context.getId()).get("key")); + assertEquals("value", store.get(context, "key").orElseThrow()); + } + + @Test + void nullValueRemovesKey() throws Exception { + encryptsAndDecrypts(); + + OIDCC4UIContext context = new OIDCC4UIContext(); + store.set(context, "key", "value"); + store.set(context, "key", null); + + assertTrue(store.get(context, "key").isEmpty()); + } + + @Test + void decryptFailureReturnsEmptyString() throws Exception { + encryptsAndDecrypts(); + + OIDCC4UIContext context = new OIDCC4UIContext(); + store.set(context, "key", "value"); + + when(encryptor.decode(anyString(), eq(CipherAlgorithm.AES))).thenThrow(new InvalidKeyException()); + + assertEquals("", store.get(context, "key").orElseThrow()); + } + + @Test + void destroySession() throws Exception { + encryptsAndDecrypts(); + + OIDCC4UIContext context = new OIDCC4UIContext(); + store.set(context, "key", "value"); + String id = context.getId(); + + assertTrue(store.destroySession(context)); + assertFalse(cache.containsKey(id)); + assertTrue(store.get(context, "key").isEmpty()); + } + + @Test + void nonOIDCContextIsIgnored() throws Exception { + encryptsAndDecrypts(); + + WebContext context = mock(WebContext.class); + + assertTrue(store.getSessionId(context, true).isEmpty()); + assertTrue(store.get(context, "key").isEmpty()); + assertFalse(store.destroySession(context)); + + store.set(context, "key", "value"); + assertFalse(cache.iterator().hasNext()); + } + + @Test + void trackableSessionsAreNotSupported() { + WebContext context = mock(WebContext.class); + + assertTrue(store.getTrackableSession(context).isEmpty()); + assertTrue(store.buildFromTrackableSession(context, new Object()).isEmpty()); + assertFalse(store.renewSession(context)); + } + + @Test + void sessionIsSharedAcrossStoreInstances() throws Exception { + encryptsAndDecrypts(); + + OIDCC4UIContext context = new OIDCC4UIContext(); + store.set(context, "key", "value"); + + EncryptorManager encryptorManager = mock(EncryptorManager.class); + when(encryptorManager.getInstance()).thenReturn(encryptor); + JCacheSessionStore other = new JCacheSessionStore(cache, encryptorManager); + + assertEquals("value", other.get(context, "key").orElseThrow()); + } +} diff --git a/ext/oidcc4ui/logic/src/test/java/org/apache/syncope/core/logic/oidc/NoOpSessionStoreTest.java b/ext/oidcc4ui/logic/src/test/java/org/apache/syncope/core/logic/oidc/NoOpSessionStoreTest.java new file mode 100644 index 00000000000..d23694126e2 --- /dev/null +++ b/ext/oidcc4ui/logic/src/test/java/org/apache/syncope/core/logic/oidc/NoOpSessionStoreTest.java @@ -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. + */ +package org.apache.syncope.core.logic.oidc; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; + +import org.junit.jupiter.api.Test; +import org.pac4j.core.context.WebContext; + +class NoOpSessionStoreTest { + + private final NoOpSessionStore store = NoOpSessionStore.INSTANCE; + + @Test + void neverStoresAnything() { + WebContext context = mock(WebContext.class); + + assertTrue(store.getSessionId(context, true).isEmpty()); + assertTrue(store.get(context, "key").isEmpty()); + assertTrue(store.getTrackableSession(context).isEmpty()); + assertTrue(store.buildFromTrackableSession(context, new Object()).isEmpty()); + + store.set(context, "key", "value"); + assertTrue(store.get(context, "key").isEmpty()); + + assertTrue(store.destroySession(context)); + assertFalse(store.renewSession(context)); + } +} diff --git a/ext/oidcc4ui/logic/src/test/java/org/apache/syncope/core/logic/oidc/OIDCClientCacheTest.java b/ext/oidcc4ui/logic/src/test/java/org/apache/syncope/core/logic/oidc/OIDCClientCacheTest.java new file mode 100644 index 00000000000..dc077a77550 --- /dev/null +++ b/ext/oidcc4ui/logic/src/test/java/org/apache/syncope/core/logic/oidc/OIDCClientCacheTest.java @@ -0,0 +1,94 @@ +/* + * 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.core.logic.oidc; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.oauth2.sdk.id.Issuer; +import com.nimbusds.openid.connect.sdk.SubjectType; +import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata; +import java.net.URI; +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.OIDCC4UIProvider; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.pac4j.core.context.session.SessionStore; +import org.pac4j.oidc.client.OidcClient; + +class OIDCClientCacheTest { + + private static OIDCC4UIProvider provider() { + OIDCC4UIProvider op = mock(OIDCC4UIProvider.class); + when(op.getName()).thenReturn("op"); + when(op.getClientID()).thenReturn("clientID"); + when(op.getClientSecret()).thenReturn("clientSecret"); + when(op.getIssuer()).thenReturn("https://op.example.com"); + when(op.getJwksUri()).thenReturn("https://op.example.com/jwks"); + when(op.getAuthorizationEndpoint()).thenReturn("https://op.example.com/authorize"); + when(op.getTokenEndpoint()).thenReturn("https://op.example.com/token"); + when(op.getUserinfoEndpoint()).thenReturn("https://op.example.com/userinfo"); + when(op.getEndSessionEndpoint()).thenReturn("https://op.example.com/logout"); + when(op.getScopes()).thenReturn(List.of("openid", "email")); + when(op.getHasDiscovery()).thenReturn(false); + return op; + } + + @Test + void nonceOnlyWithRealSessionStore() { + assertFalse(new OIDCClientCache(NoOpSessionStore.INSTANCE).useNonce); + assertTrue(new OIDCClientCache(mock(SessionStore.class)).useNonce); + } + + @Test + void addGetAndRemove() { + OIDCClientCache cache = new OIDCClientCache(NoOpSessionStore.INSTANCE); + + assertTrue(cache.get("op").isEmpty()); + assertFalse(cache.removeAll("op")); + + OIDCProviderMetadata metadata = new OIDCProviderMetadata( + new Issuer("https://op.example.com"), + List.of(SubjectType.PUBLIC), + URI.create("https://op.example.com/jwks")); + metadata.setIDTokenJWSAlgs(List.of(JWSAlgorithm.RS256)); + + OIDCC4UIProvider provider = provider(); + when(provider.getHasDiscovery()).thenReturn(true); + + try (MockedStatic mocked = mockStatic(OIDCClientCache.class)) { + mocked.when(() -> OIDCClientCache.fetchMetadata("https://op.example.com")).thenReturn(metadata); + + OidcClient client = cache.add(provider, "https://client.example.com/callback"); + + assertSame(client, cache.get("op").orElseThrow()); + assertEquals("https://client.example.com/callback", client.getCallbackUrl()); + assertFalse(client.getConfiguration().isUseNonce()); + } + + assertTrue(cache.removeAll("op")); + assertTrue(cache.get("op").isEmpty()); + } +} diff --git a/ext/oidcc4ui/logic/src/test/java/org/apache/syncope/core/logic/oidc/OIDCUserManagerTest.java b/ext/oidcc4ui/logic/src/test/java/org/apache/syncope/core/logic/oidc/OIDCUserManagerTest.java new file mode 100644 index 00000000000..0401d5e6163 --- /dev/null +++ b/ext/oidcc4ui/logic/src/test/java/org/apache/syncope/core/logic/oidc/OIDCUserManagerTest.java @@ -0,0 +1,358 @@ +/* + * 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.core.logic.oidc; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.text.ParseException; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.Attr; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.oidc.OIDCLoginResponse; +import org.apache.syncope.common.lib.request.UserCR; +import org.apache.syncope.common.lib.request.UserUR; +import org.apache.syncope.common.lib.to.Item; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.MatchType; +import org.apache.syncope.common.lib.types.SchemaType; +import org.apache.syncope.core.persistence.api.dao.ImplementationDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.OIDCC4UIProvider; +import org.apache.syncope.core.persistence.api.entity.OIDCC4UIUserTemplate; +import org.apache.syncope.core.persistence.api.entity.Schema; +import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.IntAttrName; +import org.apache.syncope.core.provisioning.api.IntAttrNameParser; +import org.apache.syncope.core.provisioning.api.ProvisioningManager; +import org.apache.syncope.core.provisioning.api.UserProvisioningManager; +import org.apache.syncope.core.provisioning.api.data.UserDataBinder; +import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils; +import org.apache.syncope.core.provisioning.api.rules.InboundMatch; +import org.apache.syncope.core.provisioning.java.pushpull.InboundMatcher; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +class OIDCUserManagerTest { + + private InboundMatcher inboundMatcher; + + private UserDAO userDAO; + + private ImplementationDAO implementationDAO; + + private IntAttrNameParser intAttrNameParser; + + private TemplateUtils templateUtils; + + private UserProvisioningManager provisioningManager; + + private UserDataBinder binder; + + private OIDCUserManager userManager; + + @BeforeEach + void setUp() { + inboundMatcher = mock(InboundMatcher.class); + userDAO = mock(UserDAO.class); + implementationDAO = mock(ImplementationDAO.class); + intAttrNameParser = mock(IntAttrNameParser.class); + templateUtils = mock(TemplateUtils.class); + provisioningManager = mock(UserProvisioningManager.class); + binder = mock(UserDataBinder.class); + + userManager = new OIDCUserManager( + inboundMatcher, + userDAO, + implementationDAO, + intAttrNameParser, + templateUtils, + provisioningManager, + binder); + } + + private static OIDCC4UIProvider provider() { + OIDCC4UIProvider op = mock(OIDCC4UIProvider.class); + when(op.getName()).thenReturn("op"); + when(op.getItems()).thenReturn(List.of()); + when(op.getActions()).thenReturn(List.of()); + return op; + } + + private static Item item(final String intAttrName, final String extAttrName) { + Item item = new Item(); + item.setIntAttrName(intAttrName); + item.setExtAttrName(extAttrName); + return item; + } + + private static IntAttrName field(final String name) { + IntAttrName intAttrName = mock(IntAttrName.class); + when(intAttrName.getField()).thenReturn(name); + return intAttrName; + } + + private static IntAttrName plainSchema(final String schemaKey) { + Schema schema = mock(Schema.class); + when(schema.getKey()).thenReturn(schemaKey); + + IntAttrName intAttrName = mock(IntAttrName.class); + when(intAttrName.getSchemaInfo()).thenReturn(new IntAttrName.SchemaInfo(schema, SchemaType.PLAIN)); + return intAttrName; + } + + @Test + void findMatchingUserReturnsMatchingUsernames() { + User john = mock(User.class); + when(john.getUsername()).thenReturn("john"); + User jane = mock(User.class); + when(jane.getUsername()).thenReturn("jane"); + + Item connObjectKeyItem = item("username", "email"); + when(inboundMatcher.matchByConnObjectKeyValue( + connObjectKeyItem, "value", AnyTypeKind.USER, null, false)). + thenReturn(List.of(new InboundMatch(MatchType.ANY, john), new InboundMatch(MatchType.ANY, jane))); + + assertEquals(List.of("john", "jane"), userManager.findMatchingUser("value", connObjectKeyItem)); + } + + @Test + void findMatchingUserSkipsMatchesWithoutAny() { + User john = mock(User.class); + when(john.getUsername()).thenReturn("john"); + + Item connObjectKeyItem = item("username", "email"); + when(inboundMatcher.matchByConnObjectKeyValue( + connObjectKeyItem, "value", AnyTypeKind.USER, null, false)). + thenReturn(List.of( + new InboundMatch(MatchType.ANY, mock(LinkedAccount.class)), + new InboundMatch(MatchType.ANY, john))); + + assertEquals(List.of("john"), userManager.findMatchingUser("value", connObjectKeyItem)); + } + + @Test + void fillSetsUsername() throws Exception { + OIDCC4UIProvider op = provider(); + when(op.getItems()).thenReturn(List.of(item("username", "email"))); + doReturn(field("username")).when(intAttrNameParser).parse("username", AnyTypeKind.USER); + + OIDCLoginResponse response = new OIDCLoginResponse(); + response.getAttrs().add(new Attr.Builder("email").value("john@example.com").build()); + + UserTO userTO = new UserTO(); + userManager.fill(op, response, userTO); + + assertEquals("john@example.com", userTO.getUsername()); + } + + @Test + void fillAddsPlainAttribute() throws Exception { + OIDCC4UIProvider op = provider(); + when(op.getItems()).thenReturn(List.of(item("firstname", "given_name"))); + doReturn(plainSchema("firstname")).when(intAttrNameParser).parse("firstname", AnyTypeKind.USER); + + OIDCLoginResponse response = new OIDCLoginResponse(); + response.getAttrs().add(new Attr.Builder("given_name").values("John", "Johnny").build()); + + UserTO userTO = new UserTO(); + userManager.fill(op, response, userTO); + + Attr firstname = userTO.getPlainAttr("firstname").orElseThrow(); + assertEquals(List.of("John", "Johnny"), firstname.getValues()); + } + + @Test + void fillReplacesExistingPlainAttribute() throws Exception { + OIDCC4UIProvider op = provider(); + when(op.getItems()).thenReturn(List.of(item("firstname", "given_name"))); + doReturn(plainSchema("firstname")).when(intAttrNameParser).parse("firstname", AnyTypeKind.USER); + + OIDCLoginResponse response = new OIDCLoginResponse(); + response.getAttrs().add(new Attr.Builder("given_name").value("John").build()); + + UserTO userTO = new UserTO(); + userTO.getPlainAttrs().add(new Attr.Builder("firstname").value("Old").build()); + + userManager.fill(op, response, userTO); + + assertEquals(List.of("John"), userTO.getPlainAttr("firstname").orElseThrow().getValues()); + } + + @Test + void fillWithoutAttributeLeavesUsernameUnset() throws Exception { + OIDCC4UIProvider op = provider(); + when(op.getItems()).thenReturn(List.of(item("username", "email"))); + doReturn(field("username")).when(intAttrNameParser).parse("username", AnyTypeKind.USER); + + UserTO userTO = new UserTO(); + userManager.fill(op, new OIDCLoginResponse(), userTO); + + assertEquals(null, userTO.getUsername()); + } + + @Test + void fillIgnoresInvalidIntAttrName() throws Exception { + OIDCC4UIProvider op = provider(); + when(op.getItems()).thenReturn(List.of(item("invalid", "email"))); + when(intAttrNameParser.parse("invalid", AnyTypeKind.USER)).thenThrow(new ParseException("invalid", 0)); + + OIDCLoginResponse response = new OIDCLoginResponse(); + response.getAttrs().add(new Attr.Builder("email").value("john@example.com").build()); + + UserTO userTO = new UserTO(); + userManager.fill(op, response, userTO); + + assertEquals(null, userTO.getUsername()); + assertTrue(userTO.getPlainAttrs().isEmpty()); + } + + @Test + void fillIgnoresUnsupportedField() throws Exception { + OIDCC4UIProvider op = provider(); + when(op.getItems()).thenReturn(List.of(item("surname", "family_name"))); + doReturn(field("surname")).when(intAttrNameParser).parse("surname", AnyTypeKind.USER); + + OIDCLoginResponse response = new OIDCLoginResponse(); + response.getAttrs().add(new Attr.Builder("family_name").value("Doe").build()); + + UserTO userTO = new UserTO(); + userManager.fill(op, response, userTO); + + assertEquals(null, userTO.getUsername()); + assertTrue(userTO.getPlainAttrs().isEmpty()); + } + + @Test + void createUsesDefaultUsernameAndRootRealm() throws Exception { + OIDCC4UIProvider op = provider(); + when(provisioningManager.create( + any(UserCR.class), eq(false), eq("default"), eq(OIDCUserManager.OIDC_CLIENT_CONTEXT))). + thenReturn(new ProvisioningManager.ProvisioningResult<>("created", List.of())); + + UserTO created = new UserTO(); + created.setUsername("created"); + when(binder.getUserTO("created")).thenReturn(created); + + assertEquals("created", userManager.create(op, new OIDCLoginResponse(), "default")); + + ArgumentCaptor userCR = ArgumentCaptor.forClass(UserCR.class); + verify(provisioningManager).create( + userCR.capture(), eq(false), eq("default"), eq(OIDCUserManager.OIDC_CLIENT_CONTEXT)); + assertEquals("default", userCR.getValue().getUsername()); + assertEquals(SyncopeConstants.ROOT_REALM, userCR.getValue().getRealm()); + } + + @Test + void createUsesUsernameFromResponse() throws Exception { + OIDCC4UIProvider op = provider(); + + IntAttrName intAttrName = field("username"); + Item item = item("username", "email"); + when(op.getItems()).thenReturn(List.of(item)); + when(intAttrNameParser.parse("username", AnyTypeKind.USER)).thenReturn(intAttrName); + + OIDCLoginResponse response = new OIDCLoginResponse(); + response.getAttrs().add(new Attr.Builder("email").value("john").build()); + + when(provisioningManager.create( + any(UserCR.class), eq(false), eq("john"), eq(OIDCUserManager.OIDC_CLIENT_CONTEXT))). + thenReturn(new ProvisioningManager.ProvisioningResult<>("created", List.of())); + + UserTO created = new UserTO(); + created.setUsername("john"); + when(binder.getUserTO("created")).thenReturn(created); + + assertEquals("john", userManager.create(op, response, "default")); + } + + @Test + void createAppliesUserTemplate() throws Exception { + OIDCC4UIProvider op = provider(); + + OIDCC4UIUserTemplate template = mock(OIDCC4UIUserTemplate.class); + when(template.get()).thenReturn(new UserTO()); + when(op.getUserTemplate()).thenReturn(template); + + when(provisioningManager.create( + any(UserCR.class), eq(false), eq("default"), eq(OIDCUserManager.OIDC_CLIENT_CONTEXT))). + thenReturn(new ProvisioningManager.ProvisioningResult<>("created", List.of())); + + UserTO created = new UserTO(); + created.setUsername("created"); + when(binder.getUserTO("created")).thenReturn(created); + + userManager.create(op, new OIDCLoginResponse(), "default"); + + verify(templateUtils).apply(any(UserCR.class), any()); + } + + @Test + void updateResolvesUserAndReturnsUpdatedUsername() throws Exception { + OIDCC4UIProvider op = provider(); + + Item item = item("username", "email"); + when(op.getItems()).thenReturn(List.of(item)); + doReturn(field("username")).when(intAttrNameParser).parse("username", AnyTypeKind.USER); + + UserTO current = new UserTO(); + current.setKey("key"); + current.setUsername("old"); + when(userDAO.findKey("old")).thenReturn(Optional.of("key")); + when(binder.getUserTO("key")).thenReturn(current); + + UserUR userUR = new UserUR(); + userUR.setKey("key"); + when(provisioningManager.update( + any(UserUR.class), eq(false), eq("new"), eq(OIDCUserManager.OIDC_CLIENT_CONTEXT))). + thenReturn(new ProvisioningManager.ProvisioningResult<>(userUR, List.of())); + + UserTO updated = new UserTO(); + updated.setUsername("new"); + when(binder.getUserTO("key")).thenReturn(current, updated); + + OIDCLoginResponse response = new OIDCLoginResponse(); + response.getAttrs().add(new Attr.Builder("email").value("new").build()); + + assertEquals("new", userManager.update("old", op, response)); + } + + @Test + void updateMissingUser() { + when(userDAO.findKey("missing")).thenReturn(Optional.empty()); + + assertThrows(NotFoundException.class, + () -> userManager.update("missing", provider(), new OIDCLoginResponse())); + verify(provisioningManager, never()). + update(any(UserUR.class), eq(false), any(String.class), any(String.class)); + } +} diff --git a/ext/oidcc4ui/persistence-api/pom.xml b/ext/oidcc4ui/persistence-api/pom.xml index 5c2cd04ac87..22f5ab7c462 100644 --- a/ext/oidcc4ui/persistence-api/pom.xml +++ b/ext/oidcc4ui/persistence-api/pom.xml @@ -48,6 +48,18 @@ under the License. syncope-ext-oidcc4ui-common-lib ${project.version} + + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-junit-jupiter + test + diff --git a/ext/oidcc4ui/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/validation/OIDCC4UIProviderValidatorTest.java b/ext/oidcc4ui/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/validation/OIDCC4UIProviderValidatorTest.java new file mode 100644 index 00000000000..ccf11c0ce4a --- /dev/null +++ b/ext/oidcc4ui/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/validation/OIDCC4UIProviderValidatorTest.java @@ -0,0 +1,95 @@ +/* + * 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.core.persistence.api.validation; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.validation.ConstraintValidatorContext; +import java.util.List; +import org.apache.syncope.common.lib.to.Item; +import org.apache.syncope.core.persistence.api.entity.OIDCC4UIProvider; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class OIDCC4UIProviderValidatorTest { + + private ConstraintValidatorContext context; + + private OIDCC4UIProviderValidator validator; + + @BeforeEach + void setUp() { + context = mock(ConstraintValidatorContext.class, RETURNS_DEEP_STUBS); + validator = new OIDCC4UIProviderValidator(); + } + + private static Item item(final boolean connObjectKey, final boolean password) { + Item item = new Item(); + item.setConnObjectKey(connObjectKey); + item.setPassword(password); + return item; + } + + private static OIDCC4UIProvider provider(final String key, final List items) { + OIDCC4UIProvider provider = mock(OIDCC4UIProvider.class); + when(provider.getKey()).thenReturn(key); + when(provider.getItems()).thenReturn(items); + return provider; + } + + @Test + void htmlInKey() { + assertFalse(validator.isValid(provider("