From 15d4440edf0f9d81b6b7bda161a3eb4887baa482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Wed, 21 Feb 2024 13:22:10 +0100 Subject: [PATCH 01/10] [SYNCOPE-1804] Introducing syncope-core-persistence-neo4j --- .github/workflows/neo4j.yml | 55 + .../resources/ConnectorDetailsPanel.java | 4 +- .../syncope/common/lib/to/ConnInstanceTO.java | 7 +- .../ConnPoolConf.java} | 6 +- .../lib/types/IdRepoImplementationType.java | 2 +- .../src/main/resources/defaultContent.xml | 4 +- .../syncope/core/logic/ConnectorLogic.java | 2 +- .../core/logic/ReconciliationLogic.java | 12 +- .../syncope/core/logic/ResourceLogic.java | 2 +- .../core/logic/IdMLogicTestContext.java | 4 +- .../syncope/core/logic/TestInitializer.java | 9 +- .../syncope/core/logic/AbstractJobLogic.java | 2 +- .../syncope/core/logic/AnyObjectLogic.java | 2 +- .../apache/syncope/core/logic/AuditLogic.java | 9 +- .../syncope/core/logic/CommandLogic.java | 2 +- .../apache/syncope/core/logic/GroupLogic.java | 2 +- .../core/logic/IdRepoLogicContext.java | 32 +- .../apache/syncope/core/logic/RealmLogic.java | 10 +- .../syncope/core/logic/ReportLogic.java | 2 +- .../apache/syncope/core/logic/RoleLogic.java | 3 +- .../apache/syncope/core/logic/TaskLogic.java | 2 +- .../apache/syncope/core/logic/UserLogic.java | 4 +- .../core/logic/audit/JdbcAuditAppender.java | 6 +- .../syncope/core/logic/init/AuditLoader.java | 3 +- .../ClassPathScanImplementationLookup.java | 2 +- .../logic/init/IdRepoEntitlementLoader.java | 3 +- .../core/logic/IdRepoLogicTestContext.java | 4 +- .../syncope/core/logic/TestInitializer.java | 9 +- .../core/rest/cxf/IdRepoRESTCXFContext.java | 2 +- .../rest/cxf/RestServiceExceptionMapper.java | 4 +- .../rest/cxf/SyncopeOpenApiCustomizer.java | 4 +- .../cxf/ThreadLocalCleanupOutInterceptor.java | 2 +- core/persistence-api/pom.xml | 7 +- .../core/persistence/api/DomainHolder.java | 10 +- .../persistence/api/SyncopeCoreLoader.java | 4 +- .../InvalidEntityException.java | 2 +- .../InvalidPlainAttrValueException.java | 2 +- .../ParsingValidationException.java | 2 +- .../PlainAttrValidationManager.java | 2 +- .../PlainAttrValueValidator.java | 2 +- .../api/content/ContentExporter.java | 2 +- .../core/persistence/api/dao/AnyDAO.java | 8 +- .../persistence/api/dao/AnySearchDAO.java | 2 +- .../persistence/api/dao/AuditConfDAO.java | 32 - .../persistence/api/dao/AuditEntryDAO.java} | 14 +- .../persistence/api/dao/PlainSchemaDAO.java | 2 - .../core/persistence/api/dao/RoleDAO.java | 2 + .../core/persistence/api/entity/AnyUtils.java | 9 +- .../api/entity/AnyUtilsFactory.java | 60 +- .../persistence/api/entity/ConnInstance.java | 1 + .../persistence/api/entity/EntityFactory.java | 2 - .../persistence/api/entity/PlainAttr.java | 2 +- .../core/persistence/api/entity/Role.java | 5 +- .../api/entity/am/ClientAppUtils.java | 24 +- .../api/entity/am/ClientAppUtilsFactory.java | 43 +- .../api/entity/policy/PolicyUtils.java | 44 +- .../api/entity/policy/PolicyUtilsFactory.java | 73 +- .../api/utils/ConnPoolConfUtils.java | 8 +- .../api/utils/ExceptionUtils2.java | 2 +- .../persistence}/api/utils/FormatUtils.java | 2 +- .../persistence}/api/utils/RealmUtils.java | 2 +- .../core/persistence}/api/utils/URIUtils.java | 2 +- .../persistence/api/utils/AbstractTest.java | 37 + .../api/utils/FormatUtilsTest.java | 3 +- .../api/utils/RealmUtilsTest.java | 3 +- .../persistence}/api/utils/URIUtilsTest.java | 3 +- core/persistence-common/pom.xml | 66 + .../common/AbstractDomainProperties.java | 78 ++ .../common/AbstractPersistenceProperties.java | 43 + .../common/CommonPersistenceContext.java | 136 ++ .../common}/RuntimeDomainLoader.java | 19 +- .../common/attrvalue}/AbstractValidator.java | 4 +- .../attrvalue}/AlwaysTrueValidator.java | 4 +- .../common/attrvalue}/BasicValidator.java | 4 +- .../common/attrvalue}/BinaryValidator.java | 4 +- .../DefaultPlainAttrValidationManager.java | 6 +- .../attrvalue}/EmailAddressValidator.java | 4 +- .../common/attrvalue}/URLValidator.java | 4 +- .../content/AbstractContentLoaderHandler.java | 81 ++ .../content/AbstractXMLContentExporter.java | 63 + .../content/AbstractXMLContentLoader.java | 95 ++ .../content/KeymasterConfParamLoader.java | 5 +- .../common}/content/MultiParentNode.java | 6 +- .../common}/content/MultiParentNodeOp.java | 11 +- .../common/dao/AbstractAnyMatchDAO.java | 501 +++++++ .../common}/dao/AbstractAnySearchDAO.java | 55 +- .../common/entity/DefaultAnyUtils.java} | 56 +- .../common/validation}/AbstractValidator.java | 2 +- .../common/validation}/AnyCheck.java | 2 +- .../common/validation}/AnyObjectCheck.java | 2 +- .../validation}/AnyObjectValidator.java | 2 +- .../common/validation}/AnyTypeCheck.java | 2 +- .../common/validation}/AnyTypeClassCheck.java | 2 +- .../validation}/AnyTypeClassValidator.java | 2 +- .../common/validation}/AnyTypeValidator.java | 2 +- .../common/validation}/AnyValidator.java | 2 +- .../common/validation}/ApplicationCheck.java | 2 +- .../validation}/ApplicationValidator.java | 2 +- .../common/validation}/ConnInstanceCheck.java | 2 +- .../validation}/ConnInstanceValidator.java | 6 +- .../common/validation}/DelegationCheck.java | 2 +- .../validation}/DelegationValidator.java | 2 +- .../common/validation}/DynRealmCheck.java | 2 +- .../common/validation}/DynRealmValidator.java | 2 +- .../validation}/ExternalResourceCheck.java | 2 +- .../ExternalResourceValidator.java | 2 +- .../common/validation}/GroupCheck.java | 2 +- .../common/validation}/GroupValidator.java | 2 +- .../validation}/ImplementationCheck.java | 2 +- .../validation}/ImplementationValidator.java | 8 +- .../common/validation}/PlainAttrCheck.java | 4 +- .../validation/PlainAttrValidator.java} | 4 +- .../validation}/PlainAttrValueCheck.java | 2 +- .../validation}/PlainAttrValueValidator.java | 6 +- .../common/validation}/PlainSchemaCheck.java | 2 +- .../validation}/PlainSchemaValidator.java | 2 +- .../common/validation}/PolicyCheck.java | 2 +- .../common/validation}/PolicyValidator.java | 2 +- .../common/validation}/PrivilegeCheck.java | 2 +- .../validation}/PrivilegeValidator.java | 2 +- .../validation}/PropagationTaskCheck.java | 2 +- .../validation}/PropagationTaskValidator.java | 2 +- .../validation}/ProvisioningTaskCheck.java | 2 +- .../ProvisioningTaskValidator.java | 2 +- .../common/validation}/RealmCheck.java | 2 +- .../common/validation}/RealmValidator.java | 2 +- .../validation}/RelationshipTypeCheck.java | 2 +- .../RelationshipTypeValidator.java | 2 +- .../common/validation}/RemediationCheck.java | 2 +- .../validation}/RemediationValidator.java | 2 +- .../common/validation}/ReportCheck.java | 2 +- .../common/validation}/ReportValidator.java | 9 +- .../common/validation}/RoleCheck.java | 2 +- .../common/validation}/RoleValidator.java | 6 +- .../common/validation}/SRARouteCheck.java | 2 +- .../common/validation}/SRARouteValidator.java | 2 +- .../common/validation}/SchedTaskCheck.java | 2 +- .../validation}/SchedTaskValidator.java | 9 +- .../common/validation}/SchemaKeyCheck.java | 2 +- .../validation}/SchemaKeyValidator.java | 25 +- .../jpa/MyJPAJSONPersistenceContext.java | 12 +- .../jpa/OJPAJSONPersistenceContext.java | 12 +- .../jpa/PGJPAJSONPersistenceContext.java | 13 +- .../jpa/dao/AbstractJPAJSONAnyDAO.java | 46 +- ...java => AbstractJPAJSONAuditEntryDAO.java} | 6 +- .../jpa/dao/MyJPAJSONAnySearchDAO.java | 2 +- ...NImpl.java => MyJPAJSONAuditEntryDAO.java} | 15 +- .../jpa/dao/OJPAJSONAnySearchDAO.java | 2 +- ...ONImpl.java => OJPAJSONAuditEntryDAO.java} | 13 +- .../jpa/dao/PGJPAJSONAnySearchDAO.java | 37 +- ...NImpl.java => PGJPAJSONAuditEntryDAO.java} | 17 +- .../repo/AbstractPlainSchemaRepoExtJSON.java | 4 +- .../dao/repo/AnyObjectRepoExtJSONImpl.java | 3 +- .../jpa/dao/repo/GroupRepoExtJSONImpl.java | 6 +- .../jpa/dao/repo/UserRepoExtJSONImpl.java | 6 +- .../jpa/entity/JPAJSONEntityListener.java | 5 +- .../entity/anyobject/JPAJSONAnyObject.java | 2 +- .../jpa/entity/group/JPAJSONGroup.java | 2 +- .../jpa/entity/user/JPAJSONLinkedAccount.java | 2 +- .../jpa/entity/user/JPAJSONUPlainAttr.java | 5 - .../jpa/entity/user/JPAJSONUser.java | 2 +- .../JPAJSONAttributableCheck.java | 2 +- .../JPAJSONAttributableValidator.java | 20 +- .../resources/META-INF/spring-orm-myjson.xml | 2 +- .../resources/META-INF/spring-orm-ojson.xml | 2 +- .../resources/META-INF/spring-orm-pgjsonb.xml | 2 +- .../domains/jpa-json/MasterContent.xml | 4 +- .../test/resources/domains/MasterContent.xml | 6 +- core/persistence-jpa/pom.xml | 16 +- .../persistence/jpa/DomainProperties.java | 58 +- ...onfFactory.java => JPADomainRegistry.java} | 15 +- .../jpa/JPARuntimeDomainLoader.java | 47 + .../persistence/jpa/PersistenceContext.java | 168 +-- .../jpa/PersistenceProperties.java | 23 +- .../persistence/jpa/StartupDomainLoader.java | 4 +- .../jpa/content/ContentLoaderHandler.java | 90 +- .../jpa/content/XMLContentExporter.java | 52 +- .../jpa/content/XMLContentLoader.java | 95 +- .../persistence/jpa/dao/JPAAnyMatchDAO.java | 479 +------ .../persistence/jpa/dao/JPAAnySearchDAO.java | 143 +- ...RepoExtImpl.java => JPAAuditEntryDAO.java} | 36 +- .../jpa/dao/JPAEntityCacheDAO.java | 2 +- ...RealmRepoExtImpl.java => JPARealmDAO.java} | 112 +- .../persistence/jpa/dao/JPATaskExecDAO.java | 10 +- .../persistence/jpa/dao/SearchSupport.java | 4 +- .../jpa/dao/repo/AbstractAnyRepoExt.java | 58 +- .../jpa/dao/repo/AnyObjectRepoExt.java | 21 - .../jpa/dao/repo/AnyObjectRepoExtImpl.java | 2 +- .../persistence/jpa/dao/repo/AnyRepoExt.java | 6 - .../jpa/dao/repo/AuditConfRepo.java | 2 +- .../jpa/dao/repo/ConnInstanceRepoExtImpl.java | 3 +- .../dao/repo/ExternalResourceRepoExtImpl.java | 32 +- .../jpa/dao/repo/GroupRepoExt.java | 74 +- .../jpa/dao/repo/GroupRepoExtImpl.java | 186 ++- .../jpa/dao/repo/OIDCRPClientAppRepoExt.java | 2 +- .../jpa/dao/repo/PlainSchemaRepoExt.java | 2 - .../jpa/dao/repo/PlainSchemaRepoExtImpl.java | 19 +- .../jpa/dao/repo/RemediationRepoExtImpl.java | 2 +- .../jpa/dao/repo/ReportExecRepoExtImpl.java | 2 +- .../jpa/dao/repo/RoleRepoExtImpl.java | 14 +- .../jpa/dao/repo/SAML2SPClientAppRepoExt.java | 2 +- .../persistence/jpa/dao/repo/UserRepoExt.java | 15 - .../jpa/dao/repo/UserRepoExtImpl.java | 2 +- .../jpa/dao/repo/VirSchemaRepoExtImpl.java | 7 +- .../persistence/jpa/entity/AbstractAny.java | 2 +- .../jpa/entity/AbstractDynMembership.java | 1 - .../jpa/entity/AbstractPlainAttr.java | 4 +- .../jpa/entity/AbstractPlainAttrValue.java | 60 +- .../jpa/entity/AbstractSchema.java | 2 +- .../persistence/jpa/entity/JPAAnyType.java | 2 +- .../jpa/entity/JPAAnyUtilsFactory.java | 88 -- .../jpa/entity/JPAApplication.java | 2 +- .../jpa/entity/JPAConnInstance.java | 11 +- .../jpa/entity/JPAConnPoolConf.java | 135 -- .../persistence/jpa/entity/JPADelegation.java | 2 +- .../persistence/jpa/entity/JPADerSchema.java | 1 - .../persistence/jpa/entity/JPADynRealm.java | 3 +- .../jpa/entity/JPADynRealmMembership.java | 1 - .../jpa/entity/JPAEntityFactory.java | 14 +- .../jpa/entity/JPAExternalResource.java | 5 +- .../jpa/entity/JPAImplementation.java | 2 +- .../jpa/entity/JPAMailTemplate.java | 1 - .../jpa/entity/JPAPlainSchema.java | 3 +- .../persistence/jpa/entity/JPAPrivilege.java | 3 +- .../core/persistence/jpa/entity/JPARealm.java | 2 +- .../jpa/entity/JPARelationshipType.java | 2 +- .../jpa/entity/JPARemediation.java | 2 +- .../persistence/jpa/entity/JPAReport.java | 2 +- .../core/persistence/jpa/entity/JPARole.java | 42 +- .../persistence/jpa/entity/JPASRARoute.java | 14 +- .../jpa/entity/am/JPAAuthModule.java | 1 - .../jpa/entity/am/JPAClientAppUtils.java | 53 - .../entity/am/JPAClientAppUtilsFactory.java | 76 - .../jpa/entity/am/JPAWAConfigEntry.java | 5 +- .../jpa/entity/anyobject/JPAAnyObject.java | 2 +- .../jpa/entity/group/JPAGroup.java | 12 +- .../jpa/entity/policy/AbstractPolicy.java | 2 +- .../jpa/entity/policy/JPAPolicyUtils.java | 79 -- .../entity/policy/JPAPolicyUtilsFactory.java | 112 -- .../entity/task/AbstractProvisioningTask.java | 2 +- .../jpa/entity/task/JPAPropagationTask.java | 2 +- .../jpa/entity/task/JPASchedTask.java | 2 +- .../jpa/entity/user/JPALinkedAccount.java | 10 +- .../jpa/entity/user/JPASecurityQuestion.java | 1 - .../persistence/jpa/entity/user/JPAUser.java | 13 - .../EntityValidationListener.java | 9 +- .../CommonEntityManagerFactoryConf.java | 24 +- .../DomainRoutingEntityManagerFactory.java | 50 +- .../jpa/spring/SyncopeJPARepository.java | 5 +- .../resources/META-INF/spring-orm-oracle.xml | 2 +- .../META-INF/spring-orm-sqlserver.xml | 2 +- .../main/resources/META-INF/spring-orm.xml | 2 +- .../main/resources/domains/MasterContent.xml | 4 +- .../jpa/PersistenceTestContext.java | 3 +- .../core/persistence/jpa/TestInitializer.java | 13 +- .../jpa/{outer => inner}/AccessTokenTest.java | 2 +- .../persistence/jpa/inner/AnySearchTest.java | 13 +- .../persistence/jpa/inner/AnyTypeTest.java | 2 +- .../persistence/jpa/inner/DerSchemaTest.java | 4 +- .../persistence/jpa/inner/OIDCJWKSTest.java | 1 - .../persistence/jpa/inner/PlainAttrTest.java | 4 +- .../jpa/inner/PlainSchemaTest.java | 21 +- .../persistence/jpa/inner/PolicyTest.java | 67 +- .../core/persistence/jpa/inner/RealmTest.java | 2 +- .../jpa/inner/RelationshipTypeTest.java | 2 +- .../jpa/inner/RemediationTest.java | 2 +- .../persistence/jpa/inner/ResourceTest.java | 5 +- .../jpa/inner/SAML2SPEntityTest.java | 6 +- .../persistence/jpa/inner/TaskExecTest.java | 2 +- .../persistence/jpa/inner/VirSchemaTest.java | 4 +- .../persistence/jpa/outer/AnySearchTest.java | 12 +- .../persistence/jpa/outer/FIQLQueryTest.java | 3 +- .../core/persistence/jpa/outer/GroupTest.java | 4 +- .../persistence/jpa/outer/PlainAttrTest.java | 2 +- .../core/persistence/jpa/outer/RoleTest.java | 22 +- .../core/persistence/jpa/outer/TaskTest.java | 14 +- .../core/persistence/jpa/outer/UserTest.java | 2 +- .../jpa/outer/XMLContentExporterTest.java | 8 +- .../test/resources/domains/MasterContent.xml | 4 +- .../src/test/resources/domains/TwoContent.xml | 4 +- core/persistence-neo4j/pom.xml | 211 +++ .../persistence/neo4j/DomainProperties.java | 66 + .../core/persistence/neo4j/MasterDomain.java | 95 ++ .../persistence/neo4j/Neo4jDomainHolder.java | 56 + .../neo4j/Neo4jDomainRegistry.java | 142 ++ .../persistence/neo4j/PersistenceContext.java | 1236 +++++++++++++++++ .../neo4j/PersistenceProperties.java | 26 + .../neo4j/StartupDomainLoader.java | 112 ++ .../neo4j/content/ContentLoaderHandler.java | 313 +++++ .../neo4j/content/XMLContentExporter.java | 208 +++ .../neo4j/content/XMLContentLoader.java | 100 ++ .../persistence/neo4j/dao/AbstractDAO.java | 97 ++ .../neo4j/dao/Neo4jAnyMatchDAO.java | 57 + .../neo4j/dao/Neo4jAnySearchDAO.java | 963 +++++++++++++ .../neo4j/dao/Neo4jAuditEntryDAO.java | 208 +++ .../persistence/neo4j/dao/Neo4jBatchDAO.java | 102 ++ .../neo4j/dao/Neo4jEntityCacheDAO.java | 56 + .../neo4j/dao/Neo4jJobStatusDAO.java | 79 ++ .../neo4j/dao/Neo4jOIDCJWKSDAO.java | 55 + .../neo4j/dao/Neo4jPersistenceInfoDAO.java | 56 + .../persistence/neo4j/dao/Neo4jPolicyDAO.java | 245 ++++ .../persistence/neo4j/dao/Neo4jRealmDAO.java | 358 +++++ .../persistence/neo4j/dao/Neo4jTaskDAO.java | 598 ++++++++ .../neo4j/dao/Neo4jTaskExecDAO.java | 268 ++++ .../neo4j/dao/repo/AbstractAnyRepoExt.java | 402 ++++++ .../neo4j/dao/repo/AbstractClientRepoExt.java | 74 + .../neo4j/dao/repo/AbstractSchemaRepoExt.java | 90 ++ .../neo4j/dao/repo/AccessTokenRepo.java | 36 + .../neo4j/dao/repo/AnyObjectRepo.java | 37 + .../neo4j/dao/repo/AnyObjectRepoExt.java | 60 + .../neo4j/dao/repo/AnyObjectRepoExtImpl.java | 327 +++++ .../neo4j/dao/repo/AnyRepoExt.java | 100 ++ .../neo4j/dao/repo/AnyTypeClassRepo.java | 28 + .../neo4j/dao/repo/AnyTypeClassRepoExt.java} | 11 +- .../dao/repo/AnyTypeClassRepoExtImpl.java | 128 ++ .../neo4j/dao/repo/AnyTypeRepo.java | 28 + .../neo4j/dao/repo/AnyTypeRepoExt.java | 34 + .../neo4j/dao/repo/AnyTypeRepoExtImpl.java | 81 ++ .../neo4j/dao/repo/ApplicationRepo.java | 28 + .../neo4j/dao/repo/ApplicationRepoExt.java | 32 + .../dao/repo/ApplicationRepoExtImpl.java | 87 ++ .../neo4j/dao/repo/AttrRepoRepo.java | 28 + .../neo4j/dao/repo/AttrRepoRepoExt.java | 26 + .../neo4j/dao/repo/AttrRepoRepoExtImpl.java | 44 + .../neo4j/dao/repo/AuditConfRepo.java | 28 + .../neo4j/dao/repo/AuthModuleRepo.java | 28 + .../neo4j/dao/repo/AuthModuleRepoExt.java | 28 + .../neo4j/dao/repo/AuthModuleRepoExtImpl.java | 69 + .../neo4j/dao/repo/AuthProfileRepo.java} | 10 +- .../neo4j/dao/repo/CASSPClientAppRepo.java | 28 + .../neo4j/dao/repo/CASSPClientAppRepoExt.java | 24 + .../dao/repo/CASSPClientAppRepoExtImpl.java | 46 + .../neo4j/dao/repo/ClientAppRepoExt.java | 31 + .../neo4j/dao/repo/ConnInstanceRepo.java | 28 + .../neo4j/dao/repo/ConnInstanceRepoExt.java | 33 + .../dao/repo/ConnInstanceRepoExtImpl.java | 103 ++ .../neo4j/dao/repo/DelegationRepo.java | 56 + .../neo4j/dao/repo/DelegationRepoExt.java | 33 + .../neo4j/dao/repo/DelegationRepoExtImpl.java | 69 + .../neo4j/dao/repo/DerSchemaRepo.java | 28 + .../neo4j/dao/repo/DerSchemaRepoExt.java | 33 + .../neo4j/dao/repo/DerSchemaRepoExtImpl.java | 71 + .../neo4j/dao/repo/DynRealmRepo.java | 28 + .../neo4j/dao/repo/DynRealmRepoExt.java} | 25 +- .../neo4j/dao/repo/DynRealmRepoExtImpl.java | 207 +++ .../neo4j/dao/repo/ExternalResourceRepo.java | 28 + .../dao/repo/ExternalResourceRepoExt.java | 51 + .../dao/repo/ExternalResourceRepoExtImpl.java | 272 ++++ .../neo4j/dao/repo/FIQLQueryRepo.java | 28 + .../neo4j/dao/repo/FIQLQueryRepoExt.java | 29 + .../neo4j/dao/repo/FIQLQueryRepoExtImpl.java | 57 + .../persistence/neo4j/dao/repo/GroupRepo.java | 85 ++ .../neo4j/dao/repo/GroupRepoExt.java | 74 + .../neo4j/dao/repo/GroupRepoExtImpl.java | 570 ++++++++ .../neo4j/dao/repo/ImplementationRepo.java | 28 + .../neo4j/dao/repo/ImplementationRepoExt.java | 31 + .../dao/repo/ImplementationRepoExtImpl.java | 75 + .../neo4j/dao/repo/MailTemplateRepo.java | 28 + .../neo4j/dao/repo/NotificationRepo.java | 28 + .../neo4j/dao/repo/NotificationRepoExt.java | 28 + .../dao/repo/NotificationRepoExtImpl.java | 73 + .../neo4j/dao/repo/OIDCRPClientAppRepo.java | 28 + .../dao/repo/OIDCRPClientAppRepoExt.java | 26 + .../dao/repo/OIDCRPClientAppRepoExtImpl.java | 62 + .../neo4j/dao/repo/PlainSchemaRepo.java | 28 + .../neo4j/dao/repo/PlainSchemaRepoExt.java | 38 + .../dao/repo/PlainSchemaRepoExtImpl.java | 109 ++ .../neo4j/dao/repo/RelationshipTypeRepo.java | 28 + .../dao/repo/RelationshipTypeRepoExt.java | 24 + .../dao/repo/RelationshipTypeRepoExtImpl.java | 77 + .../neo4j/dao/repo/RemediationRepo.java | 28 + .../neo4j/dao/repo/RemediationRepoExt.java | 42 + .../dao/repo/RemediationRepoExtImpl.java | 157 +++ .../neo4j/dao/repo/ReportExecRepo.java | 27 + .../neo4j/dao/repo/ReportExecRepoExt.java | 46 + .../neo4j/dao/repo/ReportExecRepoExtImpl.java | 170 +++ .../neo4j/dao/repo/ReportRepo.java | 28 + .../neo4j/dao/repo/ReportRepoExt.java | 28 + .../neo4j/dao/repo/ReportRepoExtImpl.java | 45 + .../persistence/neo4j/dao/repo/RoleRepo.java | 28 + .../neo4j/dao/repo/RoleRepoExt.java} | 32 +- .../neo4j/dao/repo/RoleRepoExtImpl.java | 201 +++ .../neo4j/dao/repo/SAML2IdPEntityRepo.java | 28 + .../neo4j/dao/repo/SAML2SPClientAppRepo.java | 28 + .../dao/repo/SAML2SPClientAppRepoExt.java | 26 + .../dao/repo/SAML2SPClientAppRepoExtImpl.java | 62 + .../neo4j/dao/repo/SAML2SPEntityRepo.java | 28 + .../neo4j/dao/repo/SRARouteRepo.java | 28 + .../neo4j/dao/repo/SecurityQuestionRepo.java | 28 + .../dao/repo/SecurityQuestionRepoExt.java | 24 + .../dao/repo/SecurityQuestionRepoExtImpl.java | 49 + .../persistence/neo4j/dao/repo/UserRepo.java | 38 + .../neo4j/dao/repo/UserRepoExt.java | 75 + .../neo4j/dao/repo/UserRepoExtImpl.java | 402 ++++++ .../neo4j/dao/repo/VirSchemaRepo.java | 28 + .../neo4j/dao/repo/VirSchemaRepoExt.java | 40 + .../neo4j/dao/repo/VirSchemaRepoExtImpl.java | 101 ++ .../neo4j/dao/repo/WAConfigRepo.java | 28 + .../persistence/neo4j/entity/AbstractAny.java | 177 +++ .../neo4j/entity/AbstractAnyTemplate.java | 66 + .../neo4j/entity/AbstractDynMembership.java | 42 + .../neo4j/entity/AbstractExec.java | 104 ++ .../entity/AbstractGeneratedKeyNode.java | 38 + .../entity/AbstractGroupableRelatable.java | 114 ++ .../neo4j/entity/AbstractMembership.java | 36 + .../neo4j/entity/AbstractNode.java | 70 + .../neo4j/entity/AbstractPlainAttr.java | 127 ++ .../neo4j/entity/AbstractPlainAttrValue.java | 353 +++++ .../neo4j/entity/AbstractProvidedKeyNode.java | 40 + .../neo4j/entity/AttributableCheck.java | 40 + .../neo4j/entity/AttributableValidator.java | 46 + .../neo4j/entity/Neo4jAccessToken.java | 80 ++ .../neo4j/entity/Neo4jAnyAbout.java | 75 + .../neo4j/entity/Neo4jAnyTemplateRealm.java | 48 + .../neo4j/entity/Neo4jAnyType.java | 65 + .../neo4j/entity/Neo4jAnyTypeClass.java | 84 ++ .../neo4j/entity/Neo4jApplication.java | 70 + .../neo4j/entity/Neo4jAttributable.java | 28 + .../neo4j/entity/Neo4jAuditConf.java | 44 + .../neo4j/entity/Neo4jAuditEntry.java | 41 + .../persistence/neo4j/entity/Neo4jBatch.java | 55 + .../neo4j/entity/Neo4jConnInstance.java | 255 ++++ .../neo4j/entity/Neo4jDelegation.java | 113 ++ .../neo4j/entity/Neo4jDerSchema.java | 86 ++ .../neo4j/entity/Neo4jDynRealm.java | 61 + .../neo4j/entity/Neo4jDynRealmMembership.java | 75 + .../neo4j/entity/Neo4jEntityFactory.java | 358 +++++ .../neo4j/entity/Neo4jExternalResource.java | 392 ++++++ .../neo4j/entity/Neo4jFIQLQuery.java | 88 ++ .../neo4j/entity/Neo4jImplementation.java | 69 + .../neo4j/entity/Neo4jJobStatus.java | 45 + .../neo4j/entity/Neo4jMailTemplate.java | 54 + .../neo4j/entity/Neo4jNotification.java | 237 ++++ .../neo4j/entity/Neo4jPlainAttr.java | 32 + .../neo4j/entity/Neo4jPlainSchema.java | 202 +++ .../neo4j/entity/Neo4jPrivilege.java | 72 + .../persistence/neo4j/entity/Neo4jRealm.java | 249 ++++ .../neo4j/entity/Neo4jRelationshipType.java | 44 + .../neo4j/entity/Neo4jRemediation.java | 155 +++ .../persistence/neo4j/entity/Neo4jReport.java | 135 ++ .../neo4j/entity/Neo4jReportExec.java | 68 + .../persistence/neo4j/entity/Neo4jRole.java | 158 +++ .../neo4j/entity/Neo4jSRARoute.java | 170 +++ .../persistence/neo4j/entity/Neo4jSchema.java | 81 ++ .../neo4j/entity/Neo4jVirSchema.java | 129 ++ .../neo4j/entity/am/AbstractClientApp.java | 245 ++++ .../neo4j/entity/am/Neo4jAttrRepo.java | 133 ++ .../neo4j/entity/am/Neo4jAuthModule.java | 133 ++ .../neo4j/entity/am/Neo4jAuthProfile.java | 140 ++ .../neo4j/entity/am/Neo4jCASSPClientApp.java | 44 + .../neo4j/entity/am/Neo4jOIDCJWKS.java | 45 + .../neo4j/entity/am/Neo4jOIDCRPClientApp.java | 267 ++++ .../neo4j/entity/am/Neo4jSAML2IdPEntity.java | 94 ++ .../entity/am/Neo4jSAML2SPClientApp.java | 322 +++++ .../neo4j/entity/am/Neo4jSAML2SPEntity.java | 59 + .../neo4j/entity/am/Neo4jWAConfigEntry.java | 50 + .../anyobject/Neo4jADynGroupMembership.java | 65 + .../entity/anyobject/Neo4jAMembership.java | 97 ++ .../entity/anyobject/Neo4jAPlainAttr.java | 158 +++ .../anyobject/Neo4jAPlainAttrUniqueValue.java | 58 + .../anyobject/Neo4jAPlainAttrValue.java | 46 + .../entity/anyobject/Neo4jARelationship.java | 87 ++ .../entity/anyobject/Neo4jAnyObject.java | 183 +++ .../neo4j/entity/group/Neo4jGPlainAttr.java | 122 ++ .../group/Neo4jGPlainAttrUniqueValue.java | 58 + .../entity/group/Neo4jGPlainAttrValue.java | 46 + .../neo4j/entity/group/Neo4jGroup.java | 236 ++++ .../entity/group/Neo4jTypeExtension.java | 81 ++ .../policy/AbstractCorrelationRuleEntity.java | 63 + .../entity/policy/Neo4jAccessPolicy.java | 45 + .../entity/policy/Neo4jAccountPolicy.java | 100 ++ .../entity/policy/Neo4jAttrReleasePolicy.java | 70 + .../neo4j/entity/policy/Neo4jAuthPolicy.java | 47 + .../entity/policy/Neo4jPasswordPolicy.java | 79 ++ .../neo4j/entity/policy/Neo4jPolicy.java | 47 + .../entity/policy/Neo4jPropagationPolicy.java | 99 ++ .../Neo4jPullCorrelationRuleEntity.java | 52 + .../neo4j/entity/policy/Neo4jPullPolicy.java | 70 + .../Neo4jPushCorrelationRuleEntity.java | 52 + .../neo4j/entity/policy/Neo4jPushPolicy.java | 70 + .../policy/Neo4jTicketExpirationPolicy.java | 47 + .../neo4j/entity/task/AbstractTask.java | 49 + .../neo4j/entity/task/AbstractTaskExec.java | 41 + .../entity/task/Neo4jAnyTemplatePullTask.java | 49 + .../neo4j/entity/task/Neo4jMacroTask.java | 159 +++ .../neo4j/entity/task/Neo4jMacroTaskExec.java | 49 + .../entity/task/Neo4jNotificationTask.java | 217 +++ .../task/Neo4jNotificationTaskExec.java | 50 + .../entity/task/Neo4jPropagationTask.java | 201 +++ .../entity/task/Neo4jPropagationTaskExec.java | 48 + .../entity/task/Neo4jProvisioningTask.java | 157 +++ .../neo4j/entity/task/Neo4jPullTask.java | 160 +++ .../neo4j/entity/task/Neo4jPullTaskExec.java | 47 + .../neo4j/entity/task/Neo4jPushTask.java | 141 ++ .../neo4j/entity/task/Neo4jPushTaskExec.java | 49 + .../neo4j/entity/task/Neo4jSchedTask.java | 141 ++ .../neo4j/entity/task/Neo4jSchedTaskExec.java | 48 + .../neo4j/entity/task/Neo4jTaskUtils.java | 352 +++++ .../entity/task/Neo4jTaskUtilsFactory.java | 109 ++ .../neo4j/entity/user/Neo4jLAPlainAttr.java | 108 ++ .../user/Neo4jLAPlainAttrUniqueValue.java | 60 + .../entity/user/Neo4jLAPlainAttrValue.java | 46 + .../neo4j/entity/user/Neo4jLinkedAccount.java | 233 ++++ .../entity/user/Neo4jSecurityQuestion.java | 43 + .../user/Neo4jUDynGroupMembership.java} | 35 +- .../neo4j/entity/user/Neo4jUMembership.java | 97 ++ .../neo4j/entity/user/Neo4jUPlainAttr.java | 158 +++ .../user/Neo4jUPlainAttrUniqueValue.java | 58 + .../entity/user/Neo4jUPlainAttrValue.java | 46 + .../neo4j/entity/user/Neo4jURelationship.java | 89 ++ .../neo4j/entity/user/Neo4jUser.java | 461 ++++++ .../spring/DomainRoutingNeo4jClient.java | 75 + .../DomainRoutingNeo4jTransactionManager.java | 62 + .../neo4j/spring/NodeValidator.java | 73 + .../neo4j/spring/PlainsAttrsConverter.java | 64 + .../support/SyncopeNeo4jRepository.java | 39 + .../SyncopeNeo4jRepositoryFactory.java | 76 + ...ot.autoconfigure.AutoConfiguration.imports | 18 + .../main/resources/domains/MasterContent.xml | 108 ++ .../domains/MasterKeymasterConfParams.json | 17 + .../src/main/resources/indexes.xml | 56 + .../core/persistence/neo4j/AbstractTest.java | 37 + .../persistence/neo4j/DummyConfParamOps.java | 43 + .../neo4j/DummyConnectorManager.java | 72 + .../persistence/neo4j/DummyDomainOps.java | 64 + .../neo4j/DummyImplementationLookup.java | 80 ++ .../neo4j/PersistenceTestContext.java | 144 ++ .../persistence/neo4j/TestInitializer.java | 66 + .../neo4j/inner/AbstractClientAppTest.java | 94 ++ .../neo4j/inner/AccessTokenTest.java | 132 ++ .../persistence/neo4j/inner/AnyMatchTest.java | 125 ++ .../neo4j/inner/AnyObjectTest.java | 134 ++ .../neo4j/inner/AnySearchTest.java | 902 ++++++++++++ .../neo4j/inner/AnyTypeClassTest.java | 89 ++ .../persistence/neo4j/inner/AnyTypeTest.java | 118 ++ .../neo4j/inner/ApplicationTest.java | 110 ++ .../persistence/neo4j/inner/AttrRepoTest.java | 227 +++ .../neo4j/inner/AuthModuleTest.java | 399 ++++++ .../neo4j/inner/AuthProfileTest.java | 198 +++ .../persistence/neo4j/inner/CASSPTest.java | 73 + .../neo4j/inner/ConnInstanceTest.java | 166 +++ .../neo4j/inner/DerSchemaTest.java | 98 ++ .../persistence/neo4j/inner/GroupTest.java | 88 ++ .../neo4j/inner/ImplementationTest.java | 96 ++ .../neo4j/inner/MailTemplateTest.java | 77 + .../neo4j/inner/MultitenancyTest.java | 87 ++ .../neo4j/inner/NotificationTest.java | 149 ++ .../persistence/neo4j/inner/OIDCJWKSTest.java | 56 + .../persistence/neo4j/inner/OIDCRPTest.java | 82 ++ .../neo4j/inner/PlainAttrTest.java | 247 ++++ .../neo4j/inner/PlainSchemaTest.java | 170 +++ .../persistence/neo4j/inner/PolicyTest.java | 388 ++++++ .../persistence/neo4j/inner/RealmTest.java | 184 +++ .../neo4j/inner/RelationshipTypeTest.java | 102 ++ .../neo4j/inner/RemediationTest.java | 131 ++ .../persistence/neo4j/inner/ReportTest.java | 88 ++ .../persistence/neo4j/inner/ResourceTest.java | 334 +++++ .../persistence/neo4j/inner/RoleTest.java | 83 ++ .../neo4j/inner/SAML2IdPEntityTest.java | 81 ++ .../neo4j/inner/SAML2SPEntityTest.java | 158 +++ .../persistence/neo4j/inner/SAML2SPTest.java | 93 ++ .../persistence/neo4j/inner/SRARouteTest.java | 91 ++ .../neo4j/inner/SecurityQuestionTest.java | 70 + .../persistence/neo4j/inner/TaskExecTest.java | 99 ++ .../persistence/neo4j/inner/TaskTest.java | 169 +++ .../persistence/neo4j/inner/UserTest.java | 285 ++++ .../neo4j/inner/VirSchemaTest.java | 113 ++ .../persistence/neo4j/inner/WAConfigTest.java | 101 ++ .../neo4j/outer/AnySearchTest.java | 253 ++++ .../neo4j/outer/AnyTypeClassTest.java | 82 ++ .../persistence/neo4j/outer/AnyTypeTest.java | 89 ++ .../neo4j/outer/ConnInstanceTest.java | 92 ++ .../neo4j/outer/DelegationTest.java | 66 + .../persistence/neo4j/outer/DynRealmTest.java | 96 ++ .../neo4j/outer/FIQLQueryTest.java | 60 + .../persistence/neo4j/outer/GroupTest.java | 383 +++++ .../neo4j/outer/PlainSchemaTest.java | 193 +++ .../persistence/neo4j/outer/PolicyTest.java | 126 ++ .../persistence/neo4j/outer/RealmTest.java | 66 + .../persistence/neo4j/outer/ReportTest.java | 118 ++ .../persistence/neo4j/outer/ResourceTest.java | 278 ++++ .../persistence/neo4j/outer/RoleTest.java | 183 +++ .../neo4j/outer/SecurityQuestionTest.java | 57 + .../persistence/neo4j/outer/TaskTest.java | 327 +++++ .../persistence/neo4j/outer/UserTest.java | 275 ++++ .../neo4j/outer/VirSchemaTest.java | 81 ++ .../neo4j/outer/XMLContentExporterTest.java | 68 + .../src/test/resources/core-test.properties | 35 + .../test/resources/domains/MasterContent.xml | 1105 +++++++++++++++ .../domains/MasterKeymasterConfParams.json | 17 + .../src/test/resources/domains/TwoContent.xml | 122 ++ .../domains/TwoKeymasterConfParams.json | 15 + .../test/resources/domains/TwoSecurity.json | 4 + .../src/test/resources/idp-metadata.xml | 144 ++ .../test/resources/simplelogger.properties | 21 + .../src/test/resources/sp-metadata.xml | 43 + core/pom.xml | 2 + .../provisioning/api/IntAttrNameParser.java | 16 +- .../core/provisioning/api/jexl/JexlUtils.java | 2 +- .../api/IntAttrNameParserTest.java | 14 +- .../api/{utils => jexl}/JexlUtilsTest.java | 3 +- .../api/utils/ConnPoolConfUtilsTest.java | 57 - .../java/ConnectorFacadeProxy.java | 2 +- .../provisioning/java/ConnectorLoader.java | 3 +- .../java/DefaultAuditManager.java | 2 +- .../java/DefaultConnIdBundleManager.java | 2 +- .../java/DefaultConnectorManager.java | 5 +- .../java/DefaultMappingManager.java | 2 +- .../java/ProvisioningContext.java | 6 +- .../java/data/AbstractAnyDataBinder.java | 4 +- .../data/AbstractExecutableDatabinder.java | 2 +- .../java/data/AnyObjectDataBinderImpl.java | 2 +- .../java/data/AnyTypeClassDataBinderImpl.java | 12 +- .../java/data/ConnInstanceDataBinderImpl.java | 26 +- .../java/data/GroupDataBinderImpl.java | 2 +- .../java/data/RoleDataBinderImpl.java | 63 +- .../java/data/UserDataBinderImpl.java | 2 +- .../job/AbstractSchedTaskJobDelegate.java | 2 +- .../java/job/DefaultJobManager.java | 42 +- .../core/provisioning/java/job/TaskJob.java | 2 +- .../AbstractNotificationJobDelegate.java | 2 +- .../job/notification/NotificationJob.java | 4 +- .../job/report/AbstractReportJobDelegate.java | 2 +- .../java/job/report/ReportJob.java | 2 +- .../AbstractPropagationTaskExecutor.java | 4 +- .../PriorityPropagationTaskExecutor.java | 2 +- .../DefaultRealmPullResultHandler.java | 10 +- .../java/pushpull/InboundMatcher.java | 2 +- .../java/pushpull/PullJobDelegate.java | 2 +- .../java/pushpull/PushJobDelegate.java | 2 +- .../stream/StreamPullJobDelegate.java | 4 +- .../java/DefaultMappingManagerTest.java | 2 +- .../java/ProvisioningTestContext.java | 4 +- .../provisioning/java/TestInitializer.java | 9 +- .../java/data/UserDataBinderTest.java | 2 +- .../spring/security/AuthDataAccessor.java | 13 +- .../security/SyncopeGrantedAuthority.java | 2 +- .../core/starter/SyncopeCoreApplication.java | 6 +- .../core/starter/SyncopeCoreStart.java | 8 +- .../syncope/core/starter/SyncopeCoreStop.java | 4 +- .../actuate/DomainsHealthIndicator.java | 28 +- .../java/AbstractUserWorkflowAdapter.java | 2 +- .../java/DefaultUserWorkflowAdapterTest.java | 2 +- .../core/workflow/java/TestInitializer.java | 9 +- .../workflow/java/WorkflowTestContext.java | 4 +- .../client/ElasticsearchIndexLoader.java | 3 +- .../audit/ElasticsearchLogicContext.java | 4 +- .../{persistence-jpa => persistence}/pom.xml | 14 +- .../jpa/ElasticsearchPersistenceContext.java | 53 +- .../jpa/dao/ElasticsearchAnySearchDAO.java | 13 +- .../jpa/dao/ElasticsearchAuditEntryDAO.java} | 13 +- .../jpa/dao/ElasticsearchRealmDAO.java} | 111 +- ...ot.autoconfigure.AutoConfiguration.imports | 0 .../dao/ElasticsearchAnySearchDAOTest.java | 8 +- ext/elasticsearch/pom.xml | 2 +- .../flowable/impl/FlowableRuntimeUtils.java | 16 +- .../DomainProcessEngineFactoryBean.java | 13 +- .../core/logic/init/FlowableLoader.java | 3 +- ext/oidcc4ui/persistence-api/pom.xml | 2 +- .../validation}/OIDCC4UIProviderCheck.java | 2 +- .../OIDCC4UIProviderValidator.java | 3 +- .../jpa/entity/JPAOIDCC4UIProvider.java | 2 +- .../client/OpenSearchIndexLoader.java | 3 +- .../logic/audit/OpenSearchLogicContext.java | 4 +- .../{persistence-jpa => persistence}/pom.xml | 16 +- .../jpa/OpenSearchPersistenceContext.java | 53 +- .../jpa/dao/OpenSearchAnySearchDAO.java | 13 +- .../jpa/dao/OpenSearchAuditEntryDAO.java} | 13 +- .../jpa/dao/OpenSearchRealmDAO.java} | 108 +- .../main/resources/META-INF/spring.factories | 0 .../jpa/dao/OpenSearchAnySearchDAOTest.java | 8 +- ext/opensearch/pom.xml | 2 +- ext/saml2sp4ui/persistence-api/pom.xml | 2 +- .../api/validation}/SAML2SP4UIIdPCheck.java | 2 +- .../validation}/SAML2SP4UIIdPValidator.java | 3 +- .../jpa/entity/JPASAML2SP4UIIdP.java | 2 +- .../ext/scimv2/cxf/SCIMExceptionMapper.java | 4 +- fit/core-reference/pom.xml | 4 +- .../core/reference/CoreReferenceContext.java | 4 +- .../reference/ITImplementationLookup.java | 24 +- .../apache/syncope/fit/core/AuditITCase.java | 4 +- .../syncope/fit/core/ConnectorITCase.java | 4 +- pom.xml | 13 + .../concepts/typemanagement.adoc | 4 +- .../reference-guide/usage/customization.adoc | 4 +- 685 files changed, 39398 insertions(+), 3173 deletions(-) create mode 100644 .github/workflows/neo4j.yml rename common/idm/lib/src/main/java/org/apache/syncope/common/lib/{to/ConnPoolConfTO.java => types/ConnPoolConf.java} (95%) rename core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/{validation => }/InvalidEntityException.java (98%) rename core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/{validation => }/InvalidPlainAttrValueException.java (95%) rename core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/{validation => }/ParsingValidationException.java (94%) rename core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/{validation => }/PlainAttrValidationManager.java (93%) rename core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/{validation => }/PlainAttrValueValidator.java (93%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepoExt.java => persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuditEntryDAO.java} (86%) rename core/{provisioning-api/src/main/java/org/apache/syncope/core/provisioning => persistence-api/src/main/java/org/apache/syncope/core/persistence}/api/utils/ConnPoolConfUtils.java (92%) rename core/{provisioning-api/src/main/java/org/apache/syncope/core/provisioning => persistence-api/src/main/java/org/apache/syncope/core/persistence}/api/utils/ExceptionUtils2.java (96%) rename core/{provisioning-api/src/main/java/org/apache/syncope/core/provisioning => persistence-api/src/main/java/org/apache/syncope/core/persistence}/api/utils/FormatUtils.java (98%) rename core/{provisioning-api/src/main/java/org/apache/syncope/core/provisioning => persistence-api/src/main/java/org/apache/syncope/core/persistence}/api/utils/RealmUtils.java (98%) rename core/{provisioning-api/src/main/java/org/apache/syncope/core/provisioning => persistence-api/src/main/java/org/apache/syncope/core/persistence}/api/utils/URIUtils.java (97%) create mode 100644 core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/AbstractTest.java rename core/{provisioning-api/src/test/java/org/apache/syncope/core/provisioning => persistence-api/src/test/java/org/apache/syncope/core/persistence}/api/utils/FormatUtilsTest.java (97%) rename core/{provisioning-api/src/test/java/org/apache/syncope/core/provisioning => persistence-api/src/test/java/org/apache/syncope/core/persistence}/api/utils/RealmUtilsTest.java (95%) rename core/{provisioning-api/src/test/java/org/apache/syncope/core/provisioning => persistence-api/src/test/java/org/apache/syncope/core/persistence}/api/utils/URIUtilsTest.java (93%) create mode 100644 core/persistence-common/pom.xml create mode 100644 core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/AbstractDomainProperties.java create mode 100644 core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/AbstractPersistenceProperties.java create mode 100644 core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/CommonPersistenceContext.java rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa => persistence-common/src/main/java/org/apache/syncope/core/persistence/common}/RuntimeDomainLoader.java (87%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue}/AbstractValidator.java (90%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue}/AlwaysTrueValidator.java (88%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue}/BasicValidator.java (92%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue}/BinaryValidator.java (94%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue}/DefaultPlainAttrValidationManager.java (90%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue}/EmailAddressValidator.java (90%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue}/URLValidator.java (89%) create mode 100644 core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/AbstractContentLoaderHandler.java create mode 100644 core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/AbstractXMLContentExporter.java create mode 100644 core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/AbstractXMLContentLoader.java rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa => persistence-common/src/main/java/org/apache/syncope/core/persistence/common}/content/KeymasterConfParamLoader.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa => persistence-common/src/main/java/org/apache/syncope/core/persistence/common}/content/MultiParentNode.java (96%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa => persistence-common/src/main/java/org/apache/syncope/core/persistence/common}/content/MultiParentNodeOp.java (90%) create mode 100644 core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnyMatchDAO.java rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa => persistence-common/src/main/java/org/apache/syncope/core/persistence/common}/dao/AbstractAnySearchDAO.java (92%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtils.java => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/entity/DefaultAnyUtils.java} (89%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/AbstractValidator.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/AnyCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/AnyObjectCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/AnyObjectValidator.java (96%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/AnyTypeCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/AnyTypeClassCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/AnyTypeClassValidator.java (96%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/AnyTypeValidator.java (97%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/AnyValidator.java (98%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/ApplicationCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/ApplicationValidator.java (96%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/ConnInstanceCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/ConnInstanceValidator.java (93%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/DelegationCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/DelegationValidator.java (97%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/DynRealmCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/DynRealmValidator.java (96%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/ExternalResourceCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/ExternalResourceValidator.java (98%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/GroupCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/GroupValidator.java (98%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/ImplementationCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/ImplementationValidator.java (82%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/PlainAttrCheck.java (91%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/JPAPlainAttrValidator.java => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainAttrValidator.java} (93%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/PlainAttrValueCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/PlainAttrValueValidator.java (93%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/PlainSchemaCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/PlainSchemaValidator.java (97%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/PolicyCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/PolicyValidator.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/PrivilegeCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/PrivilegeValidator.java (96%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/PropagationTaskCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/PropagationTaskValidator.java (97%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/ProvisioningTaskCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/ProvisioningTaskValidator.java (96%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/RealmCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/RealmValidator.java (97%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/RelationshipTypeCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/RelationshipTypeValidator.java (96%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/RemediationCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/RemediationValidator.java (98%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/ReportCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/ReportValidator.java (89%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/RoleCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/RoleValidator.java (88%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/SRARouteCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/SRARouteValidator.java (97%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/SchedTaskCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/SchedTaskValidator.java (88%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/SchemaKeyCheck.java (95%) rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation}/SchemaKeyValidator.java (70%) rename core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/{repo/AbstractAuditConfRepoExtJSON.java => AbstractJPAJSONAuditEntryDAO.java} (95%) rename core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/{repo/AuditConfRepoExtMyJSONImpl.java => MyJPAJSONAuditEntryDAO.java} (81%) rename core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/{repo/AuditConfRepoExtOJSONImpl.java => OJPAJSONAuditEntryDAO.java} (85%) rename core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/{repo/AuditConfRepoExtPGJSONImpl.java => PGJPAJSONAuditEntryDAO.java} (77%) rename core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/validation/{entity => }/JPAJSONAttributableCheck.java (95%) rename core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/validation/{entity => }/JPAJSONAttributableValidator.java (69%) rename core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/{DomainConfFactory.java => JPADomainRegistry.java} (94%) create mode 100644 core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPARuntimeDomainLoader.java rename core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/{repo/AuditConfRepoExtImpl.java => JPAAuditEntryDAO.java} (86%) rename core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/{repo/RealmRepoExtImpl.java => JPARealmDAO.java} (78%) delete mode 100644 core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtilsFactory.java delete mode 100644 core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnPoolConf.java delete mode 100644 core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAClientAppUtils.java delete mode 100644 core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAClientAppUtilsFactory.java delete mode 100644 core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtils.java delete mode 100644 core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtilsFactory.java rename core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/{validation/entity => openjpa}/EntityValidationListener.java (87%) rename core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/{outer => inner}/AccessTokenTest.java (98%) create mode 100644 core/persistence-neo4j/pom.xml create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/DomainProperties.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/MasterDomain.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainHolder.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainRegistry.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceProperties.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/StartupDomainLoader.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/ContentLoaderHandler.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/XMLContentExporter.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/XMLContentLoader.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/AbstractDAO.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnyMatchDAO.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnySearchDAO.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAuditEntryDAO.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jBatchDAO.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jEntityCacheDAO.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jJobStatusDAO.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jOIDCJWKSDAO.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jPersistenceInfoDAO.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jPolicyDAO.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmDAO.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskDAO.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskExecDAO.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractAnyRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractClientRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractSchemaRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AccessTokenRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeClassRepo.java rename core/{persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/DynRoleMembership.java => persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeClassRepoExt.java} (72%) create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeClassRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ApplicationRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ApplicationRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ApplicationRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AttrRepoRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AttrRepoRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AttrRepoRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuditConfRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthModuleRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthModuleRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthModuleRepoExtImpl.java rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepo.java => persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepo.java} (73%) create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/CASSPClientAppRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/CASSPClientAppRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/CASSPClientAppRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ClientAppRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DelegationRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DelegationRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DelegationRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DerSchemaRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DerSchemaRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DerSchemaRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DynRealmRepo.java rename core/{persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnPoolConf.java => persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DynRealmRepoExt.java} (63%) create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DynRealmRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/FIQLQueryRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/FIQLQueryRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/FIQLQueryRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ImplementationRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ImplementationRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ImplementationRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/MailTemplateRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NotificationRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NotificationRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NotificationRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/OIDCRPClientAppRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/OIDCRPClientAppRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/OIDCRPClientAppRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/PlainSchemaRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/PlainSchemaRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/PlainSchemaRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RelationshipTypeRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RelationshipTypeRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RelationshipTypeRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RemediationRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RemediationRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RemediationRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportExecRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportExecRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportExecRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RoleRepo.java rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepoExt.java => persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RoleRepoExt.java} (54%) create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RoleRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2IdPEntityRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2SPClientAppRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2SPClientAppRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2SPClientAppRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2SPEntityRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SRARouteRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SecurityQuestionRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SecurityQuestionRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SecurityQuestionRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/VirSchemaRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/VirSchemaRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/VirSchemaRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/WAConfigRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractAny.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractAnyTemplate.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractDynMembership.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractExec.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractGeneratedKeyNode.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractGroupableRelatable.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractMembership.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractNode.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractPlainAttr.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractPlainAttrValue.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractProvidedKeyNode.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AttributableCheck.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AttributableValidator.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAccessToken.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAnyAbout.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAnyTemplateRealm.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAnyType.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAnyTypeClass.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jApplication.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAttributable.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAuditConf.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAuditEntry.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jBatch.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jConnInstance.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDelegation.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDerSchema.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDynRealm.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDynRealmMembership.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jExternalResource.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jFIQLQuery.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jImplementation.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jJobStatus.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jMailTemplate.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jNotification.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPlainAttr.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPlainSchema.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPrivilege.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRealm.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRelationshipType.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRemediation.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jReport.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jReportExec.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRole.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jSRARoute.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jSchema.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jVirSchema.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/AbstractClientApp.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAttrRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAuthModule.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAuthProfile.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jCASSPClientApp.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCJWKS.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jSAML2IdPEntity.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jSAML2SPClientApp.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jSAML2SPEntity.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jWAConfigEntry.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jADynGroupMembership.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAMembership.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAPlainAttr.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAPlainAttrUniqueValue.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAPlainAttrValue.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jARelationship.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAnyObject.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGPlainAttr.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGPlainAttrUniqueValue.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGPlainAttrValue.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGroup.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jTypeExtension.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/AbstractCorrelationRuleEntity.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAccessPolicy.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAccountPolicy.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAttrReleasePolicy.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAuthPolicy.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPasswordPolicy.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPolicy.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPropagationPolicy.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPullCorrelationRuleEntity.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPullPolicy.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPushCorrelationRuleEntity.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPushPolicy.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jTicketExpirationPolicy.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/AbstractTask.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/AbstractTaskExec.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jAnyTemplatePullTask.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTaskExec.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jNotificationTask.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jNotificationTaskExec.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPropagationTask.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPropagationTaskExec.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jProvisioningTask.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPullTask.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPullTaskExec.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPushTask.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPushTaskExec.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jSchedTask.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jSchedTaskExec.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jTaskUtils.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jTaskUtilsFactory.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jLAPlainAttr.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jLAPlainAttrUniqueValue.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jLAPlainAttrValue.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jLinkedAccount.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jSecurityQuestion.java rename core/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPADynRoleMembership.java => persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUDynGroupMembership.java} (53%) create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUMembership.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUPlainAttr.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUPlainAttrUniqueValue.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUPlainAttrValue.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jURelationship.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUser.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/DomainRoutingNeo4jClient.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/DomainRoutingNeo4jTransactionManager.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/NodeValidator.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/PlainsAttrsConverter.java create mode 100644 core/persistence-neo4j/src/main/java/org/springframework/data/neo4j/repository/support/SyncopeNeo4jRepository.java create mode 100644 core/persistence-neo4j/src/main/java/org/springframework/data/neo4j/repository/support/SyncopeNeo4jRepositoryFactory.java create mode 100644 core/persistence-neo4j/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 core/persistence-neo4j/src/main/resources/domains/MasterContent.xml create mode 100644 core/persistence-neo4j/src/main/resources/domains/MasterKeymasterConfParams.json create mode 100644 core/persistence-neo4j/src/main/resources/indexes.xml create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/AbstractTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyConfParamOps.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyConnectorManager.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyDomainOps.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyImplementationLookup.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/PersistenceTestContext.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/TestInitializer.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AbstractClientAppTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AccessTokenTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyMatchTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyObjectTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnySearchTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyTypeClassTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyTypeTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ApplicationTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AttrRepoTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthModuleTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthProfileTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/CASSPTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ConnInstanceTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/DerSchemaTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/GroupTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ImplementationTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/MailTemplateTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/MultitenancyTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/NotificationTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/OIDCJWKSTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/OIDCRPTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PlainAttrTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PlainSchemaTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PolicyTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RealmTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RelationshipTypeTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RemediationTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ReportTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ResourceTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RoleTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2IdPEntityTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2SPEntityTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2SPTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SRARouteTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SecurityQuestionTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/TaskExecTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/TaskTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/UserTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/VirSchemaTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/WAConfigTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnySearchTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnyTypeClassTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnyTypeTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ConnInstanceTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/DelegationTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/DynRealmTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/FIQLQueryTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/GroupTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/PlainSchemaTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/PolicyTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/RealmTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ReportTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ResourceTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/RoleTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/SecurityQuestionTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/TaskTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/UserTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/VirSchemaTest.java create mode 100644 core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/XMLContentExporterTest.java create mode 100644 core/persistence-neo4j/src/test/resources/core-test.properties create mode 100644 core/persistence-neo4j/src/test/resources/domains/MasterContent.xml create mode 100644 core/persistence-neo4j/src/test/resources/domains/MasterKeymasterConfParams.json create mode 100644 core/persistence-neo4j/src/test/resources/domains/TwoContent.xml create mode 100644 core/persistence-neo4j/src/test/resources/domains/TwoKeymasterConfParams.json create mode 100644 core/persistence-neo4j/src/test/resources/domains/TwoSecurity.json create mode 100644 core/persistence-neo4j/src/test/resources/idp-metadata.xml create mode 100644 core/persistence-neo4j/src/test/resources/simplelogger.properties create mode 100644 core/persistence-neo4j/src/test/resources/sp-metadata.xml rename core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/{utils => jexl}/JexlUtilsTest.java (98%) delete mode 100644 core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/ConnPoolConfUtilsTest.java rename ext/elasticsearch/{persistence-jpa => persistence}/pom.xml (86%) rename ext/elasticsearch/{persistence-jpa => persistence}/src/main/java/org/apache/syncope/core/persistence/jpa/ElasticsearchPersistenceContext.java (60%) rename ext/elasticsearch/{persistence-jpa => persistence}/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java (98%) rename ext/elasticsearch/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepoExtElasticsearchImpl.java => persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAuditEntryDAO.java} (96%) rename ext/elasticsearch/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepoExtElasticsearchImpl.java => persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchRealmDAO.java} (77%) rename ext/elasticsearch/{persistence-jpa => persistence}/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (100%) rename ext/elasticsearch/{persistence-jpa => persistence}/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java (98%) rename ext/oidcc4ui/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-api/src/main/java/org/apache/syncope/core/persistence/api/validation}/OIDCC4UIProviderCheck.java (95%) rename ext/oidcc4ui/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-api/src/main/java/org/apache/syncope/core/persistence/api/validation}/OIDCC4UIProviderValidator.java (95%) rename ext/opensearch/{persistence-jpa => persistence}/pom.xml (86%) rename ext/opensearch/{persistence-jpa => persistence}/src/main/java/org/apache/syncope/core/persistence/jpa/OpenSearchPersistenceContext.java (60%) rename ext/opensearch/{persistence-jpa => persistence}/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAO.java (98%) rename ext/opensearch/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepoExtOpenSearchImpl.java => persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAuditEntryDAO.java} (96%) rename ext/opensearch/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepoExtOpenSearchImpl.java => persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchRealmDAO.java} (77%) rename ext/opensearch/{persistence-jpa => persistence}/src/main/resources/META-INF/spring.factories (100%) rename ext/opensearch/{persistence-jpa => persistence}/src/test/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAOTest.java (98%) rename ext/saml2sp4ui/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-api/src/main/java/org/apache/syncope/core/persistence/api/validation}/SAML2SP4UIIdPCheck.java (95%) rename ext/saml2sp4ui/{persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity => persistence-api/src/main/java/org/apache/syncope/core/persistence/api/validation}/SAML2SP4UIIdPValidator.java (95%) diff --git a/.github/workflows/neo4j.yml b/.github/workflows/neo4j.yml new file mode 100644 index 0000000000..2c2de22079 --- /dev/null +++ b/.github/workflows/neo4j.yml @@ -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 +name: "Neo4j" + +on: + push: + branches: ['master', 'pr-*'] + pull_request: + # The branches below must be a subset of the branches above + branches: [master] + schedule: + - cron: '0 13 * * 4' + +jobs: + neo4j: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup Java JDK + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: 21 + - name: Setup Maven + uses: stCarolas/setup-maven@v4.5 + with: + maven-version: 3.9.4 + - uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: Build + run: mvn -U -T 1C -P 'skipTests,all' + - name: 'Unit Tests: Neo4j' + run: mvn -f core/persistence-neo4j/pom.xml -P neo4j -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.phase=none -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true + #- name: 'Integration Tests: Neo4j' + # run: mvn -f fit/core-reference/pom.xml -P neo4j-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java index 8feec773a7..f7c901a367 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java @@ -34,8 +34,8 @@ import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel; import org.apache.syncope.common.lib.to.ConnIdBundle; import org.apache.syncope.common.lib.to.ConnInstanceTO; -import org.apache.syncope.common.lib.to.ConnPoolConfTO; import org.apache.syncope.common.lib.to.RealmTO; +import org.apache.syncope.common.lib.types.ConnPoolConf; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AutoCompleteSettings; import org.apache.wicket.extensions.wizard.WizardStep; @@ -177,7 +177,7 @@ protected void onUpdate(final AjaxRequestTarget target) { }); if (connInstanceTO.getPoolConf() == null) { - connInstanceTO.setPoolConf(new ConnPoolConfTO()); + connInstanceTO.setPoolConf(new ConnPoolConf()); } add(new AjaxSpinnerFieldPanel.Builder().min(0).max(Integer.MAX_VALUE).build( diff --git a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java index bddf9b346c..57451afad6 100644 --- a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java +++ b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ConnInstanceTO.java @@ -31,6 +31,7 @@ import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.syncope.common.lib.types.ConnConfProperty; +import org.apache.syncope.common.lib.types.ConnPoolConf; import org.apache.syncope.common.lib.types.ConnectorCapability; public class ConnInstanceTO implements EntityTO { @@ -59,7 +60,7 @@ public class ConnInstanceTO implements EntityTO { private Integer connRequestTimeout; - private ConnPoolConfTO poolConf; + private ConnPoolConf poolConf; @Override public String getKey() { @@ -166,11 +167,11 @@ public void setConnRequestTimeout(final Integer connRequestTimeout) { this.connRequestTimeout = connRequestTimeout; } - public ConnPoolConfTO getPoolConf() { + public ConnPoolConf getPoolConf() { return poolConf; } - public void setPoolConf(final ConnPoolConfTO poolConf) { + public void setPoolConf(final ConnPoolConf poolConf) { this.poolConf = poolConf; } diff --git a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ConnPoolConfTO.java b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/ConnPoolConf.java similarity index 95% rename from common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ConnPoolConfTO.java rename to common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/ConnPoolConf.java index 3ac82319ee..21858dddfb 100644 --- a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ConnPoolConfTO.java +++ b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/ConnPoolConf.java @@ -16,13 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.common.lib.to; +package org.apache.syncope.common.lib.types; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.syncope.common.lib.BaseBean; -public class ConnPoolConfTO implements BaseBean { +public class ConnPoolConf implements BaseBean { private static final long serialVersionUID = -214360178113476623L; @@ -87,7 +87,7 @@ public boolean equals(final Object obj) { if (getClass() != obj.getClass()) { return false; } - ConnPoolConfTO other = (ConnPoolConfTO) obj; + ConnPoolConf other = (ConnPoolConf) obj; return new EqualsBuilder(). append(maxObjects, other.maxObjects). append(minIdle, other.minIdle). diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoImplementationType.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoImplementationType.java index f21c995a0b..64870f1603 100644 --- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoImplementationType.java +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoImplementationType.java @@ -47,7 +47,7 @@ public final class IdRepoImplementationType { Pair.of(TASKJOB_DELEGATE, "org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate"), Pair.of(REPORT_DELEGATE, "org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate"), Pair.of(LOGIC_ACTIONS, "org.apache.syncope.core.logic.api.LogicActions"), - Pair.of(VALIDATOR, "org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValueValidator"), + Pair.of(VALIDATOR, "org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValueValidator"), Pair.of(COMMAND, "org.apache.syncope.core.logic.api.Command"), Pair.of(RECIPIENTS_PROVIDER, "org.apache.syncope.core.provisioning.api.notification.RecipientsProvider"), Pair.of(ITEM_TRANSFORMER, "org.apache.syncope.core.provisioning.api.data.ItemTransformer")); diff --git a/common/keymaster/client-api/src/main/resources/defaultContent.xml b/common/keymaster/client-api/src/main/resources/defaultContent.xml index bd53f4c2f8..babe8adb59 100644 --- a/common/keymaster/client-api/src/main/resources/defaultContent.xml +++ b/common/keymaster/client-api/src/main/resources/defaultContent.xml @@ -27,14 +27,14 @@ under the License. + body="org.apache.syncope.core.persistence.common.attrvalue.EmailAddressValidator"/> + body="org.apache.syncope.core.persistence.common.attrvalue.BinaryValidator"/> diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java index 983868caac..7b5d02c2c3 100644 --- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java +++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java @@ -40,10 +40,10 @@ 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.utils.RealmUtils; 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.apache.syncope.core.provisioning.api.utils.RealmUtils; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.core.spring.security.DelegatedAdministrationException; import org.identityconnectors.common.l10n.CurrentLocale; 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 224e33fd64..c5aa5df0f4 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 @@ -71,6 +71,7 @@ import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.persistence.api.entity.VirSchema; import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; import org.apache.syncope.core.provisioning.api.ConnectorManager; import org.apache.syncope.core.provisioning.api.MappingManager; import org.apache.syncope.core.provisioning.api.VirAttrHandler; @@ -81,7 +82,6 @@ import org.apache.syncope.core.provisioning.api.pushpull.SyncopeSinglePushExecutor; import org.apache.syncope.core.provisioning.api.pushpull.stream.SyncopeStreamPullExecutor; import org.apache.syncope.core.provisioning.api.pushpull.stream.SyncopeStreamPushExecutor; -import org.apache.syncope.core.provisioning.api.utils.RealmUtils; import org.apache.syncope.core.provisioning.java.pushpull.InboundMatcher; import org.apache.syncope.core.provisioning.java.pushpull.OutboundMatcher; import org.apache.syncope.core.provisioning.java.pushpull.SinglePullJobDelegate; @@ -630,13 +630,9 @@ public List push( } List columns = new ArrayList<>(); - spec.getFields().forEach(item -> { - if (anyUtils.getField(item) == null) { - LOG.warn("Ignoring invalid field {}", item); - } else { - columns.add(item); - } - }); + spec.getFields().forEach(item -> anyUtils.getField(item).ifPresentOrElse( + field -> columns.add(item), + () -> LOG.warn("Ignoring invalid field {}", item))); spec.getPlainAttrs().forEach(item -> { if (plainSchemaDAO.existsById(item)) { columns.add(item); diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java index 3cb2db236b..86da7079e9 100644 --- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java +++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java @@ -48,13 +48,13 @@ 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.VirSchema; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; 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.provisioning.api.VirAttrHandler; import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder; import org.apache.syncope.core.provisioning.api.data.ResourceDataBinder; -import org.apache.syncope.core.provisioning.api.utils.RealmUtils; import org.apache.syncope.core.provisioning.java.pushpull.OutboundMatcher; import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils; import org.apache.syncope.core.provisioning.java.utils.MappingUtils; diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/IdMLogicTestContext.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/IdMLogicTestContext.java index e010822542..e79f3118d8 100644 --- a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/IdMLogicTestContext.java +++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/IdMLogicTestContext.java @@ -23,7 +23,6 @@ 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.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.DomainRegistry; import org.apache.syncope.core.persistence.api.content.ContentLoader; import org.apache.syncope.core.persistence.jpa.MasterDomain; @@ -50,11 +49,10 @@ public class IdMLogicTestContext { @Bean public TestInitializer testInitializer( final StartupDomainLoader domainLoader, - final DomainHolder domainHolder, final ContentLoader contentLoader, final ConfigurableApplicationContext ctx) { - return new TestInitializer(domainLoader, domainHolder, contentLoader, ctx); + return new TestInitializer(domainLoader, contentLoader, ctx); } @Bean diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/TestInitializer.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/TestInitializer.java index 05ad8941d2..2825fa988c 100644 --- a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/TestInitializer.java +++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/TestInitializer.java @@ -19,7 +19,6 @@ package org.apache.syncope.core.logic; import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.content.ContentLoader; import org.apache.syncope.core.persistence.jpa.StartupDomainLoader; import org.apache.syncope.core.spring.ApplicationContextProvider; @@ -32,20 +31,16 @@ public class TestInitializer implements InitializingBean { private final StartupDomainLoader domainLoader; - private final DomainHolder domainHolder; - private final ContentLoader contentLoader; private final ConfigurableApplicationContext ctx; public TestInitializer( final StartupDomainLoader domainLoader, - final DomainHolder domainHolder, final ContentLoader contentLoader, final ConfigurableApplicationContext ctx) { this.domainLoader = domainLoader; - this.domainHolder = domainHolder; this.contentLoader = contentLoader; this.ctx = ctx; } @@ -61,8 +56,6 @@ public void afterPropertiesSet() throws Exception { domainLoader.load(); - contentLoader.load( - SyncopeConstants.MASTER_DOMAIN, - domainHolder.getDomains().get(SyncopeConstants.MASTER_DOMAIN)); + contentLoader.load(SyncopeConstants.MASTER_DOMAIN); } } diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java index dbf699c289..231383e168 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractJobLogic.java @@ -27,8 +27,8 @@ import org.apache.syncope.common.lib.types.JobType; import org.apache.syncope.core.persistence.api.dao.JobStatusDAO; import org.apache.syncope.core.persistence.api.entity.JobStatus; +import org.apache.syncope.core.persistence.api.utils.FormatUtils; import org.apache.syncope.core.provisioning.api.job.JobManager; -import org.apache.syncope.core.provisioning.api.utils.FormatUtils; import org.apache.syncope.core.provisioning.java.job.SystemLoadReporterJob; import org.apache.syncope.core.provisioning.java.job.TaskJob; import org.apache.syncope.core.provisioning.java.job.notification.NotificationJob; diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java index 162ba001b9..3255679550 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java @@ -50,9 +50,9 @@ import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; import org.apache.syncope.core.persistence.api.search.SyncopePage; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager; import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder; -import org.apache.syncope.core.provisioning.api.utils.RealmUtils; import org.apache.syncope.core.provisioning.java.utils.TemplateUtils; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.springframework.data.domain.Page; diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AuditLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AuditLogic.java index 44e9b6bc68..6e294561a5 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AuditLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AuditLogic.java @@ -45,6 +45,7 @@ import org.apache.syncope.core.logic.audit.AuditAppender; import org.apache.syncope.core.logic.init.AuditLoader; import org.apache.syncope.core.persistence.api.dao.AuditConfDAO; +import org.apache.syncope.core.persistence.api.dao.AuditEntryDAO; 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; @@ -76,6 +77,8 @@ public class AuditLogic extends AbstractTransactionalLogic { protected final AuditConfDAO auditConfDAO; + protected final AuditEntryDAO auditEntryDAO; + protected final ExternalResourceDAO resourceDAO; protected final EntityFactory entityFactory; @@ -90,6 +93,7 @@ public class AuditLogic extends AbstractTransactionalLogic { public AuditLogic( final AuditConfDAO auditConfDAO, + final AuditEntryDAO auditEntryDAO, final ExternalResourceDAO resourceDAO, final EntityFactory entityFactory, final AuditDataBinder binder, @@ -98,6 +102,7 @@ public AuditLogic( final LoggingSystem loggingSystem) { this.auditConfDAO = auditConfDAO; + this.auditEntryDAO = auditEntryDAO; this.resourceDAO = resourceDAO; this.entityFactory = entityFactory; this.binder = binder; @@ -270,9 +275,9 @@ public Page search( final OffsetDateTime after, final Pageable pageable) { - long count = auditConfDAO.countEntries(entityKey, type, category, subcategory, events, result, before, after); + long count = auditEntryDAO.count(entityKey, type, category, subcategory, events, result, before, after); - List matching = auditConfDAO.searchEntries( + List matching = auditEntryDAO.search( entityKey, type, category, subcategory, events, result, before, after, pageable); return new SyncopePage<>(matching, pageable, count); diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/CommandLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/CommandLogic.java index ee57f62593..80e878642f 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/CommandLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/CommandLogic.java @@ -35,7 +35,7 @@ import org.apache.syncope.common.lib.types.IdRepoEntitlement; import org.apache.syncope.common.lib.types.IdRepoImplementationType; import org.apache.syncope.core.logic.api.Command; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; 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.entity.Implementation; diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java index dd328fb148..7554a95749 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java @@ -59,12 +59,12 @@ import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.task.SchedTask; import org.apache.syncope.core.persistence.api.search.SyncopePage; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; import org.apache.syncope.core.provisioning.api.GroupProvisioningManager; 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.job.JobManager; import org.apache.syncope.core.provisioning.api.job.JobNamer; -import org.apache.syncope.core.provisioning.api.utils.RealmUtils; import org.apache.syncope.core.provisioning.java.job.GroupMemberProvisionTaskJobDelegate; import org.apache.syncope.core.provisioning.java.utils.TemplateUtils; import org.apache.syncope.core.spring.security.AuthContextUtils; diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java index 36be4ef35e..fe3f1b34f6 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java @@ -21,6 +21,7 @@ import jakarta.validation.Validator; import java.util.ArrayList; import java.util.List; +import javax.sql.DataSource; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.LoggerContext; @@ -44,6 +45,7 @@ import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.dao.ApplicationDAO; import org.apache.syncope.core.persistence.api.dao.AuditConfDAO; +import org.apache.syncope.core.persistence.api.dao.AuditEntryDAO; import org.apache.syncope.core.persistence.api.dao.CASSPClientAppDAO; import org.apache.syncope.core.persistence.api.dao.DelegationDAO; import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; @@ -106,6 +108,8 @@ import org.apache.syncope.core.provisioning.api.rules.RuleEnforcer; import org.apache.syncope.core.provisioning.java.utils.TemplateUtils; import org.apache.syncope.core.spring.security.SecurityProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.logging.LoggingSystem; import org.springframework.context.annotation.Bean; @@ -117,6 +121,8 @@ @Configuration(proxyBeanMethods = false) public class IdRepoLogicContext { + protected static final Logger LOG = LoggerFactory.getLogger(IdRepoLogicContext.class); + @ConditionalOnMissingBean @Bean public LogicInvocationHandler logicInvocationHandler( @@ -149,19 +155,23 @@ public AuditLoader auditLoader( @ConditionalOnMissingBean(name = "defaultAuditAppenders") @Bean - public List defaultAuditAppenders(final DomainHolder domainHolder) { + public List defaultAuditAppenders(final DomainHolder domainHolder) { List auditAppenders = new ArrayList<>(); LoggerContext logCtx = (LoggerContext) LogManager.getContext(false); - domainHolder.getDomains().forEach((domain, dataSource) -> { - AuditAppender appender = new JdbcAuditAppender(domain, dataSource); - - LoggerConfig logConf = new LoggerConfig(AuditLoggerName.getAuditLoggerName(domain), null, false); - logConf.addAppender(appender.getTargetAppender(), Level.DEBUG, null); - logConf.setLevel(Level.DEBUG); - logCtx.getConfiguration().addLogger(logConf.getName(), logConf); - - auditAppenders.add(appender); + domainHolder.getDomains().forEach((domain, v) -> { + if (v instanceof DataSource dataSource) { + AuditAppender appender = new JdbcAuditAppender(domain, dataSource); + + LoggerConfig logConf = new LoggerConfig(AuditLoggerName.getAuditLoggerName(domain), null, false); + logConf.addAppender(appender.getTargetAppender(), Level.DEBUG, null); + logConf.setLevel(Level.DEBUG); + logCtx.getConfiguration().addLogger(logConf.getName(), logConf); + + auditAppenders.add(appender); + } else { + LOG.warn("Unsupported persistence source: " + v.getClass().getName()); + } }); return auditAppenders; @@ -248,6 +258,7 @@ public ApplicationLogic applicationLogic( @Bean public AuditLogic auditLogic( final AuditConfDAO auditConfDAO, + final AuditEntryDAO auditEntryDAO, final ExternalResourceDAO externalResourceDAO, final EntityFactory entityFactory, final AuditDataBinder binder, @@ -257,6 +268,7 @@ public AuditLogic auditLogic( return new AuditLogic( auditConfDAO, + auditEntryDAO, externalResourceDAO, entityFactory, binder, diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java index 17b38f9d7b..5bbf0f2c9f 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java @@ -200,11 +200,11 @@ public ProvisioningResult delete(final String fullPath) { AnyCond keyCond = new AnyCond(AttrCond.Type.ISNOTNULL); keyCond.setSchema("key"); SearchCond allMatchingCond = SearchCond.getLeaf(keyCond); - int users = searchDAO.count(realm, true, adminRealms, allMatchingCond, AnyTypeKind.USER); - int groups = searchDAO.count(realm, true, adminRealms, allMatchingCond, AnyTypeKind.GROUP); - int anyObjects = searchDAO.count(realm, true, adminRealms, allMatchingCond, AnyTypeKind.ANY_OBJECT); - int macroTasks = taskDAO.findByRealm(realm).size(); - int clientApps = casSPClientAppDAO.findAllByRealm(realm).size() + long users = searchDAO.count(realm, true, adminRealms, allMatchingCond, AnyTypeKind.USER); + long groups = searchDAO.count(realm, true, adminRealms, allMatchingCond, AnyTypeKind.GROUP); + long anyObjects = searchDAO.count(realm, true, adminRealms, allMatchingCond, AnyTypeKind.ANY_OBJECT); + long macroTasks = taskDAO.findByRealm(realm).size(); + long clientApps = casSPClientAppDAO.findAllByRealm(realm).size() + saml2SPClientAppDAO.findAllByRealm(realm).size() + oidcRPClientAppDAO.findAllByRealm(realm).size(); diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java index 8404098239..8f34d58af0 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java @@ -48,10 +48,10 @@ import org.apache.syncope.core.persistence.api.entity.Report; import org.apache.syncope.core.persistence.api.entity.ReportExec; import org.apache.syncope.core.persistence.api.search.SyncopePage; +import org.apache.syncope.core.persistence.api.utils.ExceptionUtils2; import org.apache.syncope.core.provisioning.api.data.ReportDataBinder; import org.apache.syncope.core.provisioning.api.job.JobManager; import org.apache.syncope.core.provisioning.api.job.JobNamer; -import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2; import org.apache.syncope.core.provisioning.java.job.report.ReportJob; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.quartz.JobKey; diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RoleLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RoleLogic.java index 84389cd555..7c5ada538c 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RoleLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RoleLogic.java @@ -30,7 +30,6 @@ 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.apache.syncope.core.spring.security.AuthDataAccessor; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.transaction.annotation.Transactional; @@ -74,7 +73,7 @@ public RoleTO update(final RoleTO roleTO) { @PreAuthorize("hasRole('" + IdRepoEntitlement.ROLE_DELETE + "')") public RoleTO delete(final String key) { - if (AuthDataAccessor.GROUP_OWNER_ROLE.equals(key)) { + if (RoleDAO.GROUP_OWNER_ROLE.equals(key)) { SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRole); sce.getElements().add("This Role cannot be deleted"); throw sce; diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java index d8a28979ef..e4b42233fe 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java @@ -61,13 +61,13 @@ 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.persistence.api.search.SyncopePage; +import org.apache.syncope.core.persistence.api.utils.ExceptionUtils2; 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.job.JobNamer; import org.apache.syncope.core.provisioning.api.notification.NotificationJobDelegate; import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor; import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo; -import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2; import org.apache.syncope.core.provisioning.java.propagation.DefaultPropagationReporter; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.core.spring.security.DelegatedAdministrationException; diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java index 131bc93851..6593dc1d26 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java @@ -50,7 +50,7 @@ import org.apache.syncope.common.lib.types.PatchOperation; import org.apache.syncope.common.rest.api.beans.ComplianceQuery; import org.apache.syncope.core.logic.api.LogicActions; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; +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.AnySearchDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; @@ -69,11 +69,11 @@ 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.persistence.api.search.SyncopePage; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; import org.apache.syncope.core.provisioning.api.UserProvisioningManager; import org.apache.syncope.core.provisioning.api.data.UserDataBinder; import org.apache.syncope.core.provisioning.api.rules.RuleEnforcer; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; -import org.apache.syncope.core.provisioning.api.utils.RealmUtils; import org.apache.syncope.core.provisioning.java.utils.TemplateUtils; import org.apache.syncope.core.spring.policy.AccountPolicyException; import org.apache.syncope.core.spring.policy.PasswordPolicyException; diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/JdbcAuditAppender.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/JdbcAuditAppender.java index 164cdd3b7b..98ebba99fe 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/JdbcAuditAppender.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/JdbcAuditAppender.java @@ -29,7 +29,7 @@ import org.apache.logging.log4j.core.appender.db.ColumnMapping; import org.apache.logging.log4j.core.appender.db.jdbc.AbstractConnectionSource; import org.apache.logging.log4j.core.appender.db.jdbc.JdbcAppender; -import org.apache.syncope.core.persistence.api.dao.AuditConfDAO; +import org.apache.syncope.core.persistence.api.dao.AuditEntryDAO; import org.springframework.jdbc.datasource.DataSourceUtils; public class JdbcAuditAppender extends DefaultAuditAppender { @@ -48,7 +48,7 @@ public JdbcAuditAppender(final String domain, final DataSource domainDataSource) setConfiguration(logCtx.getConfiguration()).setName("LOGGER").setPattern("%logger").build(), ColumnMapping.newBuilder(). setConfiguration(logCtx.getConfiguration()). - setName(AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN).setPattern("%message").build(), + setName(AuditEntryDAO.MESSAGE_COLUMN).setPattern("%message").build(), ColumnMapping.newBuilder(). setConfiguration(logCtx.getConfiguration()).setName("THROWABLE").setPattern("%ex{full}").build() }; @@ -60,7 +60,7 @@ public JdbcAuditAppender(final String domain, final DataSource domainDataSource) setIgnoreExceptions(false). setConnectionSource(new DataSourceConnectionSource(domain, domainDataSource)). setBufferSize(0). - setTableName(AuditConfDAO.AUDIT_ENTRY_TABLE). + setTableName(AuditEntryDAO.TABLE). setColumnMappings(columnMappings). build(); a.start(); diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/AuditLoader.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/AuditLoader.java index 59c7188172..4b1b47ce72 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/AuditLoader.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/AuditLoader.java @@ -20,7 +20,6 @@ import java.util.List; import java.util.Optional; -import javax.sql.DataSource; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.Appender; @@ -71,7 +70,7 @@ public int getOrder() { } @Override - public void load(final String domain, final DataSource datasource) { + public void load(final String domain) { LoggerContext logCtx = (LoggerContext) LogManager.getContext(false); auditAppenders.forEach(auditAppender -> auditAppender.getEvents().stream(). diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java index ae68ae04d6..f594233270 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java @@ -37,7 +37,7 @@ import org.apache.syncope.common.lib.types.ImplementationTypesHolder; import org.apache.syncope.core.logic.api.Command; import org.apache.syncope.core.logic.api.LogicActions; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValueValidator; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValueValidator; import org.apache.syncope.core.provisioning.api.ImplementationLookup; import org.apache.syncope.core.provisioning.api.ProvisionSorter; import org.apache.syncope.core.provisioning.api.data.ItemTransformer; diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/IdRepoEntitlementLoader.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/IdRepoEntitlementLoader.java index 7156d22cb1..1901eddfda 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/IdRepoEntitlementLoader.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/IdRepoEntitlementLoader.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.logic.init; -import javax.sql.DataSource; import org.apache.syncope.common.lib.types.EntitlementsHolder; import org.apache.syncope.common.lib.types.IdRepoEntitlement; import org.apache.syncope.core.persistence.api.SyncopeCoreLoader; @@ -43,7 +42,7 @@ public void load() { } @Override - public void load(final String domain, final DataSource datasource) { + public void load(final String domain) { AuthContextUtils.runAsAdmin(domain, () -> entitlementAccessor.addEntitlementsForAnyTypes()); } } diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/IdRepoLogicTestContext.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/IdRepoLogicTestContext.java index e764e074cd..29ec5a8bc2 100644 --- a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/IdRepoLogicTestContext.java +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/IdRepoLogicTestContext.java @@ -23,7 +23,6 @@ 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.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.DomainRegistry; import org.apache.syncope.core.persistence.api.content.ContentLoader; import org.apache.syncope.core.persistence.jpa.MasterDomain; @@ -50,11 +49,10 @@ public class IdRepoLogicTestContext { @Bean public TestInitializer testInitializer( final StartupDomainLoader domainLoader, - final DomainHolder domainHolder, final ContentLoader contentLoader, final ConfigurableApplicationContext ctx) { - return new TestInitializer(domainLoader, domainHolder, contentLoader, ctx); + return new TestInitializer(domainLoader, contentLoader, ctx); } @Bean diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/TestInitializer.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/TestInitializer.java index 05ad8941d2..2825fa988c 100644 --- a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/TestInitializer.java +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/TestInitializer.java @@ -19,7 +19,6 @@ package org.apache.syncope.core.logic; import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.content.ContentLoader; import org.apache.syncope.core.persistence.jpa.StartupDomainLoader; import org.apache.syncope.core.spring.ApplicationContextProvider; @@ -32,20 +31,16 @@ public class TestInitializer implements InitializingBean { private final StartupDomainLoader domainLoader; - private final DomainHolder domainHolder; - private final ContentLoader contentLoader; private final ConfigurableApplicationContext ctx; public TestInitializer( final StartupDomainLoader domainLoader, - final DomainHolder domainHolder, final ContentLoader contentLoader, final ConfigurableApplicationContext ctx) { this.domainLoader = domainLoader; - this.domainHolder = domainHolder; this.contentLoader = contentLoader; this.ctx = ctx; } @@ -61,8 +56,6 @@ public void afterPropertiesSet() throws Exception { domainLoader.load(); - contentLoader.load( - SyncopeConstants.MASTER_DOMAIN, - domainHolder.getDomains().get(SyncopeConstants.MASTER_DOMAIN)); + contentLoader.load(SyncopeConstants.MASTER_DOMAIN); } } diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/IdRepoRESTCXFContext.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/IdRepoRESTCXFContext.java index 1806f8035a..4e11f9fdb3 100644 --- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/IdRepoRESTCXFContext.java +++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/IdRepoRESTCXFContext.java @@ -243,7 +243,7 @@ public AddETagFilter addETagFilter() { @ConditionalOnMissingBean(name = { "openApiCustomizer", "syncopeOpenApiCustomizer" }) @Bean - public OpenApiCustomizer openApiCustomizer(final DomainHolder domainHolder, final Environment env) { + public OpenApiCustomizer openApiCustomizer(final DomainHolder domainHolder, final Environment env) { JavaDocProvider javaDocProvider = JavaDocUtils.getJavaDocURLs(). map(JavaDocProvider::new). orElseGet(() -> JavaDocUtils.getJavaDocPaths(env). diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java index 5be58d4ab0..f102e1eadb 100644 --- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java +++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RestServiceExceptionMapper.java @@ -43,8 +43,8 @@ import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.common.lib.types.EntityViolationType; import org.apache.syncope.common.rest.api.RESTHeaders; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; -import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.ParsingValidationException; import org.apache.syncope.core.persistence.api.dao.DuplicateException; import org.apache.syncope.core.persistence.api.dao.MalformedPathException; import org.apache.syncope.core.persistence.api.dao.NotFoundException; diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/SyncopeOpenApiCustomizer.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/SyncopeOpenApiCustomizer.java index 9f6ef53848..01bc0f2879 100644 --- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/SyncopeOpenApiCustomizer.java +++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/SyncopeOpenApiCustomizer.java @@ -52,9 +52,9 @@ public class SyncopeOpenApiCustomizer extends OpenApiCustomizer { - private final DomainHolder domainHolder; + private final DomainHolder domainHolder; - public SyncopeOpenApiCustomizer(final DomainHolder domainHolder) { + public SyncopeOpenApiCustomizer(final DomainHolder domainHolder) { this.domainHolder = domainHolder; } diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/ThreadLocalCleanupOutInterceptor.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/ThreadLocalCleanupOutInterceptor.java index 39b893a690..5e8b024ec5 100644 --- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/ThreadLocalCleanupOutInterceptor.java +++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/ThreadLocalCleanupOutInterceptor.java @@ -22,7 +22,7 @@ import org.apache.cxf.message.Message; import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; -import org.apache.syncope.core.provisioning.api.utils.FormatUtils; +import org.apache.syncope.core.persistence.api.utils.FormatUtils; import org.identityconnectors.common.l10n.CurrentLocale; import org.identityconnectors.framework.impl.api.local.ThreadClassLoaderManager; import org.slf4j.MDC; diff --git a/core/persistence-api/pom.xml b/core/persistence-api/pom.xml index 2b69308862..f222a90534 100644 --- a/core/persistence-api/pom.xml +++ b/core/persistence-api/pom.xml @@ -77,13 +77,18 @@ under the License. syncope-common-keymaster-client-api ${project.version} - + org.slf4j slf4j-simple test + + org.mockito + mockito-junit-jupiter + test + org.junit.jupiter junit-jupiter diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/DomainHolder.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/DomainHolder.java index 688a36c022..bc69b6f4cc 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/DomainHolder.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/DomainHolder.java @@ -19,13 +19,15 @@ package org.apache.syncope.core.persistence.api; import java.util.Map; -import javax.sql.DataSource; /** * Holds information about domain effectively available at runtime. + * + * @param persistence source for the given domain */ -@FunctionalInterface -public interface DomainHolder { +public interface DomainHolder { + + Map getDomains(); - Map getDomains(); + Map getHealthInfo(); } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/SyncopeCoreLoader.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/SyncopeCoreLoader.java index 9f6f2b3e86..12952604ad 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/SyncopeCoreLoader.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/SyncopeCoreLoader.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.api; -import javax.sql.DataSource; import org.springframework.core.Ordered; @FunctionalInterface @@ -35,9 +34,8 @@ default void load() { * Perform init operations on the given domain. * * @param domain domain to initialize - * @param datasource db access for the given domain */ - default void load(String domain, DataSource datasource) { + default void load(String domain) { // nothing to do } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/InvalidEntityException.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/InvalidEntityException.java similarity index 98% rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/InvalidEntityException.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/InvalidEntityException.java index c91fa348df..66822d8b44 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/InvalidEntityException.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/InvalidEntityException.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.api.attrvalue.validation; +package org.apache.syncope.core.persistence.api.attrvalue; import jakarta.validation.ConstraintViolation; import jakarta.validation.ValidationException; diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/InvalidPlainAttrValueException.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/InvalidPlainAttrValueException.java similarity index 95% rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/InvalidPlainAttrValueException.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/InvalidPlainAttrValueException.java index ae5d0be676..a4c551005e 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/InvalidPlainAttrValueException.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/InvalidPlainAttrValueException.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.api.attrvalue.validation; +package org.apache.syncope.core.persistence.api.attrvalue; import jakarta.validation.ValidationException; import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/ParsingValidationException.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/ParsingValidationException.java similarity index 94% rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/ParsingValidationException.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/ParsingValidationException.java index 67c773d575..e203593db0 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/ParsingValidationException.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/ParsingValidationException.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.api.attrvalue.validation; +package org.apache.syncope.core.persistence.api.attrvalue; import jakarta.validation.ValidationException; diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/PlainAttrValidationManager.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/PlainAttrValidationManager.java similarity index 93% rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/PlainAttrValidationManager.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/PlainAttrValidationManager.java index f686162dae..973d529a8c 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/PlainAttrValidationManager.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/PlainAttrValidationManager.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.api.attrvalue.validation; +package org.apache.syncope.core.persistence.api.attrvalue; import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; import org.apache.syncope.core.persistence.api.entity.PlainSchema; diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/PlainAttrValueValidator.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/PlainAttrValueValidator.java similarity index 93% rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/PlainAttrValueValidator.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/PlainAttrValueValidator.java index 44d1f21161..4aca06ff67 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/validation/PlainAttrValueValidator.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/attrvalue/PlainAttrValueValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.api.attrvalue.validation; +package org.apache.syncope.core.persistence.api.attrvalue; import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; import org.apache.syncope.core.persistence.api.entity.PlainSchema; diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/content/ContentExporter.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/content/ContentExporter.java index 7f9d307f8c..0568d8840d 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/content/ContentExporter.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/content/ContentExporter.java @@ -27,7 +27,7 @@ public interface ContentExporter extends ContentDealer { void export( String domain, - int tableThreshold, + int threshold, OutputStream output) throws SAXException, TransformerConfigurationException; } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java index 4d082432bd..61d8c62664 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java @@ -22,6 +22,8 @@ import java.util.Collection; import java.util.List; import java.util.Optional; +import org.apache.syncope.core.persistence.api.dao.search.AnyCond; +import org.apache.syncope.core.persistence.api.dao.search.AttrCond; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.DerSchema; @@ -68,7 +70,11 @@ Optional findByPlainAttrUniqueValue( /** * @return the search condition to match all entities */ - SearchCond getAllMatchingCond(); + default SearchCond getAllMatchingCond() { + AnyCond idCond = new AnyCond(AttrCond.Type.ISNOTNULL); + idCond.setSchema("id"); + return SearchCond.getLeaf(idCond); + } AllowedSchemas findAllowedSchemas(A any, Class reference); diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnySearchDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnySearchDAO.java index 5f6810ff99..496fffa4ac 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnySearchDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnySearchDAO.java @@ -37,7 +37,7 @@ public interface AnySearchDAO { * @param kind any object * @return size of search result */ - int count( + long count( Realm base, boolean recursive, Set adminRealms, diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuditConfDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuditConfDAO.java index 5568808c1b..97df6ec884 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuditConfDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuditConfDAO.java @@ -18,39 +18,7 @@ */ package org.apache.syncope.core.persistence.api.dao; -import java.time.OffsetDateTime; -import java.util.List; -import org.apache.syncope.common.lib.audit.AuditEntry; -import org.apache.syncope.common.lib.types.AuditElements; import org.apache.syncope.core.persistence.api.entity.AuditConf; -import org.springframework.data.domain.Pageable; public interface AuditConfDAO extends DAO { - - String AUDIT_ENTRY_TABLE = "AuditEntry"; - - String AUDIT_ENTRY_MESSAGE_COLUMN = "MESSAGE"; - - String AUDIT_ENTRY_EVENT_DATE_COLUMN = "EVENT_DATE"; - - long countEntries( - String entityKey, - AuditElements.EventCategoryType type, - String category, - String subcategory, - List events, - AuditElements.Result result, - OffsetDateTime before, - OffsetDateTime after); - - List searchEntries( - String entityKey, - AuditElements.EventCategoryType type, - String category, - String subcategory, - List events, - AuditElements.Result result, - OffsetDateTime before, - OffsetDateTime after, - Pageable pageable); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepoExt.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuditEntryDAO.java similarity index 86% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepoExt.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuditEntryDAO.java index a788169309..d1ba2027e6 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepoExt.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuditEntryDAO.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao.repo; +package org.apache.syncope.core.persistence.api.dao; import java.time.OffsetDateTime; import java.util.List; @@ -24,9 +24,15 @@ import org.apache.syncope.common.lib.types.AuditElements; import org.springframework.data.domain.Pageable; -public interface AuditConfRepoExt { +public interface AuditEntryDAO { - long countEntries( + String TABLE = "AuditEntry"; + + String MESSAGE_COLUMN = "MESSAGE"; + + String EVENT_DATE_COLUMN = "EVENT_DATE"; + + long count( String entityKey, AuditElements.EventCategoryType type, String category, @@ -36,7 +42,7 @@ long countEntries( OffsetDateTime before, OffsetDateTime after); - List searchEntries( + List search( String entityKey, AuditElements.EventCategoryType type, String category, diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PlainSchemaDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PlainSchemaDAO.java index 2175eb2d4f..5ea8976744 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PlainSchemaDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PlainSchemaDAO.java @@ -25,8 +25,6 @@ public interface PlainSchemaDAO extends SchemaDAO { - > List findAttrs(PlainSchema schema, Class reference); - > boolean hasAttrs(PlainSchema schema, Class reference); List findByValidator(Implementation validator); diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RoleDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RoleDAO.java index 3613226bfa..1534fc633b 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RoleDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RoleDAO.java @@ -26,6 +26,8 @@ public interface RoleDAO extends DAO { + String GROUP_OWNER_ROLE = "GROUP_OWNER"; + List findByRealms(Realm realm); List findByPrivileges(Privilege privilege); diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyUtils.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyUtils.java index 0ceb427afe..8db658e6e8 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyUtils.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyUtils.java @@ -19,12 +19,13 @@ package org.apache.syncope.core.persistence.api.entity; import java.lang.reflect.Field; +import java.util.Optional; import java.util.Set; 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.types.AnyTypeKind; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyDAO; public interface AnyUtils { @@ -33,7 +34,7 @@ public interface AnyUtils { > Class anyClass(); - Field getField(String name); + Optional getField(String name); > Class plainAttrClass(); @@ -43,9 +44,9 @@ public interface AnyUtils { T newPlainAttrValue(); - Class plainAttrUniqueValueClass(); + Class plainAttrUniqueValueClass(); - T newPlainAttrUniqueValue(); + T newPlainAttrUniqueValue(); T clonePlainAttrValue(T src); diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyUtilsFactory.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyUtilsFactory.java index ea37344034..57b358d650 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyUtilsFactory.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyUtilsFactory.java @@ -19,12 +19,64 @@ package org.apache.syncope.core.persistence.api.entity; 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; -public interface AnyUtilsFactory { +public class AnyUtilsFactory { - AnyUtils getInstance(AnyTypeKind anyTypeKind); + protected final AnyUtils userAnyUtils; - AnyUtils getInstance(Any any); + protected final AnyUtils linkedAccountAnyUtils; - AnyUtils getLinkedAccountInstance(); + protected final AnyUtils groupAnyUtils; + + protected final AnyUtils anyObjectAnyUtils; + + public AnyUtilsFactory( + final AnyUtils userAnyUtils, + final AnyUtils linkedAccountAnyUtils, + final AnyUtils groupAnyUtils, + final AnyUtils anyObjectAnyUtils) { + + this.userAnyUtils = userAnyUtils; + this.linkedAccountAnyUtils = linkedAccountAnyUtils; + this.groupAnyUtils = groupAnyUtils; + this.anyObjectAnyUtils = anyObjectAnyUtils; + } + + public AnyUtils getInstance(final AnyTypeKind anyTypeKind) { + switch (anyTypeKind) { + case ANY_OBJECT: + return anyObjectAnyUtils; + + case GROUP: + return groupAnyUtils; + + case USER: + default: + return userAnyUtils; + } + } + + public AnyUtils getInstance(final Any any) { + AnyTypeKind anyTypeKind = null; + if (any instanceof User) { + anyTypeKind = AnyTypeKind.USER; + } else if (any instanceof Group) { + anyTypeKind = AnyTypeKind.GROUP; + } else if (any instanceof AnyObject) { + anyTypeKind = AnyTypeKind.ANY_OBJECT; + } + + if (anyTypeKind == null) { + throw new IllegalArgumentException("Any type not supported: " + any.getClass().getName()); + } + + return getInstance(anyTypeKind); + } + + public AnyUtils getLinkedAccountInstance() { + return linkedAccountAnyUtils; + } } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstance.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstance.java index 57bbda6f06..91cbe9da2a 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstance.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstance.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Set; import org.apache.syncope.common.lib.types.ConnConfProperty; +import org.apache.syncope.common.lib.types.ConnPoolConf; import org.apache.syncope.common.lib.types.ConnectorCapability; public interface ConnInstance extends Entity { diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/EntityFactory.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/EntityFactory.java index a74c4900b4..e1ceac796e 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/EntityFactory.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/EntityFactory.java @@ -27,8 +27,6 @@ public interface EntityFactory { E newEntity(Class reference); - ConnPoolConf newConnPoolConf(); - Class userClass(); Class groupClass(); diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/PlainAttr.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/PlainAttr.java index 15376c3016..b857ab9950 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/PlainAttr.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/PlainAttr.java @@ -19,7 +19,7 @@ package org.apache.syncope.core.persistence.api.entity; import java.util.List; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; public interface PlainAttr> extends Entity { diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java index 0bb7bd85ad..5d9139d9ba 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java @@ -20,7 +20,6 @@ import java.util.List; import java.util.Set; -import org.apache.syncope.core.persistence.api.entity.user.DynRoleMembership; public interface Role extends ProvidedKeyEntity { @@ -34,9 +33,9 @@ public interface Role extends ProvidedKeyEntity { List getDynRealms(); - DynRoleMembership getDynMembership(); + String getDynMembershipCond(); - void setDynMembership(DynRoleMembership dynMembership); + void setDynMembershipCond(String dynMembershipCond); String getAnyLayout(); diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientAppUtils.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientAppUtils.java index dd77ca3a2a..b8e54bbb8f 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientAppUtils.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientAppUtils.java @@ -20,9 +20,27 @@ import org.apache.syncope.common.lib.types.ClientAppType; -public interface ClientAppUtils { +public class ClientAppUtils { - ClientAppType getType(); + private final ClientAppType type; - Class clientAppClass(); + public ClientAppUtils(final ClientAppType type) { + this.type = type; + } + + public ClientAppType getType() { + return type; + } + + public Class clientAppClass() { + switch (type) { + case OIDCRP: + return OIDCRPClientApp.class; + case CASSP: + return CASSPClientApp.class; + case SAML2SP: + default: + return SAML2SPClientApp.class; + } + } } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientAppUtilsFactory.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientAppUtilsFactory.java index c5480d7e2e..360c435049 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientAppUtilsFactory.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/am/ClientAppUtilsFactory.java @@ -18,16 +18,49 @@ */ package org.apache.syncope.core.persistence.api.entity.am; +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.ClientAppType; -public interface ClientAppUtilsFactory { +public class ClientAppUtilsFactory { - ClientAppUtils getInstance(ClientAppType type); + public ClientAppUtils getInstance(final ClientAppType type) { + return new ClientAppUtils(type); + } - ClientAppUtils getInstance(ClientApp clientApp); + public ClientAppUtils getInstance(final ClientApp clientApp) { + ClientAppType type; + if (clientApp instanceof SAML2SPClientApp) { + type = ClientAppType.SAML2SP; + } else if (clientApp instanceof CASSPClientApp) { + type = ClientAppType.CASSP; + } else if (clientApp instanceof OIDCRPClientApp) { + type = ClientAppType.OIDCRP; + } else { + throw new IllegalArgumentException("Invalid client app: " + clientApp); + } - ClientAppUtils getInstance(Class clientAppClass); + return getInstance(type); + } - ClientAppUtils getInstance(ClientAppTO clientAppTO); + public ClientAppUtils getInstance(final Class clientAppClass) { + ClientAppType type; + if (clientAppClass == SAML2SPClientAppTO.class) { + type = ClientAppType.SAML2SP; + } else if (clientAppClass == CASSPClientAppTO.class) { + type = ClientAppType.CASSP; + } else if (clientAppClass == OIDCRPClientAppTO.class) { + type = ClientAppType.OIDCRP; + } else { + throw new IllegalArgumentException("Invalid ClientAppTO app: " + clientAppClass.getName()); + } + + return getInstance(type); + } + + public ClientAppUtils getInstance(final ClientAppTO clientAppTO) { + return getInstance(clientAppTO.getClass()); + } } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtils.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtils.java index e7126465a8..ddabff7694 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtils.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtils.java @@ -20,9 +20,47 @@ import org.apache.syncope.common.lib.types.PolicyType; -public interface PolicyUtils { +public class PolicyUtils { - PolicyType getType(); + protected final PolicyType type; - Class policyClass(); + public PolicyUtils(final PolicyType type) { + this.type = type; + } + + public PolicyType getType() { + return type; + } + + public Class policyClass() { + switch (type) { + case ACCOUNT: + return AccountPolicy.class; + + case PASSWORD: + return PasswordPolicy.class; + + case AUTH: + return AuthPolicy.class; + + case ATTR_RELEASE: + return AttrReleasePolicy.class; + + case ACCESS: + return AccessPolicy.class; + + case TICKET_EXPIRATION: + return TicketExpirationPolicy.class; + + case PROPAGATION: + return PropagationPolicy.class; + + case PULL: + return PullPolicy.class; + + case PUSH: + default: + return PushPolicy.class; + } + } } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtilsFactory.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtilsFactory.java index e3092c055d..e9fba5e390 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtilsFactory.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtilsFactory.java @@ -18,16 +18,79 @@ */ package org.apache.syncope.core.persistence.api.entity.policy; +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.PasswordPolicyTO; import org.apache.syncope.common.lib.policy.PolicyTO; +import org.apache.syncope.common.lib.policy.PropagationPolicyTO; +import org.apache.syncope.common.lib.policy.PullPolicyTO; +import org.apache.syncope.common.lib.policy.PushPolicyTO; +import org.apache.syncope.common.lib.policy.TicketExpirationPolicyTO; import org.apache.syncope.common.lib.types.PolicyType; -public interface PolicyUtilsFactory { +public class PolicyUtilsFactory { - PolicyUtils getInstance(PolicyType type); + public PolicyUtils getInstance(final PolicyType type) { + return new PolicyUtils(type); + } - PolicyUtils getInstance(Policy policy); + public PolicyUtils getInstance(final Policy policy) { + PolicyType type; + if (policy instanceof AccountPolicy) { + type = PolicyType.ACCOUNT; + } else if (policy instanceof PasswordPolicy) { + type = PolicyType.PASSWORD; + } else if (policy instanceof PropagationPolicy) { + type = PolicyType.PROPAGATION; + } else if (policy instanceof PullPolicy) { + type = PolicyType.PULL; + } else if (policy instanceof PushPolicy) { + type = PolicyType.PUSH; + } else if (policy instanceof AuthPolicy) { + type = PolicyType.AUTH; + } else if (policy instanceof AccessPolicy) { + type = PolicyType.ACCESS; + } else if (policy instanceof AttrReleasePolicy) { + type = PolicyType.ATTR_RELEASE; + } else if (policy instanceof TicketExpirationPolicy) { + type = PolicyType.TICKET_EXPIRATION; + } else { + throw new IllegalArgumentException("Invalid policy: " + policy); + } - PolicyUtils getInstance(Class policyClass); + return getInstance(type); + } - PolicyUtils getInstance(PolicyTO policyTO); + public PolicyUtils getInstance(final Class policyClass) { + PolicyType type; + if (policyClass == AccountPolicyTO.class) { + type = PolicyType.ACCOUNT; + } else if (policyClass == PasswordPolicyTO.class) { + type = PolicyType.PASSWORD; + } else if (policyClass == PropagationPolicyTO.class) { + type = PolicyType.PROPAGATION; + } else if (policyClass == PullPolicyTO.class) { + type = PolicyType.PULL; + } else if (policyClass == PushPolicyTO.class) { + type = PolicyType.PUSH; + } else if (policyClass == AuthPolicyTO.class) { + type = PolicyType.AUTH; + } else if (policyClass == AccessPolicyTO.class) { + type = PolicyType.ACCESS; + } else if (policyClass == AttrReleasePolicyTO.class) { + type = PolicyType.ATTR_RELEASE; + } else if (policyClass == TicketExpirationPolicyTO.class) { + type = PolicyType.TICKET_EXPIRATION; + } else { + throw new IllegalArgumentException("Invalid PolicyTO class: " + policyClass.getName()); + } + + return getInstance(type); + } + + public PolicyUtils getInstance(final PolicyTO policyTO) { + return getInstance(policyTO.getClass()); + } } diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/ConnPoolConfUtils.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/utils/ConnPoolConfUtils.java similarity index 92% rename from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/ConnPoolConfUtils.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/utils/ConnPoolConfUtils.java index c0ba763582..8af4fb0da4 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/ConnPoolConfUtils.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/utils/ConnPoolConfUtils.java @@ -16,16 +16,16 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.provisioning.api.utils; +package org.apache.syncope.core.persistence.api.utils; import java.util.Optional; -import org.apache.syncope.common.lib.to.ConnPoolConfTO; -import org.apache.syncope.core.persistence.api.entity.ConnPoolConf; +import org.apache.syncope.common.lib.types.ConnPoolConf; import org.identityconnectors.common.pooling.ObjectPoolConfiguration; public final class ConnPoolConfUtils { - public static ConnPoolConf getConnPoolConf(final ConnPoolConfTO cpcto, final ConnPoolConf cpc) { + public static ConnPoolConf getConnPoolConf(final ConnPoolConf cpcto) { + ConnPoolConf cpc = new ConnPoolConf(); ObjectPoolConfiguration opc = new ObjectPoolConfiguration(); cpc.setMaxIdle(Optional.ofNullable(cpcto.getMaxIdle()).orElse(opc.getMaxIdle())); diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/ExceptionUtils2.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/utils/ExceptionUtils2.java similarity index 96% rename from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/ExceptionUtils2.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/utils/ExceptionUtils2.java index 8a2f63b599..f63584bad8 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/ExceptionUtils2.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/utils/ExceptionUtils2.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.provisioning.api.utils; +package org.apache.syncope.core.persistence.api.utils; import org.apache.commons.lang3.exception.ExceptionUtils; diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/FormatUtils.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/utils/FormatUtils.java similarity index 98% rename from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/FormatUtils.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/utils/FormatUtils.java index ae2459f8e6..6cf8aeb32b 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/FormatUtils.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/utils/FormatUtils.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.provisioning.api.utils; +package org.apache.syncope.core.persistence.api.utils; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/RealmUtils.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/utils/RealmUtils.java similarity index 98% rename from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/RealmUtils.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/utils/RealmUtils.java index cf71c0bb40..fe5f891354 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/RealmUtils.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/utils/RealmUtils.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.provisioning.api.utils; +package org.apache.syncope.core.persistence.api.utils; import java.util.Collection; import java.util.HashSet; diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/URIUtils.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/utils/URIUtils.java similarity index 97% rename from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/URIUtils.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/utils/URIUtils.java index 6a7db47dfd..ad85519b3a 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/utils/URIUtils.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/utils/URIUtils.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.provisioning.api.utils; +package org.apache.syncope.core.persistence.api.utils; import java.io.File; import java.net.MalformedURLException; 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 new file mode 100644 index 0000000000..45b9ce254f --- /dev/null +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/AbstractTest.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.core.persistence.api.utils; + +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.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.WARN) +@SuppressWarnings("squid:S2187") +public class AbstractTest { + + @BeforeEach + public void init() { + MockitoAnnotations.openMocks(this); + } +} diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/FormatUtilsTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/FormatUtilsTest.java similarity index 97% rename from core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/FormatUtilsTest.java rename to core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/FormatUtilsTest.java index edcb885bc1..78baeb9fa4 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/FormatUtilsTest.java +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/FormatUtilsTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.provisioning.api.utils; +package org.apache.syncope.core.persistence.api.utils; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -30,7 +30,6 @@ import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.Locale; -import org.apache.syncope.core.provisioning.api.AbstractTest; import org.junit.jupiter.api.Test; public class FormatUtilsTest extends AbstractTest { diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/RealmUtilsTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/RealmUtilsTest.java similarity index 95% rename from core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/RealmUtilsTest.java rename to core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/RealmUtilsTest.java index 24096d25a2..5079b37d05 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/RealmUtilsTest.java +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/RealmUtilsTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.provisioning.api.utils; +package org.apache.syncope.core.persistence.api.utils; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -26,7 +26,6 @@ import java.util.Optional; import java.util.Set; import org.apache.commons.lang3.tuple.Pair; -import org.apache.syncope.core.provisioning.api.AbstractTest; import org.junit.jupiter.api.Test; public class RealmUtilsTest extends AbstractTest { diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/URIUtilsTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/URIUtilsTest.java similarity index 93% rename from core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/URIUtilsTest.java rename to core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/URIUtilsTest.java index 39db5247ca..8b2e665ee2 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/URIUtilsTest.java +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/utils/URIUtilsTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.provisioning.api.utils; +package org.apache.syncope.core.persistence.api.utils; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -25,7 +25,6 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.concurrent.atomic.AtomicReference; -import org.apache.syncope.core.provisioning.api.AbstractTest; import org.junit.jupiter.api.Test; public class URIUtilsTest extends AbstractTest { diff --git a/core/persistence-common/pom.xml b/core/persistence-common/pom.xml new file mode 100644 index 0000000000..c82278a4ee --- /dev/null +++ b/core/persistence-common/pom.xml @@ -0,0 +1,66 @@ + + + + + 4.0.0 + + + org.apache.syncope + syncope-core + 4.0.0-SNAPSHOT + + + Apache Syncope Core Persistence Common + Apache Syncope Core Persistence Common + org.apache.syncope.core + syncope-core-persistence-common + jar + + + ${basedir}/../.. + + + + + org.apache.syncope.core + syncope-core-spring + ${project.version} + + + + org.apache.tika + tika-core + + + + org.apache.commons + commons-jexl3 + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + + diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/AbstractDomainProperties.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/AbstractDomainProperties.java new file mode 100644 index 0000000000..f3f8b5f025 --- /dev/null +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/AbstractDomainProperties.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.persistence.common; + +import org.apache.syncope.common.lib.types.CipherAlgorithm; + +public abstract class AbstractDomainProperties { + + private String key; + + private String adminPassword; + + private CipherAlgorithm adminCipherAlgorithm = CipherAlgorithm.SHA512; + + private String content; + + private String keymasterConfParams; + + public String getKey() { + return key; + } + + public void setKey(final String key) { + this.key = key; + } + + public String getAdminPassword() { + return adminPassword; + } + + public void setAdminPassword(final String adminPassword) { + this.adminPassword = adminPassword; + } + + public CipherAlgorithm getAdminCipherAlgorithm() { + return adminCipherAlgorithm; + } + + public void setAdminCipherAlgorithm(final CipherAlgorithm adminCipherAlgorithm) { + this.adminCipherAlgorithm = adminCipherAlgorithm; + } + + public String getContent() { + return content == null + ? "classpath:domains/" + key + "Content.xml" + : content; + } + + public void setContent(final String content) { + this.content = content; + } + + public String getKeymasterConfParams() { + return keymasterConfParams == null + ? "classpath:domains/" + key + "KeymasterConfParams.json" + : keymasterConfParams; + } + + public void setKeymasterConfParams(final String keymasterConfParams) { + this.keymasterConfParams = keymasterConfParams; + } +} diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/AbstractPersistenceProperties.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/AbstractPersistenceProperties.java new file mode 100644 index 0000000000..ae4e26e866 --- /dev/null +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/AbstractPersistenceProperties.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.persistence.common; + +import java.util.ArrayList; +import java.util.List; +import org.springframework.boot.context.properties.NestedConfigurationProperty; + +public abstract class AbstractPersistenceProperties { + + private String indexesXML = "classpath:indexes.xml"; + + @NestedConfigurationProperty + private final List domain = new ArrayList<>(); + + public String getIndexesXML() { + return indexesXML; + } + + public void setIndexesXML(final String indexesXML) { + this.indexesXML = indexesXML; + } + + public List getDomain() { + return domain; + } +} diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/CommonPersistenceContext.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/CommonPersistenceContext.java new file mode 100644 index 0000000000..8725c08b32 --- /dev/null +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/CommonPersistenceContext.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 jakarta.validation.Validator; +import org.apache.syncope.common.keymaster.client.api.ConfParamOps; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; +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.UserDAO; +import org.apache.syncope.core.persistence.api.entity.AnyUtils; +import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.am.ClientAppUtilsFactory; +import org.apache.syncope.core.persistence.api.entity.policy.PolicyUtilsFactory; +import org.apache.syncope.core.persistence.api.search.SearchCondVisitor; +import org.apache.syncope.core.persistence.common.attrvalue.DefaultPlainAttrValidationManager; +import org.apache.syncope.core.persistence.common.content.KeymasterConfParamLoader; +import org.apache.syncope.core.persistence.common.entity.DefaultAnyUtils; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; + +@Configuration(proxyBeanMethods = false) +public class CommonPersistenceContext { + + @ConditionalOnMissingBean + @Bean + public SearchCondVisitor searchCondVisitor() { + return new SearchCondVisitor(); + } + + @Bean + public Validator localValidatorFactoryBean() { + return new LocalValidatorFactoryBean(); + } + + @ConditionalOnMissingBean + @Bean + public PlainAttrValidationManager plainAttrValidationManager() { + return new DefaultPlainAttrValidationManager(); + } + + @ConditionalOnMissingBean + @Bean + public PolicyUtilsFactory policyUtilsFactory() { + return new PolicyUtilsFactory(); + } + + @ConditionalOnMissingBean + @Bean + public ClientAppUtilsFactory clientAppUtilsFactory() { + return new ClientAppUtilsFactory(); + } + + @Bean(name = "userAnyUtils") + public AnyUtils userAnyUtils( + final @Lazy UserDAO userDAO, + final @Lazy GroupDAO groupDAO, + final @Lazy AnyObjectDAO anyObjectDAO, + final @Lazy EntityFactory entityFactory) { + + return new DefaultAnyUtils(userDAO, groupDAO, anyObjectDAO, entityFactory, AnyTypeKind.USER, false); + } + + @Bean(name = "linkedAccountAnyUtils") + public AnyUtils linkedAccountAnyUtils( + final @Lazy UserDAO userDAO, + final @Lazy GroupDAO groupDAO, + final @Lazy AnyObjectDAO anyObjectDAO, + final @Lazy EntityFactory entityFactory) { + + return new DefaultAnyUtils(userDAO, groupDAO, anyObjectDAO, entityFactory, AnyTypeKind.USER, true); + } + + @Bean(name = "groupAnyUtils") + public AnyUtils groupAnyUtils( + final @Lazy UserDAO userDAO, + final @Lazy GroupDAO groupDAO, + final @Lazy AnyObjectDAO anyObjectDAO, + final @Lazy EntityFactory entityFactory) { + + return new DefaultAnyUtils(userDAO, groupDAO, anyObjectDAO, entityFactory, AnyTypeKind.GROUP, false); + } + + @Bean(name = "anyObjectAnyUtils") + public AnyUtils anyObjectAnyUtils( + final @Lazy UserDAO userDAO, + final @Lazy GroupDAO groupDAO, + final @Lazy AnyObjectDAO anyObjectDAO, + final @Lazy EntityFactory entityFactory) { + + return new DefaultAnyUtils(userDAO, groupDAO, anyObjectDAO, entityFactory, AnyTypeKind.ANY_OBJECT, false); + } + + @ConditionalOnMissingBean + @Bean + public AnyUtilsFactory anyUtilsFactory( + @Qualifier("userAnyUtils") + final AnyUtils userAnyUtils, + @Qualifier("linkedAccountAnyUtils") + final AnyUtils linkedAccountAnyUtils, + @Qualifier("groupAnyUtils") + final AnyUtils groupAnyUtils, + @Qualifier("anyObjectAnyUtils") + final AnyUtils anyObjectAnyUtils) { + + return new AnyUtilsFactory(userAnyUtils, linkedAccountAnyUtils, groupAnyUtils, anyObjectAnyUtils); + } + + @ConditionalOnMissingBean + @Bean + public KeymasterConfParamLoader keymasterConfParamLoader(final ConfParamOps confParamOps) { + return new KeymasterConfParamLoader(confParamOps); + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/RuntimeDomainLoader.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/RuntimeDomainLoader.java similarity index 87% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/RuntimeDomainLoader.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/RuntimeDomainLoader.java index c0450e4b39..70854f703f 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/RuntimeDomainLoader.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/RuntimeDomainLoader.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa; +package org.apache.syncope.core.persistence.common; import java.util.Comparator; import org.apache.syncope.common.keymaster.client.api.DomainWatcher; @@ -24,7 +24,6 @@ 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.apache.syncope.core.persistence.jpa.spring.DomainRoutingEntityManagerFactory; import org.apache.syncope.core.spring.ApplicationContextProvider; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.slf4j.Logger; @@ -36,21 +35,17 @@ public class RuntimeDomainLoader implements DomainWatcher { protected static final Logger LOG = LoggerFactory.getLogger(RuntimeDomainLoader.class); - protected final DomainHolder domainHolder; + protected final DomainHolder domainHolder; protected final DomainRegistry domainRegistry; - protected final DomainRoutingEntityManagerFactory entityManagerFactory; - public RuntimeDomainLoader( - final DomainHolder domainHolder, + final DomainHolder domainHolder, final DomainRegistry domainRegistry, - final DomainRoutingEntityManagerFactory entityManagerFactory, final ConfigurableApplicationContext ctx) { this.domainHolder = domainHolder; this.domainRegistry = domainRegistry; - this.entityManagerFactory = entityManagerFactory; // only needed by ZookeeperDomainOps' early init on afterPropertiesSet if (ApplicationContextProvider.getApplicationContext() == null) { @@ -58,6 +53,10 @@ public RuntimeDomainLoader( } } + protected void onAdd(final Domain domain) { + // nothing to do + } + @Override public void added(final Domain domain) { if (domainHolder.getDomains().containsKey(domain.getKey())) { @@ -67,7 +66,7 @@ public void added(final Domain domain) { domainRegistry.register(domain); - AuthContextUtils.runAsAdmin(domain.getKey(), () -> entityManagerFactory.initJPASchema()); + onAdd(domain); ApplicationContextProvider.getBeanFactory().getBeansOfType(SyncopeCoreLoader.class).values(). stream().sorted(Comparator.comparing(SyncopeCoreLoader::getOrder)). @@ -78,7 +77,7 @@ public void added(final Domain domain) { AuthContextUtils.runAsAdmin( domain.getKey(), - () -> loader.load(domain.getKey(), domainHolder.getDomains().get(domain.getKey()))); + () -> loader.load(domain.getKey())); LOG.debug("[{}] Completed on domain '{}'", loaderName, domain); }); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/AbstractValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/AbstractValidator.java similarity index 90% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/AbstractValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/AbstractValidator.java index 4bcecbb353..da790204aa 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/AbstractValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/AbstractValidator.java @@ -16,10 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.attrvalue.validation; +package org.apache.syncope.core.persistence.common.attrvalue; import java.io.Serializable; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValueValidator; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValueValidator; import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.slf4j.Logger; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/AlwaysTrueValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/AlwaysTrueValidator.java similarity index 88% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/AlwaysTrueValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/AlwaysTrueValidator.java index 887eefc3df..7aad4d04ec 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/AlwaysTrueValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/AlwaysTrueValidator.java @@ -16,9 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.attrvalue.validation; +package org.apache.syncope.core.persistence.common.attrvalue; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidPlainAttrValueException; +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; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/BasicValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/BasicValidator.java similarity index 92% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/BasicValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/BasicValidator.java index 13840dabbe..1d5d1f3c6c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/BasicValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/BasicValidator.java @@ -16,11 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.attrvalue.validation; +package org.apache.syncope.core.persistence.common.attrvalue; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.AttrSchemaType; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidPlainAttrValueException; +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; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/BinaryValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/BinaryValidator.java similarity index 94% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/BinaryValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/BinaryValidator.java index 546c9d96df..90a689b4fc 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/BinaryValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/BinaryValidator.java @@ -16,12 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.attrvalue.validation; +package org.apache.syncope.core.persistence.common.attrvalue; import com.fasterxml.jackson.databind.json.JsonMapper; import jakarta.ws.rs.core.MediaType; import java.io.IOException; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidPlainAttrValueException; +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.apache.tika.Tika; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/DefaultPlainAttrValidationManager.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/DefaultPlainAttrValidationManager.java similarity index 90% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/DefaultPlainAttrValidationManager.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/DefaultPlainAttrValidationManager.java index cc901cd51a..7dcf20929c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/DefaultPlainAttrValidationManager.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/DefaultPlainAttrValidationManager.java @@ -16,12 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.attrvalue.validation; +package org.apache.syncope.core.persistence.common.attrvalue; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValueValidator; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValueValidator; 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; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/EmailAddressValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/EmailAddressValidator.java similarity index 90% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/EmailAddressValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/EmailAddressValidator.java index 3fa84390fa..9b7036e069 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/EmailAddressValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/EmailAddressValidator.java @@ -16,10 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.attrvalue.validation; +package org.apache.syncope.core.persistence.common.attrvalue; import java.util.regex.Matcher; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidPlainAttrValueException; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidPlainAttrValueException; import org.apache.syncope.core.persistence.api.entity.Entity; import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; import org.apache.syncope.core.persistence.api.entity.PlainSchema; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/URLValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/URLValidator.java similarity index 89% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/URLValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/URLValidator.java index 630eee9461..35618b933f 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/URLValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/URLValidator.java @@ -16,10 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.attrvalue.validation; +package org.apache.syncope.core.persistence.common.attrvalue; import java.net.URI; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidPlainAttrValueException; +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; diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/AbstractContentLoaderHandler.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/AbstractContentLoaderHandler.java new file mode 100644 index 0000000000..329b1aa939 --- /dev/null +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/AbstractContentLoaderHandler.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.core.persistence.common.content; + +import java.util.HashMap; +import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringSubstitutor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +public abstract class AbstractContentLoaderHandler extends DefaultHandler { + + protected static final Logger LOG = LoggerFactory.getLogger(AbstractContentLoaderHandler.class); + + private static final String CONF_DIR = "syncope.conf.dir"; + + private final String rootElement; + + protected final boolean continueOnError; + + protected final Map fetches = new HashMap<>(); + + protected final StringSubstitutor paramSubstitutor; + + protected AbstractContentLoaderHandler( + final String rootElement, + final boolean continueOnError, + final Environment env) { + + this.rootElement = rootElement; + this.continueOnError = continueOnError; + this.paramSubstitutor = new StringSubstitutor(key -> { + String value = env.getProperty(key, fetches.get(key)); + if (value != null && CONF_DIR.equals(key)) { + value = value.replace('\\', '/'); + } + return StringUtils.isBlank(value) ? null : value; + }); + } + + protected abstract void fetch(Attributes atts); + + protected abstract void create(String qName, Attributes atts); + + @Override + public void startElement(final String uri, final String localName, final String qName, final Attributes atts) + throws SAXException { + + // skip root element + if (rootElement.equals(qName)) { + return; + } + + if ("fetch".equalsIgnoreCase(qName)) { + fetch(atts); + } else { + create(qName, atts); + } + } +} diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/AbstractXMLContentExporter.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/AbstractXMLContentExporter.java new file mode 100644 index 0000000000..cd8cf7092e --- /dev/null +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/AbstractXMLContentExporter.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.persistence.common.content; + +import static org.apache.syncope.core.persistence.api.content.ContentDealer.ROOT_ELEMENT; + +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import javax.xml.XMLConstants; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.sax.TransformerHandler; +import javax.xml.transform.stream.StreamResult; +import org.apache.syncope.core.persistence.api.content.ContentExporter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +public abstract class AbstractXMLContentExporter implements ContentExporter { + + protected static final Logger LOG = LoggerFactory.getLogger(ContentExporter.class); + + protected TransformerHandler start(final OutputStream os) throws TransformerConfigurationException, SAXException { + StreamResult streamResult = new StreamResult(os); + + SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance(); + transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + + TransformerHandler handler = transformerFactory.newTransformerHandler(); + Transformer serializer = handler.getTransformer(); + serializer.setOutputProperty(OutputKeys.ENCODING, StandardCharsets.UTF_8.name()); + serializer.setOutputProperty(OutputKeys.INDENT, "yes"); + handler.setResult(streamResult); + handler.startDocument(); + handler.startElement("", "", ROOT_ELEMENT, new AttributesImpl()); + + return handler; + } + + protected void end(final TransformerHandler handler) throws SAXException { + handler.endElement("", "", ROOT_ELEMENT); + handler.endDocument(); + } +} diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/AbstractXMLContentLoader.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/AbstractXMLContentLoader.java new file mode 100644 index 0000000000..01acacee4a --- /dev/null +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/AbstractXMLContentLoader.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.common.content; + +import java.io.IOException; +import javax.xml.XMLConstants; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import org.apache.syncope.core.persistence.api.content.ContentLoader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; + +public abstract class AbstractXMLContentLoader implements ContentLoader { + + protected static final Logger LOG = LoggerFactory.getLogger(ContentLoader.class); + + protected final Environment env; + + protected AbstractXMLContentLoader(final Environment env) { + this.env = env; + } + + @Override + public int getOrder() { + return 400; + } + + protected abstract boolean existingData(String domain); + + protected abstract void createViews(String domain) throws IOException; + + protected abstract void createIndexes(String domain) throws IOException; + + protected SAXParser saxParser() + throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException, SAXException { + + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE); + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + return factory.newSAXParser(); + } + + protected abstract void loadDefaultContent(String domain, String contentXML) throws Exception; + + @Override + public void load(final String domain) { + LOG.debug("Loading data for domain [{}]", domain); + + if (existingData(domain)) { + LOG.info("[{}] Data found in the database, leaving untouched", domain); + } else { + LOG.info("[{}] Empty database found, loading default content", domain); + + try { + createViews(domain); + } catch (IOException e) { + LOG.error("[{}] While creating views", domain, e); + } + + try { + createIndexes(domain); + } catch (IOException e) { + LOG.error("[{}] While creating indexes", domain, e); + } + + try { + loadDefaultContent(domain, domain + "ContentXML"); + } catch (Exception e) { + LOG.error("[{}] While loading default content", domain, e); + } + } + } + +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/KeymasterConfParamLoader.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/KeymasterConfParamLoader.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/KeymasterConfParamLoader.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/KeymasterConfParamLoader.java index 0ee598824e..6cd7754d48 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/KeymasterConfParamLoader.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/KeymasterConfParamLoader.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.content; +package org.apache.syncope.core.persistence.common.content; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.json.JsonMapper; @@ -24,7 +24,6 @@ import java.io.InputStream; import java.util.Iterator; import java.util.Map; -import javax.sql.DataSource; import org.apache.syncope.common.keymaster.client.api.ConfParamOps; import org.apache.syncope.core.persistence.api.content.ConfParamLoader; import org.apache.syncope.core.spring.ApplicationContextProvider; @@ -52,7 +51,7 @@ public int getOrder() { } @Override - public void load(final String domain, final DataSource datasource) { + public void load(final String domain) { boolean existingData; try { existingData = !confParamOps.list(domain).isEmpty(); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/MultiParentNode.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/MultiParentNode.java similarity index 96% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/MultiParentNode.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/MultiParentNode.java index dbe1cf1720..accca6c70c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/MultiParentNode.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/MultiParentNode.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.content; +package org.apache.syncope.core.persistence.common.content; import java.util.HashSet; import java.util.Set; @@ -24,7 +24,7 @@ import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; -class MultiParentNode { +public class MultiParentNode { private final T object; @@ -34,7 +34,7 @@ class MultiParentNode { private boolean exploited = false; - MultiParentNode(final T object) { + public MultiParentNode(final T object) { this.object = object; } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/MultiParentNodeOp.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/MultiParentNodeOp.java similarity index 90% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/MultiParentNodeOp.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/MultiParentNodeOp.java index 46c6f53810..caf8d0f5df 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/MultiParentNodeOp.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/MultiParentNodeOp.java @@ -16,15 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.content; +package org.apache.syncope.core.persistence.common.content; import java.util.Collection; import java.util.Set; -final class MultiParentNodeOp { - - private MultiParentNodeOp() { - } +public final class MultiParentNodeOp { public static void traverseTree(final Set> roots, final Collection objects) { for (MultiParentNode root : roots) { @@ -45,4 +42,8 @@ public static void traverseTree(final MultiParentNode root, final Collect objects.add(root.getObject()); } } + + private MultiParentNodeOp() { + // private constructor for static utility class + } } diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnyMatchDAO.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnyMatchDAO.java new file mode 100644 index 0000000000..db97428db1 --- /dev/null +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnyMatchDAO.java @@ -0,0 +1,501 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.dao; + +import jakarta.validation.ValidationException; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import java.beans.PropertyDescriptor; +import java.lang.annotation.Annotation; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO; +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.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +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.dao.search.AnyCond; +import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond; +import org.apache.syncope.core.persistence.api.dao.search.AttrCond; +import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond; +import org.apache.syncope.core.persistence.api.dao.search.MemberCond; +import org.apache.syncope.core.persistence.api.dao.search.MembershipCond; +import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond; +import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond; +import org.apache.syncope.core.persistence.api.dao.search.ResourceCond; +import org.apache.syncope.core.persistence.api.dao.search.RoleCond; +import org.apache.syncope.core.persistence.api.dao.search.SearchCond; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.AnyUtils; +import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.GroupableRelatable; +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.anyobject.AnyObject; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeanUtils; +import org.springframework.transaction.annotation.Transactional; + +public abstract class AbstractAnyMatchDAO implements AnyMatchDAO { + + protected static final Logger LOG = LoggerFactory.getLogger(AnyMatchDAO.class); + + protected final UserDAO userDAO; + + protected final GroupDAO groupDAO; + + protected final AnyObjectDAO anyObjectDAO; + + protected final RealmDAO realmDAO; + + protected final PlainSchemaDAO plainSchemaDAO; + + protected final AnyUtilsFactory anyUtilsFactory; + + protected final PlainAttrValidationManager validator; + + protected final EntityFactory entityFactory; + + public AbstractAnyMatchDAO( + final UserDAO userDAO, + final GroupDAO groupDAO, + final AnyObjectDAO anyObjectDAO, + final RealmDAO realmDAO, + final PlainSchemaDAO plainSchemaDAO, + final AnyUtilsFactory anyUtilsFactory, + final PlainAttrValidationManager validator, + final EntityFactory entityFactory) { + + this.userDAO = userDAO; + this.groupDAO = groupDAO; + this.anyObjectDAO = anyObjectDAO; + this.realmDAO = realmDAO; + this.plainSchemaDAO = plainSchemaDAO; + this.anyUtilsFactory = anyUtilsFactory; + this.validator = validator; + this.entityFactory = entityFactory; + } + + /** + * Verify if any matches the given search condition. + * + * @param any to be checked + * @param cond to be verified + * @param any + * @return true if any matches cond + */ + @Transactional(readOnly = true) + @Override + public > boolean matches(final T any, final SearchCond cond) { + boolean not = cond.getType() == SearchCond.Type.NOT_LEAF; + switch (cond.getType()) { + case LEAF, NOT_LEAF -> { + Boolean match = cond.getLeaf(AnyTypeCond.class). + filter(leaf -> AnyTypeKind.ANY_OBJECT == any.getType().getKind()). + map(leaf -> matches(any, leaf, not)). + orElse(null); + + if (match == null) { + match = cond.getLeaf(RelationshipTypeCond.class). + filter(leaf -> any instanceof GroupableRelatable). + map(leaf -> matches((GroupableRelatable) any, leaf, not)). + orElse(null); + } + + if (match == null) { + match = cond.getLeaf(RelationshipCond.class). + filter(leaf -> any instanceof GroupableRelatable). + map(leaf -> matches((GroupableRelatable) any, leaf, not)). + orElse(null); + } + + if (match == null) { + match = cond.getLeaf(MembershipCond.class). + filter(leaf -> any instanceof GroupableRelatable). + map(leaf -> matches((GroupableRelatable) any, leaf, not)). + orElse(null); + } + + if (match == null) { + match = cond.getLeaf(RoleCond.class). + filter(leaf -> any instanceof User). + map(leaf -> matches((User) any, leaf, not)). + orElse(null); + } + + if (match == null) { + match = cond.getLeaf(DynRealmCond.class). + map(leaf -> matches(any, leaf, not)). + orElse(null); + } + + if (match == null) { + match = cond.getLeaf(MemberCond.class). + filter(leaf -> any instanceof Group). + map(leaf -> matches((Group) any, leaf, not)). + orElse(null); + } + + if (match == null) { + match = cond.getLeaf(ResourceCond.class). + map(leaf -> matches(any, leaf, not)). + orElse(null); + } + + if (match == null) { + match = cond.getLeaf(AnyCond.class). + map(value -> matches(any, value, not)). + orElseGet(() -> cond.getLeaf(AttrCond.class). + map(leaf -> matches(any, leaf, not)). + orElse(null)); + } + + if (match == null) { + match = cond.getLeaf(AttrCond.class). + map(leaf -> matches(any, leaf, not)). + orElse(null); + } + + return BooleanUtils.toBoolean(match); + } + case AND -> { + return matches(any, cond.getLeft()) && matches(any, cond.getRight()); + } + + case OR -> { + return matches(any, cond.getLeft()) || matches(any, cond.getRight()); + } + + default -> { + } + } + + return false; + } + + protected boolean matches(final Any any, final AnyTypeCond cond, final boolean not) { + boolean equals = any.getType().getKey().equals(cond.getAnyTypeKey()); + return not ? !equals : equals; + } + + protected boolean matches( + final GroupableRelatable any, final RelationshipTypeCond cond, final boolean not) { + + boolean found = any.getRelationships().stream(). + anyMatch(rel -> rel.getType().getKey().equals(cond.getRelationshipTypeKey())); + return not ? !found : found; + } + + protected boolean matches( + final GroupableRelatable any, final RelationshipCond cond, final boolean not) { + + Set candidates = SyncopeConstants.UUID_PATTERN.matcher(cond.getAnyObject()).matches() + ? Set.of(cond.getAnyObject()) + : anyObjectDAO.findByName(cond.getAnyObject()).stream(). + map(AnyObject::getKey).collect(Collectors.toSet()); + + boolean found = any.getRelationships().stream(). + map(r -> r.getRightEnd().getKey()). + filter(candidates::contains). + count() > 0; + + return not ? !found : found; + } + + protected boolean matches( + final GroupableRelatable any, final MembershipCond cond, final boolean not) { + + final String group = SyncopeConstants.UUID_PATTERN.matcher(cond.getGroup()).matches() + ? cond.getGroup() + : groupDAO.findKey(cond.getGroup()). + orElseThrow(() -> new NotFoundException("Group " + cond.getGroup())); + + boolean found = any.getMembership(group).isPresent() + || (any instanceof User + ? userDAO.findDynGroups(any.getKey()) + : anyObjectDAO.findDynGroups(any.getKey())).stream(). + anyMatch(item -> item.getKey().equals(group)); + return not ? !found : found; + } + + protected boolean matches(final User user, final RoleCond cond, final boolean not) { + boolean found = userDAO.findAllRoles(user).stream().anyMatch(role -> role.getKey().equals(cond.getRole())); + return not ? !found : found; + } + + protected boolean matches(final Any any, final DynRealmCond cond, final boolean not) { + boolean found = anyUtilsFactory.getInstance(any).dao().findDynRealms(any.getKey()).stream(). + anyMatch(dynRealm -> dynRealm.equals(cond.getDynRealm())); + return not ? !found : found; + } + + protected boolean matches(final Group group, final MemberCond cond, final boolean not) { + boolean found = false; + + GroupableRelatable any = userDAO.findById(cond.getMember()).orElse(null); + if (any == null) { + any = anyObjectDAO.findById(cond.getMember()).orElse(null); + if (any != null) { + found = groupDAO.findAMemberships(group).stream(). + anyMatch(memb -> memb.getLeftEnd().getKey().equals(cond.getMember())) + || groupDAO.findADynMembers(group).contains(cond.getMember()); + } + } else { + found = groupDAO.findUMemberships(group).stream(). + anyMatch(memb -> memb.getLeftEnd().getKey().equals(cond.getMember())) + || groupDAO.findUDynMembers(group).contains(cond.getMember()); + } + + return not ? !found : found; + } + + protected boolean matches(final Any any, final ResourceCond cond, final boolean not) { + boolean found = anyUtilsFactory.getInstance(any).getAllResources(any).stream(). + anyMatch(resource -> resource.getKey().equals(cond.getResource())); + return not ? !found : found; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + protected boolean matches( + final List anyAttrValues, + final PlainAttrValue attrValue, + final PlainSchema schema, + final AttrCond cond) { + + return anyAttrValues.stream().anyMatch(item -> { + switch (cond.getType()) { + case EQ -> { + return attrValue.getValue().equals(item.getValue()); + } + + case IEQ -> { + if (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum) { + return attrValue.getStringValue().equalsIgnoreCase(item.getStringValue()); + } else { + LOG.error("IEQ is only compatible with string or enum schemas"); + return false; + } + } + + case LIKE, ILIKE -> { + if (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum) { + StringBuilder output = new StringBuilder(); + for (char c : cond.getExpression().toLowerCase().toCharArray()) { + if (c == '%') { + output.append(".*"); + } else if (Character.isLetter(c)) { + output.append('['). + append(c). + append(Character.toUpperCase(c)). + append(']'); + } else { + output.append(c); + } + } + return (cond.getType() == AttrCond.Type.LIKE + ? Pattern.compile(output.toString()) + : Pattern.compile(output.toString(), Pattern.CASE_INSENSITIVE)). + matcher(item.getStringValue()).matches(); + } else { + LOG.error("LIKE is only compatible with string or enum schemas"); + return false; + } + } + case GT -> { + return item.getValue().compareTo(attrValue.getValue()) > 0; + } + + case GE -> { + return item.getValue().compareTo(attrValue.getValue()) >= 0; + } + + case LT -> { + return item.getValue().compareTo(attrValue.getValue()) < 0; + } + + case LE -> { + return item.getValue().compareTo(attrValue.getValue()) <= 0; + } + + default -> { + return false; + } + } + }); + } + + protected boolean matches(final Any any, final AttrCond cond, final boolean not) { + PlainSchema schema = plainSchemaDAO.findById(cond.getSchema()).orElse(null); + if (schema == null) { + LOG.warn("Ignoring invalid schema '{}'", cond.getSchema()); + return false; + } + + @SuppressWarnings("unchecked") + Optional> attr = (Optional>) any.getPlainAttr(cond.getSchema()); + + boolean found; + switch (cond.getType()) { + case ISNULL: + found = attr.isEmpty(); + break; + + case ISNOTNULL: + found = attr.isPresent(); + break; + + default: + PlainAttrValue attrValue = anyUtilsFactory.getInstance(any).newPlainAttrValue(); + try { + if (cond.getType() != AttrCond.Type.LIKE + && cond.getType() != AttrCond.Type.ILIKE + && cond.getType() != AttrCond.Type.ISNULL + && cond.getType() != AttrCond.Type.ISNOTNULL) { + + validator.validate(schema, cond.getExpression(), attrValue); + } + } catch (ValidationException e) { + LOG.error("Could not validate expression '" + cond.getExpression() + '\'', e); + return false; + } + + found = attr.map(a -> matches(a.getValues(), attrValue, schema, cond)).orElse(false); + } + return not ? !found : found; + } + + protected abstract void relationshipFieldMatches(PropertyDescriptor pd, AnyCond cond, PlainSchema schema); + + protected boolean matches(final Any any, final AnyCond cond, final boolean not) { + // Keeps track of difference between entity's getKey() and @Id fields + if ("key".equals(cond.getSchema())) { + cond.setSchema("id"); + } + + PropertyDescriptor pd; + Object anyAttrValue; + try { + pd = BeanUtils.getPropertyDescriptor(any.getClass(), cond.getSchema()); + if (pd == null) { + LOG.warn("Ignoring invalid schema '{}'", cond.getSchema()); + return false; + } + + anyAttrValue = pd.getReadMethod().invoke(any); + } catch (Exception e) { + LOG.error("While accessing {}.{}", any, cond.getSchema(), e); + return false; + } + + boolean found; + switch (cond.getType()) { + case ISNULL: + found = anyAttrValue == null; + break; + + case ISNOTNULL: + found = anyAttrValue != null; + break; + + default: + PlainSchema schema = entityFactory.newEntity(PlainSchema.class); + schema.setKey(pd.getName()); + for (AttrSchemaType attrSchemaType : AttrSchemaType.values()) { + if (pd.getPropertyType().isAssignableFrom(attrSchemaType.getType())) { + schema.setType(attrSchemaType); + } + } + + // Deal with any Integer fields logically mapping to boolean values + boolean foundBooleanMin = false; + boolean foundBooleanMax = false; + if (Integer.class.equals(pd.getPropertyType())) { + for (Annotation annotation : pd.getPropertyType().getAnnotations()) { + if (Min.class.equals(annotation.annotationType())) { + foundBooleanMin = ((Min) annotation).value() == 0; + } else if (Max.class.equals(annotation.annotationType())) { + foundBooleanMax = ((Max) annotation).value() == 1; + } + } + } + if (foundBooleanMin && foundBooleanMax) { + schema.setType(AttrSchemaType.Boolean); + } + + // Deal with any fields representing relationships to other entities + relationshipFieldMatches(pd, cond, schema); + + AnyUtils anyUtils = anyUtilsFactory.getInstance(any); + + PlainAttrValue attrValue = anyUtils.newPlainAttrValue(); + if (cond.getType() != AttrCond.Type.LIKE + && cond.getType() != AttrCond.Type.ILIKE + && cond.getType() != AttrCond.Type.ISNULL + && cond.getType() != AttrCond.Type.ISNOTNULL) { + + try { + validator.validate(schema, cond.getExpression(), attrValue); + } catch (ValidationException e) { + LOG.error("Could not validate expression '" + cond.getExpression() + '\'', e); + return false; + } + } + + List anyAttrValues = new ArrayList<>(); + anyAttrValues.add(anyUtils.newPlainAttrValue()); + switch (anyAttrValue) { + case String aString -> + anyAttrValues.get(0).setStringValue(aString); + case Long aLong -> + anyAttrValues.get(0).setLongValue(aLong); + case Double aDouble -> + anyAttrValues.get(0).setDoubleValue(aDouble); + case Boolean aBoolean -> + anyAttrValues.get(0).setBooleanValue(aBoolean); + case OffsetDateTime offsetDateTime -> + anyAttrValues.get(0).setDateValue(offsetDateTime); + case byte[] bytea -> + anyAttrValues.get(0).setBinaryValue(bytea); + default -> { + } + } + + found = matches(anyAttrValues, attrValue, schema, cond); + + } + return not ? !found : found; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnySearchDAO.java similarity index 92% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnySearchDAO.java index 5dd012e533..b37c72319e 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnySearchDAO.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao; +package org.apache.syncope.core.persistence.common.dao; import jakarta.validation.ValidationException; import jakarta.validation.constraints.Max; @@ -34,7 +34,7 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.AttrSchemaType; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; 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.DynRealmDAO; @@ -114,6 +114,36 @@ protected static SearchCond buildEffectiveCond( return SearchCond.getAnd(result); } + protected static String key(final AttrSchemaType schemaType) { + String key; + switch (schemaType) { + case Boolean: + key = "booleanValue"; + break; + + case Date: + key = "dateValue"; + break; + + case Double: + key = "doubleValue"; + break; + + case Long: + key = "longValue"; + break; + + case Binary: + key = "binaryValue"; + break; + + default: + key = "stringValue"; + } + + return key; + } + protected final RealmDAO realmDAO; protected final DynRealmDAO dynRealmDAO; @@ -154,11 +184,11 @@ public AbstractAnySearchDAO( this.validator = validator; } - protected abstract int doCount( + protected abstract long doCount( Realm base, boolean recursive, Set adminRealms, SearchCond cond, AnyTypeKind kind); @Override - public int count( + public long count( final Realm base, final boolean recursive, final Set adminRealms, @@ -236,10 +266,9 @@ protected Triple check(final AnyCond cond, AnyUtils anyUtils = anyUtilsFactory.getInstance(kind); - Field anyField = anyUtils.getField(computed.getSchema()); - if (anyField == null) { - throw new IllegalArgumentException("Invalid schema " + computed.getSchema()); - } + Field anyField = anyUtils.getField(computed.getSchema()). + orElseThrow(() -> new IllegalArgumentException("Invalid schema " + computed.getSchema())); + // Keeps track of difference between entity's getKey() and JPA @Id fields if ("key".equals(computed.getSchema())) { computed.setSchema("id"); @@ -291,12 +320,16 @@ protected Triple check(final AnyCond cond, return Triple.of(schema, attrValue, computed); } + protected boolean isPatternMatch(final String clause) { + return clause.indexOf('%') != -1; + } + protected List check(final MembershipCond cond) { List groups = SyncopeConstants.UUID_PATTERN.matcher(cond.getGroup()).matches() ? List.of(cond.getGroup()) - : cond.getGroup().indexOf('%') == -1 - ? groupDAO.findKey(cond.getGroup()).map(List::of).orElseGet(List::of) - : groupDAO.findKeysByNamePattern(cond.getGroup().toLowerCase()); + : isPatternMatch(cond.getGroup()) + ? groupDAO.findKeysByNamePattern(cond.getGroup().toLowerCase()) + : groupDAO.findKey(cond.getGroup()).map(List::of).orElseGet(List::of); if (groups.isEmpty()) { throw new IllegalArgumentException("Could not find group(s) for " + cond.getGroup()); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtils.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/entity/DefaultAnyUtils.java similarity index 89% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtils.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/entity/DefaultAnyUtils.java index 8a1b4c7b79..8ae01b937c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtils.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/entity/DefaultAnyUtils.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.entity; +package org.apache.syncope.core.persistence.common.entity; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -25,6 +25,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import org.apache.commons.lang3.ClassUtils; import org.apache.syncope.common.lib.request.AnyCR; @@ -40,8 +41,8 @@ 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.attrvalue.validation.InvalidPlainAttrValueException; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidPlainAttrValueException; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; 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; @@ -71,30 +72,15 @@ import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrUniqueValue; import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrValue; import org.apache.syncope.core.persistence.api.entity.user.User; -import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject; -import org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup; -import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.transaction.annotation.Transactional; @SuppressWarnings({ "unchecked", "rawtypes" }) -public class JPAAnyUtils implements AnyUtils { +public class DefaultAnyUtils implements AnyUtils { protected static final Logger LOG = LoggerFactory.getLogger(AnyUtils.class); - protected static final Map USER_FIELDS = new HashMap<>(); - - protected static final Map GROUP_FIELDS = new HashMap<>(); - - protected static final Map ANY_OBJECT_FIELDS = new HashMap<>(); - - static { - initFieldNames(JPAUser.class, USER_FIELDS); - initFieldNames(JPAGroup.class, GROUP_FIELDS); - initFieldNames(JPAAnyObject.class, ANY_OBJECT_FIELDS); - } - protected static void initFieldNames(final Class entityClass, final Map fields) { List> classes = ClassUtils.getAllSuperclasses(entityClass); classes.add(entityClass); @@ -114,12 +100,6 @@ protected static void initFieldNames(final Class entityClass, final Map userFields = new HashMap<>(); + + protected final Map groupFields = new HashMap<>(); + + protected final Map anyObjectFields = new HashMap<>(); + + public DefaultAnyUtils( final UserDAO userDAO, final GroupDAO groupDAO, final AnyObjectDAO anyObjectDAO, @@ -146,6 +132,10 @@ public JPAAnyUtils( this.entityFactory = entityFactory; this.anyTypeKind = anyTypeKind; this.linkedAccount = linkedAccount; + + initFieldNames(entityFactory.userClass(), userFields); + initFieldNames(entityFactory.groupClass(), groupFields); + initFieldNames(entityFactory.anyObjectClass(), anyObjectFields); } @Override @@ -175,25 +165,25 @@ public > Class anyClass() { } @Override - public Field getField(final String name) { + public Optional getField(final String name) { Map fields; switch (anyTypeKind) { case GROUP: - fields = GROUP_FIELDS; + fields = groupFields; break; case ANY_OBJECT: - fields = ANY_OBJECT_FIELDS; + fields = anyObjectFields; break; case USER: default: - fields = USER_FIELDS; + fields = userFields; break; } - return fields.get(name); + return Optional.ofNullable(fields.get(name)); } @Override @@ -260,12 +250,12 @@ public T newPlainAttrValue() { @Override @SuppressWarnings("unchecked") - public Class plainAttrUniqueValueClass() { + public Class plainAttrUniqueValueClass() { return (Class) newPlainAttrUniqueValue().getClass(); } @Override - public T newPlainAttrUniqueValue() { + public T newPlainAttrUniqueValue() { T result = null; switch (anyTypeKind) { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AbstractValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AbstractValidator.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AbstractValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AbstractValidator.java index c8fff7f0cb..d996c57fb3 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AbstractValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AbstractValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidator; import java.lang.annotation.Annotation; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyCheck.java index eca882a07c..39ce7678c2 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyObjectCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyObjectCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyObjectCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyObjectCheck.java index e3338f352b..3bfcb219a3 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyObjectCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyObjectCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyObjectValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyObjectValidator.java similarity index 96% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyObjectValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyObjectValidator.java index 531cc15f89..4283d777c2 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyObjectValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyObjectValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.types.EntityViolationType; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyTypeCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyTypeCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyTypeCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyTypeCheck.java index ad40c95d7c..786ce5c47d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyTypeCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyTypeCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyTypeClassCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyTypeClassCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyTypeClassCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyTypeClassCheck.java index 1a235e6ddb..9a08bc7ec8 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyTypeClassCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyTypeClassCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyTypeClassValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyTypeClassValidator.java similarity index 96% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyTypeClassValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyTypeClassValidator.java index 179b52cb87..f45fd026b7 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyTypeClassValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyTypeClassValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.types.EntityViolationType; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyTypeValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyTypeValidator.java similarity index 97% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyTypeValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyTypeValidator.java index 0472ec8aae..ef2f2ccebe 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyTypeValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyTypeValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.SyncopeConstants; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyValidator.java similarity index 98% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyValidator.java index 095c3b3fd9..a01f9154cf 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/AnyValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/AnyValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.types.EntityViolationType; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ApplicationCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ApplicationCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ApplicationCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ApplicationCheck.java index 640b21a1e2..d5588bc7c5 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ApplicationCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ApplicationCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ApplicationValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ApplicationValidator.java similarity index 96% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ApplicationValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ApplicationValidator.java index d59a213d0d..8cd34d8b35 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ApplicationValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ApplicationValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.types.EntityViolationType; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ConnInstanceCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ConnInstanceCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ConnInstanceCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ConnInstanceCheck.java index a612b2ed4e..beb8046b72 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ConnInstanceCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ConnInstanceCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ConnInstanceValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ConnInstanceValidator.java similarity index 93% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ConnInstanceValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ConnInstanceValidator.java index 587e7e940b..9672963836 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ConnInstanceValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ConnInstanceValidator.java @@ -16,13 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.types.EntityViolationType; import org.apache.syncope.core.persistence.api.entity.ConnInstance; -import org.apache.syncope.core.provisioning.api.utils.ConnPoolConfUtils; -import org.apache.syncope.core.provisioning.api.utils.URIUtils; +import org.apache.syncope.core.persistence.api.utils.ConnPoolConfUtils; +import org.apache.syncope.core.persistence.api.utils.URIUtils; public class ConnInstanceValidator extends AbstractValidator { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DelegationCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/DelegationCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DelegationCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/DelegationCheck.java index 6daced68cb..929749acba 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DelegationCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/DelegationCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DelegationValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/DelegationValidator.java similarity index 97% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DelegationValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/DelegationValidator.java index 7cc9c609bd..5fc92b87db 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DelegationValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/DelegationValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.types.EntityViolationType; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/DynRealmCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/DynRealmCheck.java index 0360988f32..9f3beb0e7c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/DynRealmCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/DynRealmValidator.java similarity index 96% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/DynRealmValidator.java index ceffafbcde..c926147a09 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/DynRealmValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/DynRealmValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import java.util.regex.Pattern; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ExternalResourceCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ExternalResourceCheck.java index 5b080625ba..04a92df9ed 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ExternalResourceCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ExternalResourceValidator.java similarity index 98% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ExternalResourceValidator.java index d3a786a5ed..1ac1a96fe3 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ExternalResourceValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ExternalResourceValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import java.util.HashSet; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/GroupCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/GroupCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/GroupCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/GroupCheck.java index c805f05dd5..4e3bd4b696 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/GroupCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/GroupCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/GroupValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/GroupValidator.java similarity index 98% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/GroupValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/GroupValidator.java index c176f0069c..ca993f9406 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/GroupValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/GroupValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import java.util.HashSet; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ImplementationCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ImplementationCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ImplementationCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ImplementationCheck.java index 9ae84af36d..6f857d688a 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ImplementationCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ImplementationCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ImplementationValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ImplementationValidator.java similarity index 82% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ImplementationValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ImplementationValidator.java index 071b7674c6..915543d700 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ImplementationValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ImplementationValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.types.EntityViolationType; @@ -26,12 +26,12 @@ public class ImplementationValidator extends AbstractValidator { @Override - public boolean isValid(final Implementation resource, final ConstraintValidatorContext context) { + public boolean isValid(final Implementation implementation, final ConstraintValidatorContext context) { context.disableDefaultConstraintViolation(); - if (resource.getKey() == null || !Entity.ID_PATTERN.matcher(resource.getKey()).matches()) { + if (implementation.getKey() == null || !Entity.ID_PATTERN.matcher(implementation.getKey()).matches()) { context.buildConstraintViolationWithTemplate( - getTemplate(EntityViolationType.InvalidKey, resource.getKey())). + getTemplate(EntityViolationType.InvalidKey, implementation.getKey())). addPropertyNode("key").addConstraintViolation(); return false; } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainAttrCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainAttrCheck.java similarity index 91% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainAttrCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainAttrCheck.java index 67ebf01fe5..3776dafc4c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainAttrCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainAttrCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; @@ -28,7 +28,7 @@ @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) -@Constraint(validatedBy = JPAPlainAttrValidator.class) +@Constraint(validatedBy = PlainAttrValidator.class) @Documented public @interface PlainAttrCheck { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/JPAPlainAttrValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainAttrValidator.java similarity index 93% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/JPAPlainAttrValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainAttrValidator.java index 3e6d0597c2..2bc8728680 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/JPAPlainAttrValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainAttrValidator.java @@ -16,13 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.types.EntityViolationType; import org.apache.syncope.core.persistence.api.entity.PlainAttr; -public class JPAPlainAttrValidator extends AbstractValidator> { +public class PlainAttrValidator extends AbstractValidator> { @Override public boolean isValid(final PlainAttr attr, final ConstraintValidatorContext context) { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainAttrValueCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainAttrValueCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainAttrValueCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainAttrValueCheck.java index e01ad1b6f8..d54561ab34 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainAttrValueCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainAttrValueCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainAttrValueValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainAttrValueValidator.java similarity index 93% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainAttrValueValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainAttrValueValidator.java index c25ca4ca7e..054d540eb2 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainAttrValueValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainAttrValueValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.types.EntityViolationType; @@ -63,8 +63,8 @@ public boolean isValid(final PlainAttrValue value, final ConstraintValidatorCont addPropertyNode(value.getClass().getSimpleName().replaceAll("\\n", " ")). addConstraintViolation(); - } else if (value instanceof PlainAttrUniqueValue) { - PlainSchema uniqueValueSchema = ((PlainAttrUniqueValue) value).getSchema(); + } else if (value instanceof PlainAttrUniqueValue plainAttrUniqueValue) { + PlainSchema uniqueValueSchema = plainAttrUniqueValue.getSchema(); PlainSchema attrSchema = value.getAttr().getSchema(); isValid = uniqueValueSchema.equals(attrSchema); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainSchemaCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainSchemaCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainSchemaCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainSchemaCheck.java index 70a1c18dad..510121e7ed 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainSchemaCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainSchemaCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainSchemaValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainSchemaValidator.java similarity index 97% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainSchemaValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainSchemaValidator.java index 159f740471..233bba341c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PlainSchemaValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PlainSchemaValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.commons.lang3.StringUtils; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PolicyCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PolicyCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PolicyCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PolicyCheck.java index 69164e8bcc..b4e26babd2 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PolicyCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PolicyCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PolicyValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PolicyValidator.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PolicyValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PolicyValidator.java index eca6cb90b2..e1c6349ed6 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PolicyValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PolicyValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.types.EntityViolationType; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PrivilegeCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PrivilegeCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PrivilegeCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PrivilegeCheck.java index f40b8681d3..7cd877c933 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PrivilegeCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PrivilegeCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PrivilegeValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PrivilegeValidator.java similarity index 96% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PrivilegeValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PrivilegeValidator.java index e673146754..b6bda00450 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PrivilegeValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PrivilegeValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.types.EntityViolationType; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PropagationTaskCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PropagationTaskCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PropagationTaskCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PropagationTaskCheck.java index 9a1742eb5a..732abf32f5 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PropagationTaskCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PropagationTaskCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PropagationTaskValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PropagationTaskValidator.java similarity index 97% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PropagationTaskValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PropagationTaskValidator.java index 52dcf72aaf..507fce9f5f 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PropagationTaskValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/PropagationTaskValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.types.EntityViolationType; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ProvisioningTaskCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ProvisioningTaskCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ProvisioningTaskCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ProvisioningTaskCheck.java index 1598495474..aff2a0a9fe 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ProvisioningTaskCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ProvisioningTaskCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ProvisioningTaskValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ProvisioningTaskValidator.java similarity index 96% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ProvisioningTaskValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ProvisioningTaskValidator.java index 4df3933f4a..42890c7af7 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ProvisioningTaskValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ProvisioningTaskValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.types.EntityViolationType; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RealmCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RealmCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RealmCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RealmCheck.java index 42bfb21379..c5dccb2329 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RealmCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RealmCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RealmValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RealmValidator.java similarity index 97% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RealmValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RealmValidator.java index a2dd0e1328..212d5df560 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RealmValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RealmValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.SyncopeConstants; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RelationshipTypeCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RelationshipTypeCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RelationshipTypeCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RelationshipTypeCheck.java index c087e82fcd..dcb44aa3b9 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RelationshipTypeCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RelationshipTypeCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RelationshipTypeValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RelationshipTypeValidator.java similarity index 96% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RelationshipTypeValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RelationshipTypeValidator.java index 0be16edb6d..e1b8058931 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RelationshipTypeValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RelationshipTypeValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.types.EntityViolationType; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RemediationCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RemediationCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RemediationCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RemediationCheck.java index a506b8c023..bf4340a1f7 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RemediationCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RemediationCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RemediationValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RemediationValidator.java similarity index 98% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RemediationValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RemediationValidator.java index 1eae333527..8f8a595847 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RemediationValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RemediationValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.SyncopeConstants; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ReportCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ReportCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ReportCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ReportCheck.java index d64190b285..0bc997a6a0 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ReportCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ReportCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ReportValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ReportValidator.java similarity index 89% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ReportValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ReportValidator.java index bd482b4396..3a119f48cf 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ReportValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/ReportValidator.java @@ -16,13 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; -import java.text.ParseException; import org.apache.syncope.common.lib.types.EntityViolationType; import org.apache.syncope.core.persistence.api.entity.Report; -import org.quartz.CronExpression; +import org.springframework.scheduling.support.CronExpression; public class ReportValidator extends AbstractValidator { @@ -43,8 +42,8 @@ public boolean isValid(final Report report, final ConstraintValidatorContext con if (report.getCronExpression() != null) { try { - new CronExpression(report.getCronExpression()); - } catch (ParseException e) { + CronExpression.parse(report.getCronExpression()); + } catch (IllegalArgumentException e) { LOG.error("Invalid cron expression '" + report.getCronExpression() + '\'', e); isValid = false; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RoleCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RoleCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RoleCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RoleCheck.java index ad1a3f284e..c95302f9d8 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RoleCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RoleCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RoleValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RoleValidator.java similarity index 88% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RoleValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RoleValidator.java index 506a64e96a..7a8e279982 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/RoleValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/RoleValidator.java @@ -16,13 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.types.EntityViolationType; +import org.apache.syncope.core.persistence.api.dao.RoleDAO; import org.apache.syncope.core.persistence.api.entity.Entity; import org.apache.syncope.core.persistence.api.entity.Role; -import org.apache.syncope.core.spring.security.AuthDataAccessor; public class RoleValidator extends AbstractValidator { @@ -31,7 +31,7 @@ public boolean isValid(final Role role, final ConstraintValidatorContext context context.disableDefaultConstraintViolation(); if (role.getKey() == null - || (!AuthDataAccessor.GROUP_OWNER_ROLE.equals(role.getKey()) + || (!RoleDAO.GROUP_OWNER_ROLE.equals(role.getKey()) && !Entity.ID_PATTERN.matcher(role.getKey()).matches())) { context.buildConstraintViolationWithTemplate( diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SRARouteCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/SRARouteCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SRARouteCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/SRARouteCheck.java index aeafdfa86a..659765a2b1 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SRARouteCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/SRARouteCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SRARouteValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/SRARouteValidator.java similarity index 97% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SRARouteValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/SRARouteValidator.java index 97c468c351..54c4d9bf9e 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SRARouteValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/SRARouteValidator.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.types.EntityViolationType; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchedTaskCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/SchedTaskCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchedTaskCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/SchedTaskCheck.java index 4c2c037f2c..7510fa33ff 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchedTaskCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/SchedTaskCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchedTaskValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/SchedTaskValidator.java similarity index 88% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchedTaskValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/SchedTaskValidator.java index 331a85ec9b..45fe4e8d95 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchedTaskValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/SchedTaskValidator.java @@ -16,13 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; -import java.text.ParseException; import org.apache.syncope.common.lib.types.EntityViolationType; import org.apache.syncope.core.persistence.api.entity.task.SchedTask; -import org.quartz.CronExpression; +import org.springframework.scheduling.support.CronExpression; public class SchedTaskValidator extends AbstractValidator { @@ -31,8 +30,8 @@ public boolean isValid(final SchedTask task, final ConstraintValidatorContext co boolean isValid = true; if (task.getCronExpression() != null) { try { - new CronExpression(task.getCronExpression()); - } catch (ParseException e) { + CronExpression.parse(task.getCronExpression()); + } catch (IllegalArgumentException e) { LOG.error("Invalid cron expression '" + task.getCronExpression() + '\'', e); isValid = false; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchemaKeyCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/SchemaKeyCheck.java similarity index 95% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchemaKeyCheck.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/SchemaKeyCheck.java index c4d54ac5ea..5cfbdb711b 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchemaKeyCheck.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/SchemaKeyCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchemaKeyValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/SchemaKeyValidator.java similarity index 70% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchemaKeyValidator.java rename to core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/SchemaKeyValidator.java index db025bd517..f3d0d628a9 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchemaKeyValidator.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/SchemaKeyValidator.java @@ -16,19 +16,36 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.common.validation; import jakarta.validation.ConstraintValidatorContext; +import java.beans.PropertyDescriptor; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; import org.apache.syncope.common.lib.types.EntityViolationType; import org.apache.syncope.core.persistence.api.entity.Entity; import org.apache.syncope.core.persistence.api.entity.Schema; -import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtils; +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.springframework.beans.BeanUtils; public class SchemaKeyValidator extends AbstractValidator { + protected static final Set ANY_FIELDS = new HashSet<>(); + + static { + ANY_FIELDS.add("id"); + Stream.of(User.class, Group.class, AnyObject.class).forEach(clazz -> { + for (PropertyDescriptor pd : BeanUtils.getPropertyDescriptors(clazz)) { + ANY_FIELDS.add(pd.getName()); + } + }); + } + @Override public boolean isValid(final Schema schema, final ConstraintValidatorContext context) { - if (schema.getKey() == null || !Entity.ID_PATTERN.matcher(schema.getKey()).matches()) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate( @@ -36,7 +53,7 @@ public boolean isValid(final Schema schema, final ConstraintValidatorContext con addPropertyNode("key").addConstraintViolation(); return false; - } else if (JPAAnyUtils.matchesFieldName(schema.getKey())) { + } else if (ANY_FIELDS.contains(schema.getKey())) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate( getTemplate(EntityViolationType.InvalidKey, "Schema key not allowed: " + schema.getKey())). diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/MyJPAJSONPersistenceContext.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/MyJPAJSONPersistenceContext.java index ae2d3e1b1c..a8092c6d76 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/MyJPAJSONPersistenceContext.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/MyJPAJSONPersistenceContext.java @@ -20,9 +20,10 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; 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.AuditEntryDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; @@ -34,8 +35,7 @@ import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAnyDAO; import org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAnySearchDAO; -import org.apache.syncope.core.persistence.jpa.dao.repo.AuditConfRepoExt; -import org.apache.syncope.core.persistence.jpa.dao.repo.AuditConfRepoExtMyJSONImpl; +import org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAuditEntryDAO; import org.apache.syncope.core.persistence.jpa.dao.repo.PlainSchemaRepoExt; import org.apache.syncope.core.persistence.jpa.dao.repo.PlainSchemaRepoExtMyJSONImpl; import org.apache.syncope.core.persistence.jpa.entity.MyJPAJSONEntityFactory; @@ -88,10 +88,10 @@ public AnySearchDAO anySearchDAO( entityManager); } - @ConditionalOnMissingBean(name = "myJPAJSONAuditConRepoExt") + @ConditionalOnMissingBean(name = "myJPAJSONAuditEntryDAO") @Bean - public AuditConfRepoExt auditConfRepoExt(final EntityManager entityManager) { - return new AuditConfRepoExtMyJSONImpl(entityManager); + public AuditEntryDAO auditEntryDAO(final EntityManager entityManager) { + return new MyJPAJSONAuditEntryDAO(entityManager); } @ConditionalOnMissingBean(name = "myJPAJSONPlainSchemaRepoExt") diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/OJPAJSONPersistenceContext.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/OJPAJSONPersistenceContext.java index cdcea2b41c..36ff906d4f 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/OJPAJSONPersistenceContext.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/OJPAJSONPersistenceContext.java @@ -20,9 +20,10 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; 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.AuditEntryDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; @@ -34,8 +35,7 @@ import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.jpa.dao.OJPAJSONAnyDAO; import org.apache.syncope.core.persistence.jpa.dao.OJPAJSONAnySearchDAO; -import org.apache.syncope.core.persistence.jpa.dao.repo.AuditConfRepoExt; -import org.apache.syncope.core.persistence.jpa.dao.repo.AuditConfRepoExtOJSONImpl; +import org.apache.syncope.core.persistence.jpa.dao.OJPAJSONAuditEntryDAO; import org.apache.syncope.core.persistence.jpa.dao.repo.PlainSchemaRepoExt; import org.apache.syncope.core.persistence.jpa.dao.repo.PlainSchemaRepoExtOJSONImpl; import org.apache.syncope.core.persistence.jpa.entity.OJPAJSONEntityFactory; @@ -88,10 +88,10 @@ public AnySearchDAO anySearchDAO( entityManager); } - @ConditionalOnMissingBean(name = "oJPAJSONAuditConfRepoExt") + @ConditionalOnMissingBean(name = "oJPAJSONAuditEntryDAO") @Bean - public AuditConfRepoExt auditConfRepoExt(final EntityManager entityManager) { - return new AuditConfRepoExtOJSONImpl(entityManager); + public AuditEntryDAO auditEntryDAO(final EntityManager entityManager) { + return new OJPAJSONAuditEntryDAO(entityManager); } @ConditionalOnMissingBean(name = "oJPAJSONPlainSchemaRepoExt") diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/PGJPAJSONPersistenceContext.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/PGJPAJSONPersistenceContext.java index 8088ba4d7d..0887a9570b 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/PGJPAJSONPersistenceContext.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/PGJPAJSONPersistenceContext.java @@ -20,9 +20,10 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; 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.AuditEntryDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; @@ -34,8 +35,7 @@ import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAnyDAO; import org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAnySearchDAO; -import org.apache.syncope.core.persistence.jpa.dao.repo.AuditConfRepoExt; -import org.apache.syncope.core.persistence.jpa.dao.repo.AuditConfRepoExtPGJSONImpl; +import org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAuditEntryDAO; import org.apache.syncope.core.persistence.jpa.dao.repo.PlainSchemaRepoExt; import org.apache.syncope.core.persistence.jpa.dao.repo.PlainSchemaRepoExtPGJSONImpl; import org.apache.syncope.core.persistence.jpa.entity.PGJPAJSONEntityFactory; @@ -88,10 +88,9 @@ public AnySearchDAO anySearchDAO( entityManager); } - @ConditionalOnMissingBean(name = "pgJPAJSONAuditConfRepoExt") - @Bean - public AuditConfRepoExt auditConfRepoExt(final EntityManager entityManager) { - return new AuditConfRepoExtPGJSONImpl(entityManager); + @ConditionalOnMissingBean(name = "pgJPAJSONAuditEntryDAO") + public AuditEntryDAO auditEntryDAO(final EntityManager entityManager) { + return new PGJPAJSONAuditEntryDAO(entityManager); } @ConditionalOnMissingBean(name = "pgJPAJSONPlainSchemaRepoExt") diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnyDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnyDAO.java index f9f0ec4e27..fb6650c0ab 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnyDAO.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAnyDAO.java @@ -57,6 +57,29 @@ abstract class AbstractJPAJSONAnyDAO implements JPAJSONAnyDAO { protected static final Logger LOG = LoggerFactory.getLogger(JPAJSONAnyDAO.class); + /** + * Split an attribute value recurring on provided literals/tokens. + * + * @param attrValue value to be split + * @param literals literals/tokens + * @return split value + */ + protected static List split(final String attrValue, final List literals) { + List attrValues = new ArrayList<>(); + + if (literals.isEmpty()) { + attrValues.add(attrValue); + } else { + for (String token : attrValue.split(Pattern.quote(literals.get(0)))) { + if (!token.isEmpty()) { + attrValues.addAll(split(token, literals.subList(1, literals.size()))); + } + } + } + + return attrValues; + } + protected final PlainSchemaDAO plainSchemaDAO; protected final EntityManager entityManager; @@ -179,29 +202,6 @@ public > Optional findByPlainAttrUniqueValue( : Optional.of(result.get(0)); } - /** - * Split an attribute value recurring on provided literals/tokens. - * - * @param attrValue value to be split - * @param literals literals/tokens - * @return split value - */ - protected List split(final String attrValue, final List literals) { - List attrValues = new ArrayList<>(); - - if (literals.isEmpty()) { - attrValues.add(attrValue); - } else { - for (String token : attrValue.split(Pattern.quote(literals.get(0)))) { - if (!token.isEmpty()) { - attrValues.addAll(split(token, literals.subList(1, literals.size()))); - } - } - } - - return attrValues; - } - @SuppressWarnings("unchecked") protected List findByDerAttrValue( final String table, diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAuditConfRepoExtJSON.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAuditEntryDAO.java similarity index 95% rename from core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAuditConfRepoExtJSON.java rename to core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAuditEntryDAO.java index 0b6533afd9..8546080932 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAuditConfRepoExtJSON.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAuditEntryDAO.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao.repo; +package org.apache.syncope.core.persistence.jpa.dao; import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -28,7 +28,7 @@ import org.apache.syncope.common.lib.types.AuditElements; import org.springframework.util.CollectionUtils; -abstract class AbstractAuditConfRepoExtJSON extends AuditConfRepoExtImpl { +abstract class AbstractJPAJSONAuditEntryDAO extends JPAAuditEntryDAO { private static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); @@ -124,7 +124,7 @@ public String build() { } } - protected AbstractAuditConfRepoExtJSON(final EntityManager entityManager) { + protected AbstractJPAJSONAuditEntryDAO(final EntityManager entityManager) { super(entityManager); } } diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java index a76bd71faa..43287537f0 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java @@ -27,7 +27,7 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.lib.types.AttrSchemaType; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepoExtMyJSONImpl.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAuditEntryDAO.java similarity index 81% rename from core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepoExtMyJSONImpl.java rename to core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAuditEntryDAO.java index 3c33a97bad..6f22d08361 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepoExtMyJSONImpl.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAuditEntryDAO.java @@ -16,16 +16,15 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao.repo; +package org.apache.syncope.core.persistence.jpa.dao; import com.fasterxml.jackson.databind.node.ObjectNode; import jakarta.persistence.EntityManager; import java.util.List; import java.util.stream.Collectors; -import org.apache.syncope.core.persistence.api.dao.AuditConfDAO; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; -public class AuditConfRepoExtMyJSONImpl extends AbstractAuditConfRepoExtJSON { +public class MyJPAJSONAuditEntryDAO extends AbstractJPAJSONAuditEntryDAO { protected static class MyMessageCriteriaBuilder extends JSONMessageCriteriaBuilder { @@ -33,13 +32,13 @@ protected static class MyMessageCriteriaBuilder extends JSONMessageCriteriaBuild protected String doBuild(final List containers) { if (entityKey != null) { query.append(andIfNeeded()).append('('). - append(AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN). + append(MESSAGE_COLUMN). append("->'$.before' LIKE '%").append(entityKey). append("%' OR "). - append(AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN). + append(MESSAGE_COLUMN). append("->'$.input' LIKE '%").append(entityKey). append("%' OR "). - append(AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN). + append(MESSAGE_COLUMN). append("->'$.output' LIKE '%").append(entityKey). append("%')"); } @@ -47,7 +46,7 @@ protected String doBuild(final List containers) { if (!containers.isEmpty()) { query.append(andIfNeeded()).append('('). append(containers.stream(). - map(container -> "JSON_CONTAINS(" + AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN + ", '" + map(container -> "JSON_CONTAINS(" + MESSAGE_COLUMN + ", '" + POJOHelper.serialize(container).replace("'", "''") + "')").collect(Collectors.joining(" OR "))). append(')'); @@ -57,7 +56,7 @@ protected String doBuild(final List containers) { } } - public AuditConfRepoExtMyJSONImpl(final EntityManager entityManager) { + public MyJPAJSONAuditEntryDAO(final EntityManager entityManager) { super(entityManager); } diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAnySearchDAO.java index fb5bd46079..75b64d3612 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAnySearchDAO.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAnySearchDAO.java @@ -28,7 +28,7 @@ import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.lib.types.AttrSchemaType; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepoExtOJSONImpl.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAuditEntryDAO.java similarity index 85% rename from core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepoExtOJSONImpl.java rename to core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAuditEntryDAO.java index 5c060bb734..2656fd1a94 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepoExtOJSONImpl.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAuditEntryDAO.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao.repo; +package org.apache.syncope.core.persistence.jpa.dao; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -26,9 +26,8 @@ import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; -import org.apache.syncope.core.persistence.api.dao.AuditConfDAO; -public class AuditConfRepoExtOJSONImpl extends AbstractAuditConfRepoExtJSON { +public class OJPAJSONAuditEntryDAO extends AbstractJPAJSONAuditEntryDAO { protected static class OMessageCriteriaBuilder extends JSONMessageCriteriaBuilder { @@ -42,13 +41,13 @@ protected Optional jsonExprItem(final JsonNode logger, final String fiel protected String doBuild(final List containers) { if (entityKey != null) { query.append(andIfNeeded()).append('('). - append("JSON_VALUE(").append(AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN). + append("JSON_VALUE(").append(MESSAGE_COLUMN). append(", '$.before' RETURNING VARCHAR2(32767)) LIKE '%"). append(entityKey).append("%' OR "). - append("JSON_VALUE(").append(AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN). + append("JSON_VALUE(").append(MESSAGE_COLUMN). append(", '$.input' RETURNING VARCHAR2(32767)) LIKE '%"). append(entityKey).append("%' OR "). - append("JSON_VALUE(").append(AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN). + append("JSON_VALUE(").append(MESSAGE_COLUMN). append(", '$.output' RETURNING VARCHAR2(32767)) LIKE '%"). append(entityKey).append("%')"); } @@ -74,7 +73,7 @@ protected String doBuild(final List containers) { } } - public AuditConfRepoExtOJSONImpl(final EntityManager entityManager) { + public OJPAJSONAuditEntryDAO(final EntityManager entityManager) { super(entityManager); } diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java index 5a4e66b1b0..49e8c7a7bd 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java @@ -35,7 +35,7 @@ import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.AttrSchemaType; import org.apache.syncope.common.rest.api.service.JAXRSService; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; @@ -69,11 +69,11 @@ public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO { protected static final String ALWAYS_FALSE_ASSERTION = "1=2"; - protected static final String POSTGRESQL_REGEX_CHARS = "!$()*+.:<=>?[\\]^{|}-"; + protected static final String REGEX_CHARS = "!$()*+.:<=>?[\\]^{|}-"; protected static String escapeForLikeRegex(final String input) { String output = input; - for (char toEscape : POSTGRESQL_REGEX_CHARS.toCharArray()) { + for (char toEscape : REGEX_CHARS.toCharArray()) { output = output.replace(String.valueOf(toEscape), "\\" + toEscape); } return output.replace("'", "''"); @@ -150,15 +150,14 @@ protected void fillAttrQuery( final PlainSchema schema, final AttrCond cond, final boolean not, - final List parameters, final SearchSupport svs) { if (not && cond.getType() == AttrCond.Type.ISNULL) { cond.setType(AttrCond.Type.ISNOTNULL); - fillAttrQuery(anyUtils, query, attrValue, schema, cond, true, parameters, svs); + fillAttrQuery(anyUtils, query, attrValue, schema, cond, true, svs); } else if (not) { query.append("NOT ("); - fillAttrQuery(anyUtils, query, attrValue, schema, cond, false, parameters, svs); + fillAttrQuery(anyUtils, query, attrValue, schema, cond, false, svs); query.append(')'); } else { String key = key(schema.getType()); @@ -217,11 +216,12 @@ protected void fillAttrQuery( LOG.error("LIKE is only compatible with string or enum schemas"); } } + case IEQ, EQ -> { query.append("jsonb_path_exists(").append(schema.getKey()).append(", '$[*] ? "). append("(@.").append(key); - if (StringUtils.containsAny(value, POSTGRESQL_REGEX_CHARS) || lower) { + if (StringUtils.containsAny(value, REGEX_CHARS) || lower) { query.append(" like_regex \"^"). append(escapeForLikeRegex(value).replace("'", "''")). append("$\""); @@ -236,6 +236,7 @@ protected void fillAttrQuery( query.append("jsonb_path_exists(").append(schema.getKey()).append(", '$[*] ? "). append("(@.").append(key).append(" >= "). append(escapeIfString(value, isStr)).append(")')"); + case GT -> query.append("jsonb_path_exists(").append(schema.getKey()).append(", '$[*] ? "). append("(@.").append(key).append(" > "). @@ -279,8 +280,9 @@ protected String getQuery( append("jsonb_path_exists(").append(checked.getLeft().getKey()).append(",'$[*]')"); default -> - fillAttrQuery(anyUtilsFactory.getInstance(svs.anyTypeKind), - query, checked.getRight(), checked.getLeft(), cond, not, parameters, svs); + fillAttrQuery( + anyUtilsFactory.getInstance(svs.anyTypeKind), + query, checked.getRight(), checked.getLeft(), cond, not, svs); } return query.toString(); @@ -618,8 +620,9 @@ protected String getQuery( StringBuilder query = new StringBuilder(); plainSchemaDAO.findById(cond.getSchema()).ifPresentOrElse( - schema -> fillAttrQuery(anyUtilsFactory.getInstance(svs.anyTypeKind), - query, checked.getMiddle(), checked.getLeft(), checked.getRight(), not, parameters, svs), + schema -> fillAttrQuery( + anyUtilsFactory.getInstance(svs.anyTypeKind), + query, checked.getMiddle(), checked.getLeft(), checked.getRight(), not, svs), () -> fillAttrQuery( query, checked.getMiddle(), checked.getLeft(), checked.getRight(), not, parameters, svs)); @@ -643,7 +646,7 @@ protected String buildAdminRealmsFilter( } @Override - protected int doCount( + protected long doCount( final Realm base, final boolean recursive, final Set adminRealms, @@ -668,7 +671,7 @@ protected int doCount( Query countQuery = entityManager.createNativeQuery(queryString.toString()); fillWithParameters(countQuery, parameters); - return ((Number) countQuery.getSingleResult()).intValue(); + return ((Number) countQuery.getSingleResult()).longValue(); } @Override @@ -783,8 +786,7 @@ protected void fillAttrQuery( case ILIKE, LIKE -> { if (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum) { - query.append(column); - query.append(" LIKE "); + query.append(column).append(" LIKE "); if (lower) { query.append("LOWER(?").append(setParameter(parameters, cond.getExpression())).append(')'); } else { @@ -795,9 +797,9 @@ protected void fillAttrQuery( LOG.error("LIKE is only compatible with string or enum schemas"); } } + case IEQ, EQ -> { - query.append(column); - query.append('='); + query.append(column).append('='); if (lower && (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum)) { @@ -817,6 +819,7 @@ protected void fillAttrQuery( } query.append('?').append(setParameter(parameters, attrValue.getValue())); } + case GT -> { query.append(column); if (not) { diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepoExtPGJSONImpl.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAuditEntryDAO.java similarity index 77% rename from core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepoExtPGJSONImpl.java rename to core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAuditEntryDAO.java index 832bba8238..830cfaae01 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepoExtPGJSONImpl.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAuditEntryDAO.java @@ -16,16 +16,15 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao.repo; +package org.apache.syncope.core.persistence.jpa.dao; import com.fasterxml.jackson.databind.node.ObjectNode; import jakarta.persistence.EntityManager; import java.util.List; import java.util.stream.Collectors; -import org.apache.syncope.core.persistence.api.dao.AuditConfDAO; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; -public class AuditConfRepoExtPGJSONImpl extends AbstractAuditConfRepoExtJSON { +public class PGJPAJSONAuditEntryDAO extends AbstractJPAJSONAuditEntryDAO { protected static class PGMessageCriteriaBuilder extends JSONMessageCriteriaBuilder { @@ -33,13 +32,13 @@ protected static class PGMessageCriteriaBuilder extends JSONMessageCriteriaBuild protected String doBuild(final List containers) { if (entityKey != null) { query.append(andIfNeeded()).append('('). - append(AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN). + append(MESSAGE_COLUMN). append(" ->> 'before' LIKE '%").append(entityKey). append("%' OR "). - append(AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN). + append(MESSAGE_COLUMN). append(" ->> 'input' LIKE '%").append(entityKey). append("%' OR "). - append(AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN). + append(MESSAGE_COLUMN). append(" ->> 'output' LIKE '%").append(entityKey). append("%')"); } @@ -47,7 +46,7 @@ protected String doBuild(final List containers) { if (!containers.isEmpty()) { query.append(andIfNeeded()).append('('). append(containers.stream(). - map(container -> AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN + "::jsonb @> '" + map(container -> MESSAGE_COLUMN + "::jsonb @> '" + POJOHelper.serialize(container).replace("'", "''") + "'::jsonb").collect(Collectors.joining(" OR "))). append(')'); @@ -57,13 +56,13 @@ protected String doBuild(final List containers) { } } - public AuditConfRepoExtPGJSONImpl(final EntityManager entityManager) { + public PGJPAJSONAuditEntryDAO(final EntityManager entityManager) { super(entityManager); } @Override protected String select() { - return AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN + "::text"; + return MESSAGE_COLUMN + "::text"; } @Override diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractPlainSchemaRepoExtJSON.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractPlainSchemaRepoExtJSON.java index f948711358..ab5a4200d9 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractPlainSchemaRepoExtJSON.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractPlainSchemaRepoExtJSON.java @@ -22,8 +22,8 @@ import java.util.List; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; -import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; +import org.apache.syncope.core.persistence.api.entity.Attributable; import org.apache.syncope.core.persistence.api.entity.PlainAttr; import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttr; @@ -65,7 +65,7 @@ protected void deleteAttrs(final PlainSchema schema) { @SuppressWarnings("unchecked") public > void delete(final T plainAttr) { if (plainAttr.getOwner() != null) { - ((Any) plainAttr.getOwner()).remove(plainAttr); + ((Attributable) plainAttr.getOwner()).remove(plainAttr); } } } diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExtJSONImpl.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExtJSONImpl.java index acbe8989c2..78a793b3b5 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExtJSONImpl.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExtJSONImpl.java @@ -68,8 +68,7 @@ public List findByPlainAttrValue( final PlainAttrValue attrValue, final boolean ignoreCaseMatch) { - return anyDAO.findByPlainAttrValue( - JPAJSONAnyObject.TABLE, anyUtils, schema, attrValue, ignoreCaseMatch); + return anyDAO.findByPlainAttrValue(JPAJSONAnyObject.TABLE, anyUtils, schema, attrValue, ignoreCaseMatch); } @Override diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtJSONImpl.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtJSONImpl.java index cb4a180a16..a5af21c915 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtJSONImpl.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtJSONImpl.java @@ -78,8 +78,7 @@ public List findByPlainAttrValue( final PlainAttrValue attrValue, final boolean ignoreCaseMatch) { - return anyDAO.findByPlainAttrValue( - JPAGroup.TABLE, anyUtils, schema, attrValue, ignoreCaseMatch); + return anyDAO.findByPlainAttrValue(JPAGroup.TABLE, anyUtils, schema, attrValue, ignoreCaseMatch); } @Override @@ -88,8 +87,7 @@ public Optional findByPlainAttrUniqueValue( final PlainAttrUniqueValue attrUniqueValue, final boolean ignoreCaseMatch) { - return anyDAO.findByPlainAttrUniqueValue( - JPAGroup.TABLE, anyUtils, schema, attrUniqueValue, ignoreCaseMatch); + return anyDAO.findByPlainAttrUniqueValue(JPAGroup.TABLE, anyUtils, schema, attrUniqueValue, ignoreCaseMatch); } @Override diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtJSONImpl.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtJSONImpl.java index 0fc05f7283..57012a31a4 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtJSONImpl.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtJSONImpl.java @@ -80,8 +80,7 @@ public List findByPlainAttrValue( final PlainAttrValue attrValue, final boolean ignoreCaseMatch) { - return anyDAO.findByPlainAttrValue( - JPAJSONUser.TABLE, anyUtils, schema, attrValue, ignoreCaseMatch); + return anyDAO.findByPlainAttrValue(JPAJSONUser.TABLE, anyUtils, schema, attrValue, ignoreCaseMatch); } @Override @@ -90,8 +89,7 @@ public Optional findByPlainAttrUniqueValue( final PlainAttrUniqueValue attrUniqueValue, final boolean ignoreCaseMatch) { - return anyDAO.findByPlainAttrUniqueValue( - JPAJSONUser.TABLE, anyUtils, schema, attrUniqueValue, ignoreCaseMatch); + return anyDAO.findByPlainAttrUniqueValue(JPAJSONUser.TABLE, anyUtils, schema, attrUniqueValue, ignoreCaseMatch); } @Override diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAJSONEntityListener.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAJSONEntityListener.java index 93158d8d34..bb86ed0f52 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAJSONEntityListener.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAJSONEntityListener.java @@ -19,6 +19,7 @@ package org.apache.syncope.core.persistence.jpa.entity; import java.util.List; +import java.util.Optional; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.JSONAttributable; import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr; @@ -44,9 +45,7 @@ protected void json2list(final JSONAttributable entity, final boolean clearFi ((LAPlainAttr) attr).setAccount(linkedAccount); } attr.getValues().forEach(value -> value.setAttr(attr)); - if (attr.getUniqueValue() != null) { - attr.getUniqueValue().setAttr(attr); - } + Optional.ofNullable(attr.getUniqueValue()).ifPresent(value -> value.setAttr(attr)); return attr; }).forEach(entity::add); } diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAJSONAnyObject.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAJSONAnyObject.java index 8172267fbf..bd0bbb7e39 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAJSONAnyObject.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAJSONAnyObject.java @@ -33,7 +33,7 @@ import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership; import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttr; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; -import org.apache.syncope.core.persistence.jpa.validation.entity.JPAJSONAttributableCheck; +import org.apache.syncope.core.persistence.jpa.validation.JPAJSONAttributableCheck; @Entity @Table(name = JPAAnyObject.TABLE, uniqueConstraints = diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAJSONGroup.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAJSONGroup.java index aa1c4a9766..944e069fac 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAJSONGroup.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAJSONGroup.java @@ -28,7 +28,7 @@ import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr; import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr; import org.apache.syncope.core.persistence.api.entity.group.Group; -import org.apache.syncope.core.persistence.jpa.validation.entity.JPAJSONAttributableCheck; +import org.apache.syncope.core.persistence.jpa.validation.JPAJSONAttributableCheck; @Entity @Table(name = JPAGroup.TABLE) diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONLinkedAccount.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONLinkedAccount.java index c1ac290438..bffdb3d867 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONLinkedAccount.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONLinkedAccount.java @@ -30,7 +30,7 @@ import org.apache.syncope.core.persistence.api.entity.user.LAPlainAttr; 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.persistence.jpa.validation.entity.JPAJSONAttributableCheck; +import org.apache.syncope.core.persistence.jpa.validation.JPAJSONAttributableCheck; @Entity @Table(name = JPALinkedAccount.TABLE) diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONUPlainAttr.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONUPlainAttr.java index 239aac9efa..18d1095125 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONUPlainAttr.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONUPlainAttr.java @@ -151,11 +151,6 @@ public List getValues() { return values; } - @JsonIgnore - public List getPGValues() { - return values; - } - @Override public JPAJSONUPlainAttrUniqueValue getUniqueValue() { return uniqueValue; diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONUser.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONUser.java index f6f19e3e4a..762deb26ff 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONUser.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONUser.java @@ -36,7 +36,7 @@ import org.apache.syncope.core.persistence.api.entity.user.UMembership; import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr; import org.apache.syncope.core.persistence.api.entity.user.User; -import org.apache.syncope.core.persistence.jpa.validation.entity.JPAJSONAttributableCheck; +import org.apache.syncope.core.persistence.jpa.validation.JPAJSONAttributableCheck; @Entity @Table(name = JPAUser.TABLE) diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/JPAJSONAttributableCheck.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/validation/JPAJSONAttributableCheck.java similarity index 95% rename from core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/JPAJSONAttributableCheck.java rename to core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/validation/JPAJSONAttributableCheck.java index 247880d95a..d3dc6339bb 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/JPAJSONAttributableCheck.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/validation/JPAJSONAttributableCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.jpa.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/JPAJSONAttributableValidator.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/validation/JPAJSONAttributableValidator.java similarity index 69% rename from core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/JPAJSONAttributableValidator.java rename to core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/validation/JPAJSONAttributableValidator.java index bbb69feae0..9d7c9970fb 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/JPAJSONAttributableValidator.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/validation/JPAJSONAttributableValidator.java @@ -16,28 +16,30 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.jpa.validation; import jakarta.validation.ConstraintValidatorContext; import java.util.concurrent.atomic.AtomicReference; import org.apache.syncope.core.persistence.api.entity.JSONAttributable; -import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.common.validation.AbstractValidator; +import org.apache.syncope.core.persistence.common.validation.PlainAttrValidator; +import org.apache.syncope.core.persistence.common.validation.PlainAttrValueValidator; public class JPAJSONAttributableValidator extends AbstractValidator> { + private static final PlainAttrValidator ATTR_VALIDATOR = new PlainAttrValidator(); + + private static final PlainAttrValueValidator ATTR_VALUE_VALIDATOR = new PlainAttrValueValidator(); + @Override public boolean isValid(final JSONAttributable entity, final ConstraintValidatorContext context) { context.disableDefaultConstraintViolation(); - JPAPlainAttrValidator attrValidator = new JPAPlainAttrValidator(); - PlainAttrValueValidator attrValueValidator = new PlainAttrValueValidator(); - AtomicReference isValid = new AtomicReference<>(Boolean.TRUE); entity.getPlainAttrList().forEach(attr -> { - PlainAttr plainAttr = (PlainAttr) attr; - isValid.getAndSet(isValid.get() && attrValidator.isValid(plainAttr, context)); - plainAttr.getValues().forEach( - value -> isValid.getAndSet(isValid.get() && attrValueValidator.isValid(value, context))); + isValid.getAndSet(isValid.get() && ATTR_VALIDATOR.isValid(attr, context)); + attr.getValues().forEach( + value -> isValid.getAndSet(isValid.get() && ATTR_VALUE_VALIDATOR.isValid(value, context))); }); return isValid.get(); diff --git a/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-myjson.xml b/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-myjson.xml index ec73b4b348..e80b733863 100644 --- a/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-myjson.xml +++ b/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-myjson.xml @@ -26,7 +26,7 @@ under the License. - + diff --git a/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-ojson.xml b/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-ojson.xml index 427948f540..ad7ddcb0b4 100644 --- a/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-ojson.xml +++ b/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-ojson.xml @@ -26,7 +26,7 @@ under the License. - + diff --git a/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-pgjsonb.xml b/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-pgjsonb.xml index 2a9df30d9b..73e3b096a9 100644 --- a/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-pgjsonb.xml +++ b/core/persistence-jpa-json/src/main/resources/META-INF/spring-orm-pgjsonb.xml @@ -26,7 +26,7 @@ under the License. - + diff --git a/core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml b/core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml index 56c5c74801..94a8d4510d 100644 --- a/core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml +++ b/core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml @@ -30,14 +30,14 @@ under the License. + body="org.apache.syncope.core.persistence.common.attrvalue.EmailAddressValidator"/> + body="org.apache.syncope.core.persistence.common.attrvalue.BinaryValidator"/> diff --git a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml index 2277c31420..ff80648843 100644 --- a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml +++ b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml @@ -125,7 +125,7 @@ under the License. mandatoryCondition="true" multivalue="0" uniqueConstraint="1" readonly="0"/> + body="org.apache.syncope.core.persistence.common.attrvalue.EmailAddressValidator"/> @@ -176,7 +176,7 @@ under the License. mimeType="image/jpeg"/> + body="org.apache.syncope.core.persistence.common.attrvalue.BinaryValidator"/> @@ -344,7 +344,7 @@ under the License. realm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" creator="admin" lastModifier="admin" creationDate="2010-10-20 11:00:00" lastChangeDate="2010-10-20 11:00:00" - plainAttrs="[{"values":[{"stringValue":"niceIcon"}],"schema":"icon"},{"values":[{"booleanValue":true}],"schema":"show"},{"values":[{"stringValue":"sx"}],"schema":"rderived_sx"},{"values":[{"stringValue":"dx"}],"schema":"rderived_dx"}]"/> + plainAttrs='[{"values":[{"stringValue":"niceIcon"}],"schema":"icon"},{"uniqueValue":{"booleanValue":true},"schema":"show"},{"values":[{"stringValue":"sx"}],"schema":"rderived_sx"},{"values":[{"stringValue":"dx"}],"schema":"rderived_dx"}]'/> spring-boot-starter-data-jpa - - org.apache.syncope.core - syncope-core-spring - ${project.version} - - org.apache.openjpa openjpa - org.apache.commons - commons-jexl3 - - - - org.apache.tika - tika-core + org.apache.syncope.core + syncope-core-persistence-common + ${project.version} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/DomainProperties.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/DomainProperties.java index 68bb940c1c..da0ea31e50 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/DomainProperties.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/DomainProperties.java @@ -19,11 +19,9 @@ package org.apache.syncope.core.persistence.jpa; import org.apache.syncope.common.keymaster.client.api.model.Domain; -import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.core.persistence.common.AbstractDomainProperties; -public class DomainProperties { - - private String key; +public class DomainProperties extends AbstractDomainProperties { private String jdbcDriver; @@ -47,22 +45,6 @@ public class DomainProperties { private String databasePlatform; - private String adminPassword; - - private CipherAlgorithm adminCipherAlgorithm = CipherAlgorithm.SHA512; - - private String content; - - private String keymasterConfParams; - - public String getKey() { - return key; - } - - public void setKey(final String key) { - this.key = key; - } - public String getJdbcDriver() { return jdbcDriver; } @@ -150,40 +132,4 @@ public String getDatabasePlatform() { public void setDatabasePlatform(final String databasePlatform) { this.databasePlatform = databasePlatform; } - - public String getAdminPassword() { - return adminPassword; - } - - public void setAdminPassword(final String adminPassword) { - this.adminPassword = adminPassword; - } - - public CipherAlgorithm getAdminCipherAlgorithm() { - return adminCipherAlgorithm; - } - - public void setAdminCipherAlgorithm(final CipherAlgorithm adminCipherAlgorithm) { - this.adminCipherAlgorithm = adminCipherAlgorithm; - } - - public String getContent() { - return content == null - ? "classpath:domains/" + key + "Content.xml" - : content; - } - - public void setContent(final String content) { - this.content = content; - } - - public String getKeymasterConfParams() { - return keymasterConfParams == null - ? "classpath:domains/" + key + "KeymasterConfParams.json" - : keymasterConfParams; - } - - public void setKeymasterConfParams(final String keymasterConfParams) { - this.keymasterConfParams = keymasterConfParams; - } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/DomainConfFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java similarity index 94% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/DomainConfFactory.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java index 3cab70a0fc..78c971f8cd 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/DomainConfFactory.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java @@ -27,8 +27,6 @@ import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.DomainRegistry; import org.apache.syncope.core.persistence.jpa.spring.DomainRoutingEntityManagerFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.ConfigurableApplicationContext; @@ -37,13 +35,11 @@ import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import org.springframework.jndi.JndiObjectFactoryBean; -public class DomainConfFactory implements DomainRegistry { - - protected static final Logger LOG = LoggerFactory.getLogger(DomainConfFactory.class); +public class JPADomainRegistry implements DomainRegistry { protected final ConfigurableApplicationContext ctx; - public DomainConfFactory(final ConfigurableApplicationContext ctx) { + public JPADomainRegistry(final ConfigurableApplicationContext ctx) { this.ctx = ctx; } @@ -62,6 +58,11 @@ protected void registerSingleton(final String name, final Object bean) { beanFactory().registerSingleton(name, bean); } + @SuppressWarnings("unchecked") + protected DomainHolder domainHolder() { + return beanFactory().getBean(DomainHolder.class); + } + @Override public void register(final Domain domain) { HikariConfig hikariConfig = new HikariConfig(); @@ -83,7 +84,7 @@ public void register(final Domain domain) { getBeanDefinition()); DataSource initedDataSource = beanFactory().getBean(domain.getKey() + "DataSource", DataSource.class); - beanFactory().getBean(DomainHolder.class).getDomains().put(domain.getKey(), initedDataSource); + domainHolder().getDomains().put(domain.getKey(), initedDataSource); // domainResourceDatabasePopulator ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPARuntimeDomainLoader.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPARuntimeDomainLoader.java new file mode 100644 index 0000000000..8c729676f8 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPARuntimeDomainLoader.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.persistence.jpa; + +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.common.RuntimeDomainLoader; +import org.apache.syncope.core.persistence.jpa.spring.DomainRoutingEntityManagerFactory; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.springframework.context.ConfigurableApplicationContext; + +public class JPARuntimeDomainLoader extends RuntimeDomainLoader { + + protected final DomainRoutingEntityManagerFactory entityManagerFactory; + + public JPARuntimeDomainLoader( + final DomainHolder domainHolder, + final DomainRegistry domainRegistry, + final DomainRoutingEntityManagerFactory entityManagerFactory, + final ConfigurableApplicationContext ctx) { + + super(domainHolder, domainRegistry, ctx); + this.entityManagerFactory = entityManagerFactory; + } + + @Override + protected void onAdd(final Domain domain) { + AuthContextUtils.runAsAdmin(domain.getKey(), () -> entityManagerFactory.initJPASchema()); + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java index 9810f6b735..883ba8c9c5 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java @@ -21,17 +21,14 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.ValidationMode; -import jakarta.validation.Validator; import java.util.HashMap; import java.util.Map; import javax.sql.DataSource; -import org.apache.syncope.common.keymaster.client.api.ConfParamOps; import org.apache.syncope.common.keymaster.client.api.DomainOps; import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.DomainRegistry; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; @@ -41,6 +38,7 @@ import org.apache.syncope.core.persistence.api.dao.ApplicationDAO; import org.apache.syncope.core.persistence.api.dao.AttrRepoDAO; import org.apache.syncope.core.persistence.api.dao.AuditConfDAO; +import org.apache.syncope.core.persistence.api.dao.AuditEntryDAO; import org.apache.syncope.core.persistence.api.dao.AuthModuleDAO; import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO; import org.apache.syncope.core.persistence.api.dao.BatchDAO; @@ -79,19 +77,17 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO; import org.apache.syncope.core.persistence.api.dao.WAConfigDAO; -import org.apache.syncope.core.persistence.api.entity.AnyUtils; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; -import org.apache.syncope.core.persistence.api.entity.am.ClientAppUtilsFactory; -import org.apache.syncope.core.persistence.api.entity.policy.PolicyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory; import org.apache.syncope.core.persistence.api.search.SearchCondVisitor; -import org.apache.syncope.core.persistence.jpa.attrvalue.validation.DefaultPlainAttrValidationManager; -import org.apache.syncope.core.persistence.jpa.content.KeymasterConfParamLoader; +import org.apache.syncope.core.persistence.common.CommonPersistenceContext; +import org.apache.syncope.core.persistence.common.RuntimeDomainLoader; import org.apache.syncope.core.persistence.jpa.content.XMLContentExporter; import org.apache.syncope.core.persistence.jpa.content.XMLContentLoader; import org.apache.syncope.core.persistence.jpa.dao.JPAAnyMatchDAO; import org.apache.syncope.core.persistence.jpa.dao.JPAAnySearchDAO; +import org.apache.syncope.core.persistence.jpa.dao.JPAAuditEntryDAO; import org.apache.syncope.core.persistence.jpa.dao.JPABatchDAO; import org.apache.syncope.core.persistence.jpa.dao.JPAEntityCacheDAO; import org.apache.syncope.core.persistence.jpa.dao.JPAJobStatusDAO; @@ -99,6 +95,7 @@ import org.apache.syncope.core.persistence.jpa.dao.JPAPersistenceInfoDAO; import org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrValueDAO; import org.apache.syncope.core.persistence.jpa.dao.JPAPolicyDAO; +import org.apache.syncope.core.persistence.jpa.dao.JPARealmDAO; import org.apache.syncope.core.persistence.jpa.dao.JPATaskDAO; import org.apache.syncope.core.persistence.jpa.dao.JPATaskExecDAO; import org.apache.syncope.core.persistence.jpa.dao.repo.AccessTokenRepo; @@ -118,8 +115,6 @@ import org.apache.syncope.core.persistence.jpa.dao.repo.AttrRepoRepoExt; import org.apache.syncope.core.persistence.jpa.dao.repo.AttrRepoRepoExtImpl; import org.apache.syncope.core.persistence.jpa.dao.repo.AuditConfRepo; -import org.apache.syncope.core.persistence.jpa.dao.repo.AuditConfRepoExt; -import org.apache.syncope.core.persistence.jpa.dao.repo.AuditConfRepoExtImpl; import org.apache.syncope.core.persistence.jpa.dao.repo.AuthModuleRepo; import org.apache.syncope.core.persistence.jpa.dao.repo.AuthModuleRepoExt; import org.apache.syncope.core.persistence.jpa.dao.repo.AuthModuleRepoExtImpl; @@ -159,9 +154,6 @@ import org.apache.syncope.core.persistence.jpa.dao.repo.PlainSchemaRepo; import org.apache.syncope.core.persistence.jpa.dao.repo.PlainSchemaRepoExt; import org.apache.syncope.core.persistence.jpa.dao.repo.PlainSchemaRepoExtImpl; -import org.apache.syncope.core.persistence.jpa.dao.repo.RealmRepo; -import org.apache.syncope.core.persistence.jpa.dao.repo.RealmRepoExt; -import org.apache.syncope.core.persistence.jpa.dao.repo.RealmRepoExtImpl; import org.apache.syncope.core.persistence.jpa.dao.repo.RelationshipTypeRepo; import org.apache.syncope.core.persistence.jpa.dao.repo.RelationshipTypeRepoExt; import org.apache.syncope.core.persistence.jpa.dao.repo.RelationshipTypeRepoExtImpl; @@ -191,11 +183,7 @@ import org.apache.syncope.core.persistence.jpa.dao.repo.VirSchemaRepoExt; import org.apache.syncope.core.persistence.jpa.dao.repo.VirSchemaRepoExtImpl; import org.apache.syncope.core.persistence.jpa.dao.repo.WAConfigRepo; -import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtils; -import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtilsFactory; import org.apache.syncope.core.persistence.jpa.entity.JPAEntityFactory; -import org.apache.syncope.core.persistence.jpa.entity.am.JPAClientAppUtilsFactory; -import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPolicyUtilsFactory; import org.apache.syncope.core.persistence.jpa.entity.task.JPATaskUtilsFactory; import org.apache.syncope.core.persistence.jpa.spring.CommonEntityManagerFactoryConf; import org.apache.syncope.core.persistence.jpa.spring.DomainRoutingEntityManagerFactory; @@ -211,6 +199,7 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Lazy; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; @@ -221,9 +210,9 @@ import org.springframework.orm.jpa.SharedEntityManagerCreator; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.support.TransactionTemplate; -import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; @EnableConfigurationProperties(PersistenceProperties.class) +@Import(CommonPersistenceContext.class) @Configuration(proxyBeanMethods = false) public class PersistenceContext { @@ -294,32 +283,16 @@ public TransactionTemplate domainTransactionTemplate(final PlatformTransactionMa return new TransactionTemplate(domainTransactionManager); } - @ConditionalOnMissingBean - @Bean - public SearchCondVisitor searchCondVisitor() { - return new SearchCondVisitor(); - } - - @Bean - public Validator localValidatorFactoryBean() { - return new LocalValidatorFactoryBean(); - } - - @ConditionalOnMissingBean - @Bean - public PlainAttrValidationManager plainAttrValidationManager() { - return new DefaultPlainAttrValidationManager(); - } - @ConditionalOnMissingBean @Bean public XMLContentLoader xmlContentLoader( + final DomainHolder domainHolder, final PersistenceProperties persistenceProperties, final ResourceLoader resourceLoader, final Environment env) { return new XMLContentLoader( - persistenceProperties, + domainHolder, resourceLoader.getResource(persistenceProperties.getViewsXML()), resourceLoader.getResource(persistenceProperties.getIndexesXML()), env); @@ -328,34 +301,28 @@ public XMLContentLoader xmlContentLoader( @ConditionalOnMissingBean @Bean public XMLContentExporter xmlContentExporter( - final DomainHolder domainHolder, + final DomainHolder domainHolder, final RealmDAO realmDAO, final EntityManagerFactory entityManagerFactory) { return new XMLContentExporter(domainHolder, realmDAO, entityManagerFactory); } - @ConditionalOnMissingBean - @Bean - public KeymasterConfParamLoader keymasterConfParamLoader(final ConfParamOps confParamOps) { - return new KeymasterConfParamLoader(confParamOps); - } - @ConditionalOnMissingBean @Bean public DomainRegistry domainRegistry(final ConfigurableApplicationContext ctx) { - return new DomainConfFactory(ctx); + return new JPADomainRegistry(ctx); } @ConditionalOnMissingBean @Bean public RuntimeDomainLoader runtimeDomainLoader( - final DomainHolder domainHolder, + final DomainHolder domainHolder, final DomainRegistry domainRegistry, final DomainRoutingEntityManagerFactory entityManagerFactory, final ConfigurableApplicationContext ctx) { - return new RuntimeDomainLoader(domainHolder, domainRegistry, entityManagerFactory, ctx); + return new JPARuntimeDomainLoader(domainHolder, domainRegistry, entityManagerFactory, ctx); } @ConditionalOnMissingBean @@ -364,7 +331,7 @@ public StartupDomainLoader startupDomainLoader( final PersistenceProperties persistenceProperties, final ResourceLoader resourceLoader, final DomainOps domainOps, - final DomainHolder domainHolder, + final DomainHolder domainHolder, final DomainRegistry domainRegistry) { return new StartupDomainLoader(domainOps, domainHolder, persistenceProperties, resourceLoader, domainRegistry); @@ -376,73 +343,6 @@ public EntityFactory entityFactory() { return new JPAEntityFactory(); } - @Bean(name = "userAnyUtils") - public AnyUtils userAnyUtils( - final @Lazy UserDAO userDAO, - final @Lazy GroupDAO groupDAO, - final @Lazy AnyObjectDAO anyObjectDAO, - final @Lazy EntityFactory entityFactory) { - - return new JPAAnyUtils(userDAO, groupDAO, anyObjectDAO, entityFactory, AnyTypeKind.USER, false); - } - - @Bean(name = "linkedAccountAnyUtils") - public AnyUtils linkedAccountAnyUtils( - final @Lazy UserDAO userDAO, - final @Lazy GroupDAO groupDAO, - final @Lazy AnyObjectDAO anyObjectDAO, - final @Lazy EntityFactory entityFactory) { - - return new JPAAnyUtils(userDAO, groupDAO, anyObjectDAO, entityFactory, AnyTypeKind.USER, true); - } - - @Bean(name = "groupAnyUtils") - public AnyUtils groupAnyUtils( - final @Lazy UserDAO userDAO, - final @Lazy GroupDAO groupDAO, - final @Lazy AnyObjectDAO anyObjectDAO, - final @Lazy EntityFactory entityFactory) { - - return new JPAAnyUtils(userDAO, groupDAO, anyObjectDAO, entityFactory, AnyTypeKind.GROUP, false); - } - - @Bean(name = "anyObjectAnyUtils") - public AnyUtils anyObjectAnyUtils( - final @Lazy UserDAO userDAO, - final @Lazy GroupDAO groupDAO, - final @Lazy AnyObjectDAO anyObjectDAO, - final @Lazy EntityFactory entityFactory) { - - return new JPAAnyUtils(userDAO, groupDAO, anyObjectDAO, entityFactory, AnyTypeKind.ANY_OBJECT, false); - } - - @ConditionalOnMissingBean - @Bean - public AnyUtilsFactory anyUtilsFactory( - @Qualifier("userAnyUtils") - final AnyUtils userAnyUtils, - @Qualifier("linkedAccountAnyUtils") - final AnyUtils linkedAccountAnyUtils, - @Qualifier("groupAnyUtils") - final AnyUtils groupAnyUtils, - @Qualifier("anyObjectAnyUtils") - final AnyUtils anyObjectAnyUtils) { - - return new JPAAnyUtilsFactory(userAnyUtils, linkedAccountAnyUtils, groupAnyUtils, anyObjectAnyUtils); - } - - @ConditionalOnMissingBean - @Bean - public ClientAppUtilsFactory clientAppUtilsFactory() { - return new JPAClientAppUtilsFactory(); - } - - @ConditionalOnMissingBean - @Bean - public PolicyUtilsFactory policyUtilsFactory() { - return new JPAPolicyUtilsFactory(); - } - @ConditionalOnMissingBean @Bean public TaskUtilsFactory taskUtilsFactory() { @@ -482,7 +382,8 @@ public AnyMatchDAO anyMatchDAO( final RealmDAO realmDAO, final PlainSchemaDAO plainSchemaDAO, final AnyUtilsFactory anyUtilsFactory, - final PlainAttrValidationManager validator) { + final PlainAttrValidationManager validator, + final EntityFactory entityFactory) { return new JPAAnyMatchDAO( userDAO, @@ -491,7 +392,8 @@ public AnyMatchDAO anyMatchDAO( realmDAO, plainSchemaDAO, anyUtilsFactory, - validator); + validator, + entityFactory); } @ConditionalOnMissingBean @@ -619,17 +521,14 @@ public ApplicationDAO applicationDAO( @ConditionalOnMissingBean @Bean - public AuditConfRepoExt auditConfRepoExt(final EntityManager entityManager) { - return new AuditConfRepoExtImpl(entityManager); + public AuditConfDAO auditConfDAO(final JpaRepositoryFactory jpaRepositoryFactory) { + return jpaRepositoryFactory.getRepository(AuditConfRepo.class); } @ConditionalOnMissingBean @Bean - public AuditConfDAO auditConfDAO( - final JpaRepositoryFactory jpaRepositoryFactory, - final AuditConfRepoExt auditConfRepoExt) { - - return jpaRepositoryFactory.getRepository(AuditConfRepo.class, auditConfRepoExt); + public AuditEntryDAO auditEntryDAO(final EntityManager entityManager) { + return new JPAAuditEntryDAO(entityManager); } @ConditionalOnMissingBean @@ -738,7 +637,7 @@ public DynRealmRepoExt dynRealmRepoExt( final @Lazy UserDAO userDAO, final @Lazy GroupDAO groupDAO, final @Lazy AnyObjectDAO anyObjectDAO, - final AnySearchDAO searchDAO, + final AnySearchDAO anySearchDAO, final AnyMatchDAO anyMatchDAO, final SearchCondVisitor searchCondVisitor, final EntityManager entityManager) { @@ -748,7 +647,7 @@ public DynRealmRepoExt dynRealmRepoExt( userDAO, groupDAO, anyObjectDAO, - searchDAO, + anySearchDAO, anyMatchDAO, searchCondVisitor, entityManager); @@ -944,23 +843,14 @@ public PolicyDAO policyDAO( entityManager); } - @ConditionalOnMissingBean - @Bean - public RealmRepoExt realmRepoExt( + @ConditionalOnMissingBean(name = { "realmDAO", "delegateRealmDAO" }) + @Bean(name = { "realmDAO", "delegateRealmDAO" }) + public RealmDAO realmDAO( final @Lazy RoleDAO roleDAO, final ApplicationEventPublisher publisher, final EntityManager entityManager) { - return new RealmRepoExtImpl(roleDAO, publisher, entityManager); - } - - @ConditionalOnMissingBean - @Bean - public RealmDAO realmDAO( - final JpaRepositoryFactory jpaRepositoryFactory, - final RealmRepoExt realmRepoExt) { - - return jpaRepositoryFactory.getRepository(RealmRepo.class, realmRepoExt); + return new JPARealmDAO(roleDAO, publisher, entityManager); } @ConditionalOnMissingBean diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java index 03fd3c4e46..7559dccbb1 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java @@ -18,13 +18,11 @@ */ package org.apache.syncope.core.persistence.jpa; -import java.util.ArrayList; -import java.util.List; +import org.apache.syncope.core.persistence.common.AbstractPersistenceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.NestedConfigurationProperty; @ConfigurationProperties("persistence") -public class PersistenceProperties { +public class PersistenceProperties extends AbstractPersistenceProperties { private String remoteCommitProvider = "sjvm"; @@ -32,11 +30,6 @@ public class PersistenceProperties { private String viewsXML = "classpath:views.xml"; - private String indexesXML = "classpath:indexes.xml"; - - @NestedConfigurationProperty - private final List domain = new ArrayList<>(); - public String getRemoteCommitProvider() { return remoteCommitProvider; } @@ -60,16 +53,4 @@ public String getViewsXML() { public void setViewsXML(final String viewsXML) { this.viewsXML = viewsXML; } - - public String getIndexesXML() { - return indexesXML; - } - - public void setIndexesXML(final String indexesXML) { - this.indexesXML = indexesXML; - } - - public List getDomain() { - return domain; - } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/StartupDomainLoader.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/StartupDomainLoader.java index d3e335f94f..8ceae5ec76 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/StartupDomainLoader.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/StartupDomainLoader.java @@ -40,7 +40,7 @@ public class StartupDomainLoader implements SyncopeCoreLoader { protected final DomainOps domainOps; - protected final DomainHolder domainHolder; + protected final DomainHolder domainHolder; protected final PersistenceProperties persistenceProperties; @@ -50,7 +50,7 @@ public class StartupDomainLoader implements SyncopeCoreLoader { public StartupDomainLoader( final DomainOps domainOps, - final DomainHolder domainHolder, + final DomainHolder domainHolder, final PersistenceProperties persistenceProperties, final ResourceLoader resourceLoader, final DomainRegistry domainRegistry) { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/ContentLoaderHandler.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/ContentLoaderHandler.java index 42b98b3654..1c3b3eb684 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/ContentLoaderHandler.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/ContentLoaderHandler.java @@ -25,54 +25,36 @@ import java.util.Map; import java.util.Objects; import javax.sql.DataSource; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringEscapeUtils; -import org.apache.commons.text.StringSubstitutor; -import org.apache.syncope.core.provisioning.api.utils.FormatUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.syncope.core.persistence.api.utils.FormatUtils; +import org.apache.syncope.core.persistence.common.content.AbstractContentLoaderHandler; import org.springframework.core.env.Environment; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; /** * SAX handler for generating SQL INSERT statements out of given XML file. */ -public class ContentLoaderHandler extends DefaultHandler { - - private static final Logger LOG = LoggerFactory.getLogger(ContentLoaderHandler.class); - - private static final String CONF_DIR = "syncope.conf.dir"; +public class ContentLoaderHandler extends AbstractContentLoaderHandler { private final JdbcTemplate jdbcTemplate; - private final String rootElement; - - private final boolean continueOnError; - - private final Map fetches = new HashMap<>(); - - private final StringSubstitutor paramSubstitutor; - public ContentLoaderHandler( final DataSource dataSource, final String rootElement, final boolean continueOnError, final Environment env) { + super(rootElement, continueOnError, env); this.jdbcTemplate = new JdbcTemplate(dataSource); - this.rootElement = rootElement; - this.continueOnError = continueOnError; - this.paramSubstitutor = new StringSubstitutor(key -> { - String value = env.getProperty(key, fetches.get(key)); - if (value != null && CONF_DIR.equals(key)) { - value = value.replace('\\', '/'); - } - return StringUtils.isBlank(value) ? null : value; - }); + } + + @Override + protected void fetch(final Attributes atts) { + String value = jdbcTemplate.queryForObject(atts.getValue("query"), String.class); + String key = atts.getValue("key"); + fetches.put(key, value); } private Object[] getParameters(final String tableName, final Attributes attrs) { @@ -190,39 +172,27 @@ private Object[] getParameters(final String tableName, final Attributes attrs) { } @Override - public void startElement(final String uri, final String localName, final String qName, final Attributes atts) - throws SAXException { + protected void create(final String qName, final Attributes atts) { + StringBuilder query = new StringBuilder("INSERT INTO ").append(qName).append('('); - // skip root element - if (rootElement.equals(qName)) { - return; - } - if ("fetch".equalsIgnoreCase(qName)) { - String value = jdbcTemplate.queryForObject(atts.getValue("query"), String.class); - String key = atts.getValue("key"); - fetches.put(key, value); - } else { - StringBuilder query = new StringBuilder("INSERT INTO ").append(qName).append('('); - - StringBuilder values = new StringBuilder(); - - for (int i = 0; i < atts.getLength(); i++) { - query.append(atts.getQName(i)); - values.append('?'); - if (i < atts.getLength() - 1) { - query.append(','); - values.append(','); - } + StringBuilder values = new StringBuilder(); + + for (int i = 0; i < atts.getLength(); i++) { + query.append(atts.getQName(i)); + values.append('?'); + if (i < atts.getLength() - 1) { + query.append(','); + values.append(','); } - query.append(") VALUES (").append(values).append(')'); - - try { - jdbcTemplate.update(query.toString(), getParameters(qName, atts)); - } catch (DataAccessException e) { - LOG.error("While trying to perform {} with params {}", query, getParameters(qName, atts), e); - if (!continueOnError) { - throw e; - } + } + query.append(") VALUES (").append(values).append(')'); + + try { + jdbcTemplate.update(query.toString(), getParameters(qName, atts)); + } catch (DataAccessException e) { + LOG.error("While trying to perform {} with params {}", query, getParameters(qName, atts), e); + if (!continueOnError) { + throw e; } } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java index b09f7ceb71..00954a0e6a 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java @@ -31,7 +31,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; -import java.nio.charset.StandardCharsets; import java.sql.Blob; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -58,13 +57,8 @@ import java.util.function.Supplier; import java.util.stream.Stream; import javax.sql.DataSource; -import javax.xml.XMLConstants; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; -import javax.xml.transform.sax.SAXTransformerFactory; import javax.xml.transform.sax.TransformerHandler; -import javax.xml.transform.stream.StreamResult; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.cxf.helpers.IOUtils; @@ -72,14 +66,14 @@ import org.apache.openjpa.lib.util.collections.DualHashBidiMap; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.core.persistence.api.DomainHolder; -import org.apache.syncope.core.persistence.api.content.ContentExporter; -import org.apache.syncope.core.persistence.api.dao.AuditConfDAO; +import org.apache.syncope.core.persistence.api.dao.AuditEntryDAO; import org.apache.syncope.core.persistence.api.dao.RealmDAO; +import org.apache.syncope.core.persistence.api.utils.FormatUtils; +import org.apache.syncope.core.persistence.common.content.AbstractXMLContentExporter; +import org.apache.syncope.core.persistence.common.content.MultiParentNode; +import org.apache.syncope.core.persistence.common.content.MultiParentNodeOp; import org.apache.syncope.core.persistence.jpa.entity.JPARealm; -import org.apache.syncope.core.provisioning.api.utils.FormatUtils; import org.apache.syncope.core.spring.ApplicationContextProvider; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.data.domain.Pageable; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceUtils; @@ -91,12 +85,9 @@ /** * Export internal storage content as XML. */ -public class XMLContentExporter implements ContentExporter { +public class XMLContentExporter extends AbstractXMLContentExporter { - protected static final Logger LOG = LoggerFactory.getLogger(XMLContentExporter.class); - - protected static final Set TABLE_PREFIXES_TO_BE_EXCLUDED = Set.of( - "QRTZ_", AuditConfDAO.AUDIT_ENTRY_TABLE); + protected static final Set TABLE_PREFIXES_TO_BE_EXCLUDED = Set.of("QRTZ_", AuditEntryDAO.TABLE); protected static boolean isTableAllowed(final String tableName) { return TABLE_PREFIXES_TO_BE_EXCLUDED.stream(). @@ -214,12 +205,10 @@ protected static List sortByForeignKeys( final Connection conn, final String schema, final Set tableNames) throws SQLException { - Set> roots = new HashSet<>(); - DatabaseMetaData meta = conn.getMetaData(); + Set> roots = new HashSet<>(); Map> exploited = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - Set pkTableNames = new HashSet<>(); for (String tableName : tableNames) { MultiParentNode node = Optional.ofNullable(exploited.get(tableName)).orElseGet(() -> { @@ -229,7 +218,7 @@ protected static List sortByForeignKeys( return n; }); - pkTableNames.clear(); + Set pkTableNames = new HashSet<>(); try (ResultSet rs = meta.getImportedKeys(conn.getCatalog(), schema, tableName)) { // this is to avoid repetition while (rs.next()) { @@ -271,14 +260,14 @@ protected static List sortByForeignKeys( return sortedTableNames; } - protected final DomainHolder domainHolder; + protected final DomainHolder domainHolder; protected final RealmDAO realmDAO; protected final EntityManagerFactory entityManagerFactory; public XMLContentExporter( - final DomainHolder domainHolder, + final DomainHolder domainHolder, final RealmDAO realmDAO, final EntityManagerFactory entityManagerFactory) { @@ -386,21 +375,11 @@ protected void exportTable( @Override public void export( final String domain, - final int tableThreshold, + final int threshold, final OutputStream os) throws SAXException, TransformerConfigurationException { - StreamResult streamResult = new StreamResult(os); - SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance(); - transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - - TransformerHandler handler = transformerFactory.newTransformerHandler(); - Transformer serializer = handler.getTransformer(); - serializer.setOutputProperty(OutputKeys.ENCODING, StandardCharsets.UTF_8.name()); - serializer.setOutputProperty(OutputKeys.INDENT, "yes"); - handler.setResult(streamResult); - handler.startDocument(); - handler.startElement("", "", ROOT_ELEMENT, new AttributesImpl()); + TransformerHandler handler = start(os); DataSource dataSource = Optional.ofNullable(domainHolder.getDomains().get(domain)). orElseThrow(() -> new IllegalArgumentException("Could not find DataSource for domain " + domain)); @@ -437,7 +416,7 @@ public void export( // then sort tables based on foreign keys and dump for (String tableName : sortByForeignKeys(conn, schema, tableNames)) { try { - exportTable(dataSource, tableName, tableThreshold, entities, relationTables(entities), handler); + exportTable(dataSource, tableName, threshold, entities, relationTables(entities), handler); } catch (Exception e) { LOG.error("Failure exporting table {}", tableName, e); } @@ -448,7 +427,6 @@ public void export( DataSourceUtils.releaseConnection(conn, dataSource); } - handler.endElement("", "", ROOT_ELEMENT); - handler.endDocument(); + end(handler); } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentLoader.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentLoader.java index 4e134e1cbc..5c95efb6a6 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentLoader.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentLoader.java @@ -22,60 +22,42 @@ import java.io.InputStream; import java.util.Properties; import javax.sql.DataSource; -import javax.xml.XMLConstants; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; -import org.apache.syncope.core.persistence.api.content.ContentLoader; -import org.apache.syncope.core.persistence.jpa.PersistenceProperties; +import org.apache.syncope.core.persistence.api.DomainHolder; +import org.apache.syncope.core.persistence.common.content.AbstractXMLContentLoader; import org.apache.syncope.core.persistence.jpa.entity.JPARealm; import org.apache.syncope.core.spring.ApplicationContextProvider; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; -import org.xml.sax.SAXException; /** * Initialize Database with default content if no data is present already. */ -public class XMLContentLoader implements ContentLoader { +public class XMLContentLoader extends AbstractXMLContentLoader { - protected static final Logger LOG = LoggerFactory.getLogger(XMLContentLoader.class); - - protected final PersistenceProperties persistenceProperties; + protected final DomainHolder domainHolder; protected final Resource viewsXML; protected final Resource indexesXML; - protected final Environment env; - public XMLContentLoader( - final PersistenceProperties persistenceProperties, + final DomainHolder domainHolder, final Resource viewsXML, final Resource indexesXML, final Environment env) { - this.persistenceProperties = persistenceProperties; + super(env); + this.domainHolder = domainHolder; this.viewsXML = viewsXML; this.indexesXML = indexesXML; - this.env = env; } @Override - public int getOrder() { - return 400; - } - - @Override - public void load(final String domain, final DataSource datasource) { - LOG.debug("Loading data for domain [{}]", domain); - - JdbcTemplate jdbcTemplate = new JdbcTemplate(datasource); + protected boolean existingData(final String domain) { + JdbcTemplate jdbcTemplate = new JdbcTemplate(domainHolder.getDomains().get(domain)); boolean existingData; try { existingData = jdbcTemplate.queryForObject("SELECT COUNT(0) FROM " + JPARealm.TABLE, Integer.class) > 0; @@ -83,50 +65,14 @@ public void load(final String domain, final DataSource datasource) { LOG.error("[{}] Could not access table {}", domain, JPARealm.TABLE, e); existingData = true; } - - if (existingData) { - LOG.info("[{}] Data found in the database, leaving untouched", domain); - } else { - LOG.info("[{}] Empty database found, loading default content", domain); - - try { - createViews(domain, datasource); - } catch (IOException e) { - LOG.error("[{}] While creating views", domain, e); - } - try { - createIndexes(domain, datasource); - } catch (IOException e) { - LOG.error("[{}] While creating indexes", domain, e); - } - try { - InputStream contentXML = ApplicationContextProvider.getBeanFactory(). - getBean(domain + "ContentXML", InputStream.class); - loadDefaultContent(domain, contentXML, datasource); - } catch (Exception e) { - LOG.error("[{}] While loading default content", domain, e); - } - } + return existingData; } - protected void loadDefaultContent( - final String domain, final InputStream contentXML, final DataSource dataSource) - throws IOException, ParserConfigurationException, SAXException { - - SAXParserFactory factory = SAXParserFactory.newInstance(); - factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE); - factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - try (contentXML) { - SAXParser parser = factory.newSAXParser(); - parser.parse(contentXML, new ContentLoaderHandler(dataSource, ROOT_ELEMENT, true, env)); - LOG.debug("[{}] Default content successfully loaded", domain); - } - } - - protected void createViews(final String domain, final DataSource dataSource) throws IOException { + @Override + protected void createViews(final String domain) throws IOException { LOG.debug("[{}] Creating views", domain); - JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); + JdbcTemplate jdbcTemplate = new JdbcTemplate(domainHolder.getDomains().get(domain)); Properties views = PropertiesLoaderUtils.loadProperties(viewsXML); views.stringPropertyNames().stream().sorted().forEach(idx -> { @@ -141,10 +87,11 @@ protected void createViews(final String domain, final DataSource dataSource) thr LOG.debug("Views created"); } - protected void createIndexes(final String domain, final DataSource dataSource) throws IOException { + @Override + protected void createIndexes(final String domain) throws IOException { LOG.debug("[{}] Creating indexes", domain); - JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); + JdbcTemplate jdbcTemplate = new JdbcTemplate(domainHolder.getDomains().get(domain)); Properties indexes = PropertiesLoaderUtils.loadProperties(indexesXML); indexes.stringPropertyNames().stream().sorted().forEach(idx -> { @@ -158,4 +105,14 @@ protected void createIndexes(final String domain, final DataSource dataSource) t LOG.debug("Indexes created"); } + + @Override + protected void loadDefaultContent(final String domain, final String contentXML) throws Exception { + InputStream in = ApplicationContextProvider.getBeanFactory().getBean(contentXML, InputStream.class); + try (in) { + saxParser().parse(in, new ContentLoaderHandler( + domainHolder.getDomains().get(domain), ROOT_ELEMENT, true, env)); + LOG.debug("[{}] Default content successfully loaded", domain); + } + } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyMatchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyMatchDAO.java index 337eaaad06..46f97e0106 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyMatchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyMatchDAO.java @@ -19,76 +19,23 @@ package org.apache.syncope.core.persistence.jpa.dao; import jakarta.persistence.Entity; -import jakarta.validation.ValidationException; -import jakarta.validation.constraints.Max; -import jakarta.validation.constraints.Min; import java.beans.PropertyDescriptor; -import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import java.time.OffsetDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.ClassUtils; -import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.AttrSchemaType; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; -import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; 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.NotFoundException; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; 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.dao.search.AnyCond; -import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond; -import org.apache.syncope.core.persistence.api.dao.search.AttrCond; -import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond; -import org.apache.syncope.core.persistence.api.dao.search.MemberCond; -import org.apache.syncope.core.persistence.api.dao.search.MembershipCond; -import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond; -import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond; -import org.apache.syncope.core.persistence.api.dao.search.ResourceCond; -import org.apache.syncope.core.persistence.api.dao.search.RoleCond; -import org.apache.syncope.core.persistence.api.dao.search.SearchCond; -import org.apache.syncope.core.persistence.api.entity.Any; -import org.apache.syncope.core.persistence.api.entity.AnyUtils; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; -import org.apache.syncope.core.persistence.api.entity.GroupableRelatable; -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.EntityFactory; import org.apache.syncope.core.persistence.api.entity.PlainSchema; -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.entity.JPAPlainSchema; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.BeanUtils; -import org.springframework.transaction.annotation.Transactional; +import org.apache.syncope.core.persistence.common.dao.AbstractAnyMatchDAO; -public class JPAAnyMatchDAO implements AnyMatchDAO { - - protected static final Logger LOG = LoggerFactory.getLogger(AnyMatchDAO.class); - - protected final UserDAO userDAO; - - protected final GroupDAO groupDAO; - - protected final AnyObjectDAO anyObjectDAO; - - protected final RealmDAO realmDAO; - - protected final PlainSchemaDAO plainSchemaDAO; - - protected final AnyUtilsFactory anyUtilsFactory; - - protected final PlainAttrValidationManager validator; +public class JPAAnyMatchDAO extends AbstractAnyMatchDAO { public JPAAnyMatchDAO( final UserDAO userDAO, @@ -97,414 +44,30 @@ public JPAAnyMatchDAO( final RealmDAO realmDAO, final PlainSchemaDAO plainSchemaDAO, final AnyUtilsFactory anyUtilsFactory, - final PlainAttrValidationManager validator) { + final PlainAttrValidationManager validator, + final EntityFactory entityFactory) { - this.userDAO = userDAO; - this.groupDAO = groupDAO; - this.anyObjectDAO = anyObjectDAO; - this.realmDAO = realmDAO; - this.plainSchemaDAO = plainSchemaDAO; - this.anyUtilsFactory = anyUtilsFactory; - this.validator = validator; + super(userDAO, groupDAO, anyObjectDAO, realmDAO, plainSchemaDAO, anyUtilsFactory, validator, entityFactory); } - /** - * Verify if any matches the given search condition. - * - * @param any to be checked - * @param cond to be verified - * @param any - * @return true if any matches cond - */ - @Transactional(readOnly = true) @Override - public > boolean matches(final T any, final SearchCond cond) { - boolean not = cond.getType() == SearchCond.Type.NOT_LEAF; - switch (cond.getType()) { - case LEAF, NOT_LEAF -> { - Boolean match = cond.getLeaf(AnyTypeCond.class). - filter(leaf -> AnyTypeKind.ANY_OBJECT == any.getType().getKind()). - map(leaf -> matches(any, leaf, not)). - orElse(null); - - if (match == null) { - match = cond.getLeaf(RelationshipTypeCond.class). - filter(leaf -> any instanceof GroupableRelatable). - map(leaf -> matches((GroupableRelatable) any, leaf, not)). - orElse(null); - } - - if (match == null) { - match = cond.getLeaf(RelationshipCond.class). - filter(leaf -> any instanceof GroupableRelatable). - map(leaf -> matches((GroupableRelatable) any, leaf, not)). - orElse(null); - } - - if (match == null) { - match = cond.getLeaf(MembershipCond.class). - filter(leaf -> any instanceof GroupableRelatable). - map(leaf -> matches((GroupableRelatable) any, leaf, not)). - orElse(null); - } - - if (match == null) { - match = cond.getLeaf(RoleCond.class). - filter(leaf -> any instanceof User). - map(leaf -> matches((User) any, leaf, not)). - orElse(null); - } - - if (match == null) { - match = cond.getLeaf(DynRealmCond.class). - map(leaf -> matches(any, leaf, not)). - orElse(null); - } - - if (match == null) { - match = cond.getLeaf(MemberCond.class). - filter(leaf -> any instanceof Group). - map(leaf -> matches((Group) any, leaf, not)). - orElse(null); - } - - if (match == null) { - match = cond.getLeaf(ResourceCond.class). - map(leaf -> matches(any, leaf, not)). - orElse(null); - } - - if (match == null) { - match = cond.getLeaf(AnyCond.class). - map(value -> matches(any, value, not)). - orElseGet(() -> cond.getLeaf(AttrCond.class). - map(leaf -> matches(any, leaf, not)). - orElse(null)); - } - - if (match == null) { - match = cond.getLeaf(AttrCond.class). - map(leaf -> matches(any, leaf, not)). - orElse(null); - } - - return BooleanUtils.toBoolean(match); - } - case AND -> { - return matches(any, cond.getLeft()) && matches(any, cond.getRight()); - } - - case OR -> { - return matches(any, cond.getLeft()) || matches(any, cond.getRight()); - } - - default -> { - } - } - - return false; - } - - protected boolean matches(final Any any, final AnyTypeCond cond, final boolean not) { - boolean equals = any.getType().getKey().equals(cond.getAnyTypeKey()); - return not ? !equals : equals; - } - - protected boolean matches( - final GroupableRelatable any, final RelationshipTypeCond cond, final boolean not) { - - boolean found = any.getRelationships().stream(). - anyMatch(rel -> rel.getType().getKey().equals(cond.getRelationshipTypeKey())); - return not ? !found : found; - } - - protected boolean matches( - final GroupableRelatable any, final RelationshipCond cond, final boolean not) { - - Set candidates = SyncopeConstants.UUID_PATTERN.matcher(cond.getAnyObject()).matches() - ? Optional.ofNullable(cond.getAnyObject()).map(Set::of).orElse(Set.of()) - : anyObjectDAO.findByName(cond.getAnyObject()).stream(). - map(AnyObject::getKey).collect(Collectors.toSet()); - - boolean found = any.getRelationships().stream(). - map(r -> r.getRightEnd().getKey()). - filter(candidates::contains). - count() > 0; - - return not ? !found : found; - } - - protected boolean matches( - final GroupableRelatable any, final MembershipCond cond, final boolean not) { - - final String group = SyncopeConstants.UUID_PATTERN.matcher(cond.getGroup()).matches() - ? cond.getGroup() - : groupDAO.findKey(cond.getGroup()). - orElseThrow(() -> new NotFoundException("Group " + cond.getGroup())); - - boolean found = any.getMembership(group).isPresent() - || (any instanceof User - ? userDAO.findDynGroups(any.getKey()) - : anyObjectDAO.findDynGroups(any.getKey())).stream(). - anyMatch(item -> item.getKey().equals(group)); - return not ? !found : found; - } - - protected boolean matches(final User user, final RoleCond cond, final boolean not) { - boolean found = userDAO.findAllRoles(user).stream().anyMatch(role -> role.getKey().equals(cond.getRole())); - return not ? !found : found; - } - - protected boolean matches(final Any any, final DynRealmCond cond, final boolean not) { - boolean found = anyUtilsFactory.getInstance(any).dao().findDynRealms(any.getKey()).stream(). - anyMatch(dynRealm -> dynRealm.equals(cond.getDynRealm())); - return not ? !found : found; - } - - protected boolean matches(final Group group, final MemberCond cond, final boolean not) { - boolean found = false; - - GroupableRelatable any = userDAO.findById(cond.getMember()).orElse(null); - if (any == null) { - any = anyObjectDAO.findById(cond.getMember()).orElse(null); - if (any != null) { - found = groupDAO.findAMemberships(group).stream(). - anyMatch(memb -> memb.getLeftEnd().getKey().equals(cond.getMember())) - || groupDAO.findADynMembers(group).contains(cond.getMember()); - } - } else { - found = groupDAO.findUMemberships(group).stream(). - anyMatch(memb -> memb.getLeftEnd().getKey().equals(cond.getMember())) - || groupDAO.findUDynMembers(group).contains(cond.getMember()); - } - - return not ? !found : found; - } - - protected boolean matches(final Any any, final ResourceCond cond, final boolean not) { - boolean found = anyUtilsFactory.getInstance(any).getAllResources(any).stream(). - anyMatch(resource -> resource.getKey().equals(cond.getResource())); - return not ? !found : found; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - protected boolean matches( - final List anyAttrValues, - final PlainAttrValue attrValue, - final PlainSchema schema, - final AttrCond cond) { - - return anyAttrValues.stream().anyMatch(item -> { - switch (cond.getType()) { - case EQ -> { - return attrValue.getValue().equals(item.getValue()); - } - - case IEQ -> { - if (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum) { - return attrValue.getStringValue().equalsIgnoreCase(item.getStringValue()); - } else { - LOG.error("IEQ is only compatible with string or enum schemas"); - return false; - } - } - - case LIKE, ILIKE -> { - if (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum) { - StringBuilder output = new StringBuilder(); - for (char c : cond.getExpression().toLowerCase().toCharArray()) { - if (c == '%') { - output.append(".*"); - } else if (Character.isLetter(c)) { - output.append('['). - append(c). - append(Character.toUpperCase(c)). - append(']'); - } else { - output.append(c); - } - } - return (cond.getType() == AttrCond.Type.LIKE - ? Pattern.compile(output.toString()) - : Pattern.compile(output.toString(), Pattern.CASE_INSENSITIVE)). - matcher(item.getStringValue()).matches(); - } else { - LOG.error("LIKE is only compatible with string or enum schemas"); - return false; - } - } - case GT -> { - return item.getValue().compareTo(attrValue.getValue()) > 0; - } - - case GE -> { - return item.getValue().compareTo(attrValue.getValue()) >= 0; - } - - case LT -> { - return item.getValue().compareTo(attrValue.getValue()) < 0; - } - - case LE -> { - return item.getValue().compareTo(attrValue.getValue()) <= 0; - } - - default -> { - return false; - } + protected void relationshipFieldMatches( + final PropertyDescriptor pd, + final AnyCond cond, + final PlainSchema schema) { + + if (pd.getPropertyType().getAnnotation(Entity.class) != null) { + Method relMethod = null; + try { + relMethod = ClassUtils.getPublicMethod(pd.getPropertyType(), "getKey", new Class[0]); + } catch (Exception e) { + LOG.error("Could not find {}#getKey", pd.getPropertyType(), e); } - }); - } - - protected boolean matches(final Any any, final AttrCond cond, final boolean not) { - PlainSchema schema = plainSchemaDAO.findById(cond.getSchema()).orElse(null); - if (schema == null) { - LOG.warn("Ignoring invalid schema '{}'", cond.getSchema()); - return false; - } - - @SuppressWarnings("unchecked") - Optional> attr = (Optional>) any.getPlainAttr(cond.getSchema()); - - boolean found; - switch (cond.getType()) { - case ISNULL: - found = attr.isEmpty(); - break; - case ISNOTNULL: - found = attr.isPresent(); - break; - - default: - PlainAttrValue attrValue = anyUtilsFactory.getInstance(any).newPlainAttrValue(); - try { - if (cond.getType() != AttrCond.Type.LIKE - && cond.getType() != AttrCond.Type.ILIKE - && cond.getType() != AttrCond.Type.ISNULL - && cond.getType() != AttrCond.Type.ISNOTNULL) { - - validator.validate(schema, cond.getExpression(), attrValue); - } - } catch (ValidationException e) { - LOG.error("Could not validate expression '" + cond.getExpression() + '\'', e); - return false; - } - - found = attr.map(a -> matches(a.getValues(), attrValue, schema, cond)).orElse(false); - } - return not ? !found : found; - } - - protected boolean matches(final Any any, final AnyCond cond, final boolean not) { - // Keeps track of difference between entity's getKey() and JPA @Id fields - if ("key".equals(cond.getSchema())) { - cond.setSchema("id"); - } - - PropertyDescriptor pd; - Object anyAttrValue; - try { - pd = BeanUtils.getPropertyDescriptor(any.getClass(), cond.getSchema()); - if (pd == null) { - LOG.warn("Ignoring invalid schema '{}'", cond.getSchema()); - return false; + if (relMethod != null && String.class.isAssignableFrom(relMethod.getReturnType())) { + cond.setSchema(cond.getSchema() + "_id"); + schema.setType(AttrSchemaType.String); } - - anyAttrValue = pd.getReadMethod().invoke(any); - } catch (Exception e) { - LOG.error("While accessing {}.{}", any, cond.getSchema(), e); - return false; - } - - boolean found; - switch (cond.getType()) { - case ISNULL: - found = anyAttrValue == null; - break; - - case ISNOTNULL: - found = anyAttrValue != null; - break; - - default: - PlainSchema schema = new JPAPlainSchema(); - schema.setKey(pd.getName()); - for (AttrSchemaType attrSchemaType : AttrSchemaType.values()) { - if (pd.getPropertyType().isAssignableFrom(attrSchemaType.getType())) { - schema.setType(attrSchemaType); - } - } - - // Deal with any Integer fields logically mapping to boolean values - boolean foundBooleanMin = false; - boolean foundBooleanMax = false; - if (Integer.class.equals(pd.getPropertyType())) { - for (Annotation annotation : pd.getPropertyType().getAnnotations()) { - if (Min.class.equals(annotation.annotationType())) { - foundBooleanMin = ((Min) annotation).value() == 0; - } else if (Max.class.equals(annotation.annotationType())) { - foundBooleanMax = ((Max) annotation).value() == 1; - } - } - } - if (foundBooleanMin && foundBooleanMax) { - schema.setType(AttrSchemaType.Boolean); - } - - // Deal with any fields representing relationships to other entities - if (pd.getPropertyType().getAnnotation(Entity.class) != null) { - Method relMethod = null; - try { - relMethod = ClassUtils.getPublicMethod(pd.getPropertyType(), "getKey", new Class[0]); - } catch (Exception e) { - LOG.error("Could not find {}#getKey", pd.getPropertyType(), e); - } - - if (relMethod != null && String.class.isAssignableFrom(relMethod.getReturnType())) { - cond.setSchema(cond.getSchema() + "_id"); - schema.setType(AttrSchemaType.String); - } - } - - AnyUtils anyUtils = anyUtilsFactory.getInstance(any); - - PlainAttrValue attrValue = anyUtils.newPlainAttrValue(); - if (cond.getType() != AttrCond.Type.LIKE - && cond.getType() != AttrCond.Type.ILIKE - && cond.getType() != AttrCond.Type.ISNULL - && cond.getType() != AttrCond.Type.ISNOTNULL) { - - try { - validator.validate(schema, cond.getExpression(), attrValue); - } catch (ValidationException e) { - LOG.error("Could not validate expression '" + cond.getExpression() + '\'', e); - return false; - } - } - - List anyAttrValues = new ArrayList<>(); - anyAttrValues.add(anyUtils.newPlainAttrValue()); - switch (anyAttrValue) { - case String aString -> - anyAttrValues.get(0).setStringValue(aString); - case Long aLong -> - anyAttrValues.get(0).setLongValue(aLong); - case Double aDouble -> - anyAttrValues.get(0).setDoubleValue(aDouble); - case Boolean aBoolean -> - anyAttrValues.get(0).setBooleanValue(aBoolean); - case OffsetDateTime offsetDateTime -> - anyAttrValues.get(0).setDateValue(offsetDateTime); - case byte[] bytea -> - anyAttrValues.get(0).setBinaryValue(bytea); - default -> { - } - } - - found = matches(anyAttrValues, attrValue, schema, cond); - } - return not ? !found : found; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java index 54ce5e815a..6da93ac55e 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java @@ -43,7 +43,7 @@ import org.apache.syncope.common.lib.types.AttrSchemaType; import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.common.rest.api.service.JAXRSService; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; @@ -70,7 +70,8 @@ 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.provisioning.api.utils.RealmUtils; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; +import org.apache.syncope.core.persistence.common.dao.AbstractAnySearchDAO; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -87,6 +88,21 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO { private static final Map IS_ORACLE = new ConcurrentHashMap<>(); + protected static int setParameter(final List parameters, final Object parameter) { + parameters.add(parameter); + return parameters.size(); + } + + protected static void fillWithParameters(final Query query, final List parameters) { + for (int i = 0; i < parameters.size(); i++) { + if (parameters.get(i) instanceof Boolean aBoolean) { + query.setParameter(i + 1, aBoolean ? 1 : 0); + } else { + query.setParameter(i + 1, parameters.get(i)); + } + } + } + protected final EntityManagerFactory entityManagerFactory; protected final EntityManager entityManager; @@ -192,7 +208,7 @@ SearchSupport buildSearchSupport(final AnyTypeKind kind) { } @Override - protected int doCount( + protected long doCount( final Realm base, final boolean recursive, final Set adminRealms, @@ -223,7 +239,7 @@ protected int doCount( Query countQuery = entityManager.createNativeQuery(queryString.toString()); fillWithParameters(countQuery, parameters); - return ((Number) countQuery.getSingleResult()).intValue(); + return ((Number) countQuery.getSingleResult()).longValue(); } @Override @@ -236,11 +252,9 @@ protected > List doSearch( final Pageable pageable, final AnyTypeKind kind) { + List parameters = new ArrayList<>(); + SearchSupport svs = buildSearchSupport(kind); try { - List parameters = new ArrayList<>(); - - SearchSupport svs = buildSearchSupport(kind); - Triple, Set> filter = getAdminRealmsFilter(base, recursive, adminRealms, svs, parameters); @@ -289,21 +303,6 @@ protected > List doSearch( return List.of(); } - protected int setParameter(final List parameters, final Object parameter) { - parameters.add(parameter); - return parameters.size(); - } - - protected void fillWithParameters(final Query query, final List parameters) { - for (int i = 0; i < parameters.size(); i++) { - if (parameters.get(i) instanceof Boolean aBoolean) { - query.setParameter(i + 1, aBoolean ? 1 : 0); - } else { - query.setParameter(i + 1, parameters.get(i)); - } - } - } - protected StringBuilder buildSelect(final OrderBySupport obs) { StringBuilder select = new StringBuilder("SELECT DISTINCT u.any_id"); @@ -397,36 +396,6 @@ protected StringBuilder buildOrderBy(final OrderBySupport obs) { return orderBy; } - protected String key(final AttrSchemaType schemaType) { - String key; - switch (schemaType) { - case Boolean: - key = "booleanValue"; - break; - - case Date: - key = "dateValue"; - break; - - case Double: - key = "doubleValue"; - break; - - case Long: - key = "longValue"; - break; - - case Binary: - key = "binaryValue"; - break; - - default: - key = "stringValue"; - } - - return key; - } - protected void parseOrderByForPlainSchema( final SearchSupport svs, final OrderBySupport obs, @@ -498,7 +467,19 @@ protected OrderBySupport parseOrderBy( parseOrderByForCustom(svs, clause, item, obs); if (item.isEmpty()) { - if (anyUtils.getField(clause.getProperty()) == null) { + if (anyUtils.getField(clause.getProperty()).isPresent()) { + // Manage difference among external key attribute and internal JPA @Id + String fieldName = "key".equals(clause.getProperty()) ? "id" : clause.getProperty(); + + // Adjust field name to column name + if (ArrayUtils.contains(RELATIONSHIP_FIELDS, fieldName)) { + fieldName += "_id"; + } + + obs.views.add(svs.field()); + + parseOrderByForField(svs, item, fieldName, clause); + } else { Optional schema = plainSchemaDAO.findById(clause.getProperty()); if (schema.isPresent()) { if (schema.get().isUniqueConstraint()) { @@ -516,18 +497,6 @@ protected OrderBySupport parseOrderBy( } parseOrderByForPlainSchema(svs, obs, item, clause, schema.get(), clause.getProperty()); } - } else { - // Manage difference among external key attribute and internal JPA @Id - String fieldName = "key".equals(clause.getProperty()) ? "id" : clause.getProperty(); - - // Adjust field name to column name - if (ArrayUtils.contains(RELATIONSHIP_FIELDS, fieldName)) { - fieldName += "_id"; - } - - obs.views.add(svs.field()); - - parseOrderByForField(svs, item, fieldName, clause); } } @@ -557,11 +526,11 @@ protected void queryOp( final Pair> leftInfo, final Pair> rightInfo) { - String subQuery = leftInfo.getKey().toString(); + String subQuery = leftInfo.getLeft().toString(); // Add extra parentheses subQuery = subQuery.replaceFirst("WHERE ", "WHERE ("); query.append(subQuery). - append(' ').append(op).append(" any_id IN ( ").append(rightInfo.getKey()).append("))"); + append(' ').append(op).append(" any_id IN ( ").append(rightInfo.getLeft()).append("))"); } protected Pair> getQuery( @@ -573,8 +542,7 @@ protected Pair> getQuery( Set involvedPlainAttrs = new HashSet<>(); switch (cond.getType()) { - case LEAF: - case NOT_LEAF: + case LEAF, NOT_LEAF -> { cond.getLeaf(AnyTypeCond.class). filter(leaf -> AnyTypeKind.ANY_OBJECT == svs.anyTypeKind). ifPresent(leaf -> query.append(getQuery(leaf, not, parameters, svs))); @@ -613,25 +581,21 @@ protected Pair> getQuery( ifPresent(leaf -> query.append(getQuery(leaf, not, parameters, svs))); cond.getLeaf(AnyCond.class).ifPresentOrElse( - anyCond -> { - query.append(getQuery(anyCond, not, parameters, svs)); - }, - () -> { - cond.getLeaf(AttrCond.class).ifPresent(leaf -> { - query.append(getQuery(leaf, not, parameters, svs)); - try { - involvedPlainAttrs.add(check(leaf, svs.anyTypeKind).getLeft().getKey()); - } catch (IllegalArgumentException e) { - // ignore - } - }); - }); + anyCond -> query.append(getQuery(anyCond, not, parameters, svs)), + () -> cond.getLeaf(AttrCond.class).ifPresent(leaf -> { + query.append(getQuery(leaf, not, parameters, svs)); + try { + involvedPlainAttrs.add(check(leaf, svs.anyTypeKind).getLeft().getKey()); + } catch (IllegalArgumentException e) { + // ignore + } + })); // allow for additional search conditions getQueryForCustomConds(cond, parameters, svs, not, query); - break; + } - case AND: + case AND -> { Pair> leftAndInfo = getQuery(cond.getLeft(), parameters, svs); involvedPlainAttrs.addAll(leftAndInfo.getRight()); @@ -639,9 +603,9 @@ protected Pair> getQuery( involvedPlainAttrs.addAll(rigthAndInfo.getRight()); queryOp(query, "AND", leftAndInfo, rigthAndInfo); - break; + } - case OR: + case OR -> { Pair> leftOrInfo = getQuery(cond.getLeft(), parameters, svs); involvedPlainAttrs.addAll(leftOrInfo.getRight()); @@ -649,9 +613,10 @@ protected Pair> getQuery( involvedPlainAttrs.addAll(rigthOrInfo.getRight()); queryOp(query, "OR", leftOrInfo, rigthOrInfo); - break; + } - default: + default -> { + } } return Pair.of(query, involvedPlainAttrs); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuditEntryDAO.java similarity index 86% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepoExtImpl.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuditEntryDAO.java index 626ba76e6d..1f957de49e 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuditEntryDAO.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao.repo; +package org.apache.syncope.core.persistence.jpa.dao; import jakarta.persistence.EntityManager; import jakarta.persistence.Query; @@ -30,16 +30,16 @@ import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.lib.audit.AuditEntry; import org.apache.syncope.common.lib.types.AuditElements; -import org.apache.syncope.core.persistence.api.dao.AuditConfDAO; +import org.apache.syncope.core.persistence.api.dao.AuditEntryDAO; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.domain.Pageable; import org.springframework.transaction.annotation.Transactional; -public class AuditConfRepoExtImpl implements AuditConfRepoExt { +public class JPAAuditEntryDAO implements AuditEntryDAO { - protected static final Logger LOG = LoggerFactory.getLogger(AuditConfRepoExt.class); + protected static final Logger LOG = LoggerFactory.getLogger(AuditEntryDAO.class); protected static class MessageCriteriaBuilder { @@ -56,7 +56,7 @@ protected int setParameter(final List parameters, final Object parameter protected MessageCriteriaBuilder entityKey(final String entityKey) { if (entityKey != null) { - query.append(andIfNeeded()).append(AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN). + query.append(andIfNeeded()).append(MESSAGE_COLUMN). append(" LIKE '%key%").append(entityKey).append("%'"); } return this; @@ -64,7 +64,7 @@ protected MessageCriteriaBuilder entityKey(final String entityKey) { public MessageCriteriaBuilder type(final AuditElements.EventCategoryType type) { if (type != null) { - query.append(andIfNeeded()).append(AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN). + query.append(andIfNeeded()).append(MESSAGE_COLUMN). append(" LIKE '%\"type\":\"").append(type.name()).append("\"%'"); } return this; @@ -72,7 +72,7 @@ public MessageCriteriaBuilder type(final AuditElements.EventCategoryType type) { public MessageCriteriaBuilder category(final String category) { if (StringUtils.isNotBlank(category)) { - query.append(andIfNeeded()).append(AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN). + query.append(andIfNeeded()).append(MESSAGE_COLUMN). append(" LIKE '%\"category\":\"").append(category).append("\"%'"); } return this; @@ -80,7 +80,7 @@ public MessageCriteriaBuilder category(final String category) { public MessageCriteriaBuilder subcategory(final String subcategory) { if (StringUtils.isNotBlank(subcategory)) { - query.append(andIfNeeded()).append(AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN). + query.append(andIfNeeded()).append(MESSAGE_COLUMN). append(" LIKE '%\"subcategory\":\"").append(subcategory).append("\"%'"); } return this; @@ -90,7 +90,7 @@ public MessageCriteriaBuilder events(final List events) { if (!events.isEmpty()) { query.append(andIfNeeded()).append("( "). append(events.stream(). - map(event -> AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN + map(event -> MESSAGE_COLUMN + " LIKE '%\"event\":\"" + event + "\"%'"). collect(Collectors.joining(" OR "))). append(" )"); @@ -100,7 +100,7 @@ public MessageCriteriaBuilder events(final List events) { public MessageCriteriaBuilder result(final AuditElements.Result result) { if (result != null) { - query.append(andIfNeeded()).append(AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN). + query.append(andIfNeeded()).append(MESSAGE_COLUMN). append(" LIKE '%\"result\":\"").append(result.name()).append("\"%' "); } return this; @@ -108,7 +108,7 @@ public MessageCriteriaBuilder result(final AuditElements.Result result) { public MessageCriteriaBuilder before(final OffsetDateTime before, final List parameters) { if (before != null) { - query.append(andIfNeeded()).append(AuditConfDAO.AUDIT_ENTRY_EVENT_DATE_COLUMN). + query.append(andIfNeeded()).append(EVENT_DATE_COLUMN). append(" <= ?").append(setParameter(parameters, before)); } return this; @@ -116,7 +116,7 @@ public MessageCriteriaBuilder before(final OffsetDateTime before, final List parameters) { if (after != null) { - query.append(andIfNeeded()).append(AuditConfDAO.AUDIT_ENTRY_EVENT_DATE_COLUMN). + query.append(andIfNeeded()).append(EVENT_DATE_COLUMN). append(" >= ?").append(setParameter(parameters, after)); } return this; @@ -129,7 +129,7 @@ public String build() { protected final EntityManager entityManager; - public AuditConfRepoExtImpl(final EntityManager entityManager) { + public JPAAuditEntryDAO(final EntityManager entityManager) { this.entityManager = entityManager; } @@ -148,7 +148,7 @@ protected void fillWithParameters(final Query query, final List paramete } @Override - public long countEntries( + public long count( final String entityKey, final AuditElements.EventCategoryType type, final String category, @@ -160,7 +160,7 @@ public long countEntries( List parameters = new ArrayList<>(); String queryString = "SELECT COUNT(0)" - + " FROM " + AuditConfDAO.AUDIT_ENTRY_TABLE + + " FROM " + TABLE + " WHERE " + messageCriteriaBuilder(entityKey). type(type). category(category). @@ -177,12 +177,12 @@ public long countEntries( } protected String select() { - return AuditConfDAO.AUDIT_ENTRY_MESSAGE_COLUMN; + return MESSAGE_COLUMN; } @Transactional(readOnly = true) @Override - public List searchEntries( + public List search( final String entityKey, final AuditElements.EventCategoryType type, final String category, @@ -195,7 +195,7 @@ public List searchEntries( List parameters = new ArrayList<>(); String queryString = "SELECT " + select() - + " FROM " + AuditConfDAO.AUDIT_ENTRY_TABLE + + " FROM " + TABLE + " WHERE " + messageCriteriaBuilder(entityKey). type(type). category(category). diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAEntityCacheDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAEntityCacheDAO.java index 91355a0910..e95eda19c4 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAEntityCacheDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAEntityCacheDAO.java @@ -32,7 +32,7 @@ import org.apache.openjpa.persistence.QueryResultCacheImpl; import org.apache.syncope.core.persistence.api.dao.EntityCacheDAO; import org.apache.syncope.core.persistence.api.entity.Entity; -import org.apache.syncope.core.provisioning.api.utils.FormatUtils; +import org.apache.syncope.core.persistence.api.utils.FormatUtils; public class JPAEntityCacheDAO implements EntityCacheDAO { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java similarity index 78% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepoExtImpl.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java index e50fbb3d8b..120676c9c2 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao.repo; +package org.apache.syncope.core.persistence.jpa.dao; import jakarta.persistence.EntityManager; import jakarta.persistence.NoResultException; @@ -30,6 +30,8 @@ import org.apache.syncope.core.persistence.api.dao.MalformedPathException; import org.apache.syncope.core.persistence.api.dao.RealmDAO; import org.apache.syncope.core.persistence.api.dao.RoleDAO; +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.AccessPolicy; import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; @@ -40,6 +42,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; import org.apache.syncope.core.persistence.api.entity.policy.ProvisioningPolicy; import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; +import org.apache.syncope.core.persistence.api.search.SyncopePage; import org.apache.syncope.core.persistence.jpa.entity.JPARealm; import org.apache.syncope.core.provisioning.api.event.EntityLifecycleEvent; import org.apache.syncope.core.spring.security.AuthContextUtils; @@ -47,28 +50,19 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationEventPublisher; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.transaction.annotation.Transactional; -public class RealmRepoExtImpl implements RealmRepoExt { +public class JPARealmDAO implements RealmDAO { - protected static final Logger LOG = LoggerFactory.getLogger(RealmRepoExt.class); + protected static final Logger LOG = LoggerFactory.getLogger(RealmDAO.class); protected static int setParameter(final List parameters, final Object parameter) { parameters.add(parameter); return parameters.size(); } - protected static StringBuilder buildDescendantQuery(final String base, final List parameters) { - return new StringBuilder("SELECT e FROM "). - append(JPARealm.class.getSimpleName()).append(" e "). - append("WHERE e.fullPath=?"). - append(setParameter(parameters, base)). - append(" OR e.fullPath LIKE ?"). - append(setParameter(parameters, SyncopeConstants.ROOT_REALM.equals(base) ? "/%" : base + "/%")). - append(" ORDER BY e.fullPath"); - } - protected static StringBuilder buildDescendantQuery( final String base, final String keyword, @@ -90,19 +84,13 @@ protected static StringBuilder buildDescendantQuery( return queryString; } - protected static String buildFullPath(final Realm realm) { - return realm.getParent() == null - ? SyncopeConstants.ROOT_REALM - : StringUtils.appendIfMissing(realm.getParent().getFullPath(), "/") + realm.getName(); - } - protected final RoleDAO roleDAO; protected final ApplicationEventPublisher publisher; protected final EntityManager entityManager; - public RealmRepoExtImpl( + public JPARealmDAO( final RoleDAO roleDAO, final ApplicationEventPublisher publisher, final EntityManager entityManager) { @@ -128,6 +116,17 @@ public Realm getRoot() { return result; } + @Override + public boolean existsById(final String key) { + return findById(key).isPresent(); + } + + @Transactional(readOnly = true) + @Override + public Optional findById(final String key) { + return Optional.ofNullable(entityManager.find(JPARealm.class, key)); + } + @Transactional(readOnly = true) @Override public Optional findByFullPath(final String fullPath) { @@ -135,20 +134,19 @@ public Optional findByFullPath(final String fullPath) { return Optional.of(getRoot()); } - if (StringUtils.isBlank(fullPath) || !RealmDAO.PATH_PATTERN.matcher(fullPath).matches()) { + if (StringUtils.isBlank(fullPath) || !PATH_PATTERN.matcher(fullPath).matches()) { throw new MalformedPathException(fullPath); } - TypedQuery query = entityManager. - createQuery("SELECT e FROM " + JPARealm.class.getSimpleName() + " e " - + "WHERE e.fullPath=:fullPath", Realm.class); + TypedQuery query = entityManager.createQuery( + "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE e.fullPath=:fullPath", Realm.class); query.setParameter("fullPath", fullPath); Realm result = null; try { result = query.getSingleResult(); } catch (NoResultException e) { - LOG.debug("Root realm not found", e); + LOG.debug("Realm with fullPath {} not found", fullPath, e); } return Optional.ofNullable(result); @@ -156,8 +154,8 @@ public Optional findByFullPath(final String fullPath) { @Override public List findByName(final String name) { - TypedQuery query = entityManager.createQuery("SELECT e FROM " + JPARealm.class.getSimpleName() + " e " - + "WHERE e.name=:name", Realm.class); + TypedQuery query = entityManager.createQuery( + "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE e.name=:name", Realm.class); query.setParameter("name", name); return query.getResultList(); @@ -177,7 +175,7 @@ public long countDescendants(final String base, final String keyword) { query.setParameter(i, parameters.get(i - 1)); } - return ((Number) query.getSingleResult()).intValue(); + return ((Number) query.getSingleResult()).longValue(); } @Override @@ -297,14 +295,61 @@ public List findChildren(final Realm realm) { } @Override - public Realm save(final Realm realm) { + public List findByActionsContaining(final Implementation logicActions) { + TypedQuery query = entityManager.createQuery( + "SELECT e FROM " + JPARealm.class.getSimpleName() + " e " + + "WHERE :logicActions MEMBER OF e.actions", Realm.class); + query.setParameter("logicActions", logicActions); + + return query.getResultList(); + } + + @Override + public List findByResources(final ExternalResource resource) { + TypedQuery query = entityManager.createQuery( + "SELECT e FROM " + JPARealm.class.getSimpleName() + " e " + + "WHERE :resource MEMBER OF e.resources", Realm.class); + query.setParameter("resource", resource); + + return query.getResultList(); + } + + @Override + public long count() { + Query query = entityManager.createNativeQuery( + "SELECT COUNT(id) FROM " + JPARealm.TABLE); + return ((Number) query.getSingleResult()).longValue(); + } + + @Override + public List findAll() { + throw new UnsupportedOperationException(); + } + + @Override + public Page findAll(final Pageable pageable) { + TypedQuery query = entityManager.createQuery( + "SELECT e FROM " + JPARealm.class.getSimpleName() + " e ORDER BY e.fullPath", Realm.class); + + if (pageable.isPaged()) { + query.setFirstResult(pageable.getPageSize() * pageable.getPageNumber()); + query.setMaxResults(pageable.getPageSize()); + } + + return new SyncopePage<>(query.getResultList(), pageable, count()); + } + + @Override + public S save(final S realm) { String fullPathBefore = realm.getFullPath(); - String fullPathAfter = buildFullPath(realm); + String fullPathAfter = realm.getParent() == null + ? SyncopeConstants.ROOT_REALM + : StringUtils.appendIfMissing(realm.getParent().getFullPath(), "/") + realm.getName(); if (!fullPathAfter.equals(fullPathBefore)) { ((JPARealm) realm).setFullPath(fullPathAfter); } - Realm merged = entityManager.merge(realm); + S merged = entityManager.merge(realm); if (!fullPathAfter.equals(fullPathBefore)) { findChildren(realm).forEach(this::save); @@ -316,6 +361,11 @@ public Realm save(final Realm realm) { return merged; } + @Override + public void deleteById(final String key) { + findById(key).ifPresent(this::delete); + } + @Override public void delete(final Realm realm) { if (realm == null || realm.getParent() == null) { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskExecDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskExecDAO.java index 214f7fb207..3e03f54a44 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskExecDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskExecDAO.java @@ -141,6 +141,11 @@ public Optional> findLatestEnded(final TaskType type, fina return findLatest(type, task, "end"); } + @Override + public long count() { + throw new UnsupportedOperationException(); + } + protected StringBuilder query( final StringBuilder select, final Task task, @@ -159,11 +164,6 @@ protected StringBuilder query( return query; } - @Override - public long count() { - throw new UnsupportedOperationException(); - } - @Override public long count( final Task task, diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java index 32abbdc5f4..9e3e8fc6ce 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java @@ -134,8 +134,8 @@ public SearchView entitlements() { } SearchViewSupport asSearchViewSupport() { - if (this instanceof SearchViewSupport) { - return (SearchViewSupport) this; + if (this instanceof SearchViewSupport searchViewSupport) { + return searchViewSupport; } throw new IllegalArgumentException("Not an " + SearchViewSupport.class + " instance"); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAnyRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAnyRepoExt.java index d9e75b1523..287ada1a8d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAnyRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAnyRepoExt.java @@ -41,9 +41,6 @@ import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; 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.dao.search.AnyCond; -import org.apache.syncope.core.persistence.api.dao.search.AttrCond; -import org.apache.syncope.core.persistence.api.dao.search.SearchCond; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; import org.apache.syncope.core.persistence.api.entity.AnyUtils; @@ -67,6 +64,29 @@ public abstract class AbstractAnyRepoExt> implements AnyRepoExt protected static final Logger LOG = LoggerFactory.getLogger(AnyRepoExt.class); + /** + * Split an attribute value recurring on provided literals/tokens. + * + * @param attrValue value to be split + * @param literals literals/tokens + * @return split value + */ + protected static List split(final String attrValue, final List literals) { + final List attrValues = new ArrayList<>(); + + if (literals.isEmpty()) { + attrValues.add(attrValue); + } else { + for (String token : attrValue.split(Pattern.quote(literals.get(0)))) { + if (!token.isEmpty()) { + attrValues.addAll(split(token, literals.subList(1, literals.size()))); + } + } + } + + return attrValues; + } + protected final PlainSchemaDAO plainSchemaDAO; protected final DerSchemaDAO derSchemaDAO; @@ -202,29 +222,6 @@ public Optional findByPlainAttrUniqueValue( : Optional.of(result.get(0)); } - /** - * Split an attribute value recurring on provided literals/tokens. - * - * @param attrValue value to be split - * @param literals literals/tokens - * @return split value - */ - private static List split(final String attrValue, final List literals) { - final List attrValues = new ArrayList<>(); - - if (literals.isEmpty()) { - attrValues.add(attrValue); - } else { - for (String token : attrValue.split(Pattern.quote(literals.get(0)))) { - if (!token.isEmpty()) { - attrValues.addAll(split(token, literals.subList(1, literals.size()))); - } - } - } - - return attrValues; - } - private Set getWhereClause(final String expression, final String value, final boolean ignoreCaseMatch) { Parser parser = new Parser(expression); @@ -377,13 +374,6 @@ public List findByDerAttrValue(final DerSchema schema, final String value, fi return result; } - @Override - public SearchCond getAllMatchingCond() { - AnyCond idCond = new AnyCond(AttrCond.Type.ISNOTNULL); - idCond.setSchema("id"); - return SearchCond.getLeaf(idCond); - } - @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) @Override @SuppressWarnings("unchecked") @@ -448,7 +438,7 @@ public List findDynRealms(final String key) { @SuppressWarnings("unchecked") List result = query.getResultList(); return result.stream(). - map(roleKey -> dynRealmDAO.findById(roleKey.toString())). + map(dynRealm -> dynRealmDAO.findById(dynRealm.toString())). filter(Optional::isPresent).map(Optional::get). map(DynRealm::getKey). distinct(). diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExt.java index 1d10000911..45eb7cbff0 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExt.java @@ -33,23 +33,8 @@ public interface AnyObjectRepoExt extends AnyRepoExt { - /** - * Checks if the calling user is authorized to access the Any Object matching the provided key, under the given - * realm. - * - * @param authRealms realms for which the calling user owns entitlement(s) to check - * @param key Any Object key - * @param realm Any Object's realm full path - * @param groups group the Any Object is member of - */ void securityChecks(Set authRealms, String key, String realm, Collection groups); - /** - * Counts the number of instances for each type. - * The returned map is expected to be sorted on values. - * - * @return the number of instances for each type - */ Map countByType(); Map countByRealm(AnyType anyType); @@ -67,10 +52,4 @@ public interface AnyObjectRepoExt extends AnyRepoExt { Collection findAllResources(AnyObject anyObject); Pair, Set> saveAndGetDynGroupMembs(AnyObject anyObject); - - @Override - S save(S anyObject); - - @Override - void delete(AnyObject anyObject); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExtImpl.java index 11befb0338..c061be9a25 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExtImpl.java @@ -51,11 +51,11 @@ 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.URelationship; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership; import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAARelationship; import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject; import org.apache.syncope.core.persistence.jpa.entity.user.JPAURelationship; -import org.apache.syncope.core.provisioning.api.utils.RealmUtils; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.core.spring.security.DelegatedAdministrationException; import org.springframework.transaction.annotation.Propagation; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyRepoExt.java index 6e17a9f7c2..dd1073ba82 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyRepoExt.java @@ -23,7 +23,6 @@ import java.util.List; import java.util.Optional; import org.apache.syncope.core.persistence.api.dao.AllowedSchemas; -import org.apache.syncope.core.persistence.api.dao.search.SearchCond; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.DerSchema; import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; @@ -55,11 +54,6 @@ Optional findByPlainAttrUniqueValue( */ List findByDerAttrValue(DerSchema schema, String value, boolean ignoreCaseMatch); - /** - * @return the search condition to match all entities - */ - SearchCond getAllMatchingCond(); - AllowedSchemas findAllowedSchemas(A any, Class reference); List findDynRealms(String key); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepo.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepo.java index 98883e7413..d54a092443 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepo.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AuditConfRepo.java @@ -23,6 +23,6 @@ import org.springframework.data.repository.ListCrudRepository; public interface AuditConfRepo - extends ListCrudRepository, AuditConfRepoExt, AuditConfDAO { + extends ListCrudRepository, AuditConfDAO { } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConnInstanceRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConnInstanceRepoExtImpl.java index b474af59d3..5196d2f22e 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConnInstanceRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConnInstanceRepoExtImpl.java @@ -92,8 +92,7 @@ public void deleteById(final String key) { return; } - connInstance.getResources().stream().map(ExternalResource::getKey).toList(). - forEach(resourceDAO::deleteById); + connInstance.getResources().stream().map(ExternalResource::getKey).toList().forEach(resourceDAO::deleteById); entityManager.remove(connInstance); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ExternalResourceRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ExternalResourceRepoExtImpl.java index 26f779ac93..a05727052c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ExternalResourceRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ExternalResourceRepoExtImpl.java @@ -124,30 +124,26 @@ public boolean anyItemHaving(final Implementation transformer) { count() > 0; } - protected StringBuilder getByPolicyQuery(final Class policyClass) { - StringBuilder query = new StringBuilder("SELECT e FROM "). + @Override + public List findByPolicy(final Policy policy) { + StringBuilder queryString = new StringBuilder("SELECT e FROM "). append(JPAExternalResource.class.getSimpleName()). append(" e WHERE e."); - if (AccountPolicy.class.isAssignableFrom(policyClass)) { - query.append("accountPolicy"); - } else if (PasswordPolicy.class.isAssignableFrom(policyClass)) { - query.append("passwordPolicy"); - } else if (PropagationPolicy.class.isAssignableFrom(policyClass)) { - query.append("propagationPolicy"); - } else if (PullPolicy.class.isAssignableFrom(policyClass)) { - query.append("pullPolicy"); - } else if (PushPolicy.class.isAssignableFrom(policyClass)) { - query.append("pushPolicy"); + if (AccountPolicy.class.isAssignableFrom(policy.getClass())) { + queryString.append("accountPolicy"); + } else if (PasswordPolicy.class.isAssignableFrom(policy.getClass())) { + queryString.append("passwordPolicy"); + } else if (PropagationPolicy.class.isAssignableFrom(policy.getClass())) { + queryString.append("propagationPolicy"); + } else if (PullPolicy.class.isAssignableFrom(policy.getClass())) { + queryString.append("pullPolicy"); + } else if (PushPolicy.class.isAssignableFrom(policy.getClass())) { + queryString.append("pushPolicy"); } - return query; - } - - @Override - public List findByPolicy(final Policy policy) { TypedQuery query = entityManager.createQuery( - getByPolicyQuery(policy.getClass()).append("=:policy").toString(), ExternalResource.class); + queryString.append("=:policy").toString(), ExternalResource.class); query.setParameter("policy", policy); return query.getResultList(); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExt.java index 09c1e2679a..a49a84ca2d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExt.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.jpa.dao.repo; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; @@ -33,92 +32,45 @@ public interface GroupRepoExt extends AnyRepoExt { - String UDYNMEMB_TABLE = "UDynGroupMembers"; - String ADYNMEMB_TABLE = "ADynGroupMembers"; - /** - * Checks if the calling user is authorized to access the Group matching the provided key, under the given - * realm. - * - * @param authRealms realms for which the calling user owns entitlement(s) to check - * @param key Group key - * @param realm Group's realm full path - */ + String UDYNMEMB_TABLE = "UDynGroupMembers"; + void securityChecks(Set authRealms, String key, String realm); Map countByRealm(); List findOwnedByUser(String userKey); - List findAMemberships(Group group); + boolean existsAMembership(String anyObjectKey, String groupKey); - List findUMemberships(Group group); + boolean existsUMembership(String userKey, String groupKey); - List findTypeExtensions(AnyTypeClass anyTypeClass); + List findAMemberships(Group group); - List findADynMembers(Group group); + List findUMemberships(Group group); - boolean existsAMembership(String anyObjectKey, String groupKey); + Group saveAndRefreshDynMemberships(Group group); - boolean existsUMembership(String userKey, String groupKey); + List findTypeExtensions(AnyTypeClass anyTypeClass); long countADynMembers(Group group); long countUDynMembers(Group group); - @Override - Collection findAllResourceKeys(String key); + List findADynMembers(Group group); + + List findUDynMembers(Group group); void clearADynMembers(Group group); - /** - * Evaluates all the dynamic group membership conditions against the given anyObject (invoked during save). - * - * @param anyObject anyObject being saved - * @return pair of groups dynamically assigned before and after refresh - */ + void clearUDynMembers(Group group); + Pair, Set> refreshDynMemberships(AnyObject anyObject); - /** - * Removes the dynamic group memberships of the given anyObject (invoked during delete). - * - * @param anyObject anyObject being deleted - * @return groups dynamically assigned before refresh - */ Set removeDynMemberships(AnyObject anyObject); - List findUDynMembers(Group group); - - void clearUDynMembers(Group group); - - /** - * Evaluates all the dynamic group membership conditions against the given user (invoked during save). - * - * @param user user being saved - * @return pair of groups dynamically assigned before and after refresh - */ Pair, Set> refreshDynMemberships(User user); - /** - * Removes the dynamic group memberships of the given anyObject (invoked during delete). - * - * @param user user being deleted - * @return groups dynamically assigned before refresh - */ Set removeDynMemberships(User user); - - /** - * Saves the provided group and refreshes all User and AnyObject members. - * - * @param group group to save - * @return merged group - */ - Group saveAndRefreshDynMemberships(Group group); - - @Override - S save(S group); - - @Override - void delete(Group group); } 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 0df829b649..271358b6f6 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 @@ -58,6 +58,7 @@ import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.api.search.SearchCondConverter; import org.apache.syncope.core.persistence.api.search.SearchCondVisitor; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADynGroupMembership; import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership; import org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup; @@ -65,7 +66,6 @@ import org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership; import org.apache.syncope.core.persistence.jpa.entity.user.JPAUMembership; import org.apache.syncope.core.provisioning.api.event.EntityLifecycleEvent; -import org.apache.syncope.core.provisioning.api.utils.RealmUtils; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.core.spring.security.DelegatedAdministrationException; import org.identityconnectors.framework.common.objects.SyncDeltaType; @@ -120,18 +120,6 @@ public Optional findLastChange(final String key) { return findLastChange(key, JPAGroup.TABLE); } - @Override - public Map countByRealm() { - Query query = entityManager.createQuery( - "SELECT e.realm, COUNT(e) FROM " + anyUtils.anyClass().getSimpleName() + " e GROUP BY e.realm"); - - @SuppressWarnings("unchecked") - List results = query.getResultList(); - return results.stream().collect(Collectors.toMap( - result -> ((Realm) result[0]).getFullPath(), - result -> ((Number) result[1]).longValue())); - } - @Transactional(readOnly = true) @Override public void securityChecks( @@ -162,6 +150,18 @@ protected void securityChecks(final Group group) { securityChecks(authRealms, group.getKey(), group.getRealm().getFullPath()); } + @Override + public Map countByRealm() { + Query query = entityManager.createQuery( + "SELECT e.realm, COUNT(e) FROM " + anyUtils.anyClass().getSimpleName() + " e GROUP BY e.realm"); + + @SuppressWarnings("unchecked") + List results = query.getResultList(); + return results.stream().collect(Collectors.toMap( + result -> ((Realm) result[0]).getFullPath(), + result -> ((Number) result[1]).longValue())); + } + @Transactional(readOnly = true) @Override public List findOwnedByUser(final String userKey) { @@ -181,6 +181,36 @@ public List findOwnedByUser(final String userKey) { return query.getResultList(); } + @Transactional(readOnly = true) + @Override + public Collection findAllResourceKeys(final String key) { + return findById(key).map(Any::getResources). + orElse(List.of()). + stream().map(ExternalResource::getKey).toList(); + } + + @Transactional(readOnly = true) + @Override + public boolean existsAMembership(final String anyObjectKey, final String groupKey) { + Query query = entityManager.createNativeQuery( + "SELECT COUNT(*) FROM " + JPAAMembership.TABLE + " WHERE group_id=? AND anyobject_it=?"); + query.setParameter(1, groupKey); + query.setParameter(2, anyObjectKey); + + return ((Number) query.getSingleResult()).longValue() > 0; + } + + @Transactional(readOnly = true) + @Override + public boolean existsUMembership(final String userKey, final String groupKey) { + Query query = entityManager.createNativeQuery( + "SELECT COUNT(*) FROM " + JPAUMembership.TABLE + " WHERE group_id=? AND user_id=?"); + query.setParameter(1, groupKey); + query.setParameter(2, userKey); + + return ((Number) query.getSingleResult()).longValue() > 0; + } + @Override public List findAMemberships(final Group group) { TypedQuery query = entityManager.createQuery( @@ -201,19 +231,20 @@ public List findUMemberships(final Group group) { return query.getResultList(); } - protected SearchCond buildDynMembershipCond(final String baseCondFIQL) { - return SearchCondConverter.convert(searchCondVisitor, baseCondFIQL); + @Override + public S save(final S group) { + return entityManager.merge(group); } @Override public Group saveAndRefreshDynMemberships(final Group group) { - Group merged = entityManager.merge(group); + Group merged = save(group); // refresh dynamic memberships clearUDynMembers(merged); if (merged.getUDynMembership() != null) { - SearchCond cond = buildDynMembershipCond(merged.getUDynMembership().getFIQLCond()); - int count = anySearchDAO.count( + SearchCond cond = SearchCondConverter.convert(searchCondVisitor, merged.getUDynMembership().getFIQLCond()); + long count = anySearchDAO.count( merged.getRealm(), true, Set.of(merged.getRealm().getFullPath()), cond, AnyTypeKind.USER); for (int page = 0; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE); page++) { List matching = anySearchDAO.search( @@ -238,8 +269,8 @@ public Group saveAndRefreshDynMemberships(final Group group) { } clearADynMembers(merged); merged.getADynMemberships().forEach(memb -> { - SearchCond cond = buildDynMembershipCond(memb.getFIQLCond()); - int count = anySearchDAO.count( + SearchCond cond = SearchCondConverter.convert(searchCondVisitor, memb.getFIQLCond()); + long count = anySearchDAO.count( merged.getRealm(), true, Set.of(merged.getRealm().getFullPath()), cond, AnyTypeKind.ANY_OBJECT); for (int page = 0; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE); page++) { List matching = anySearchDAO.search( @@ -323,6 +354,28 @@ public List findTypeExtensions(final AnyTypeClass anyTypeClass) { return query.getResultList(); } + @Override + public long countADynMembers(final Group group) { + Query query = entityManager.createNativeQuery( + "SELECT COUNT(any_id) FROM " + ADYNMEMB_TABLE + " WHERE group_id=?"); + query.setParameter(1, group.getKey()); + + return ((Number) query.getSingleResult()).longValue(); + } + + @Override + public long countUDynMembers(final Group group) { + if (group.getUDynMembership() == null) { + return 0; + } + + Query query = entityManager.createNativeQuery( + "SELECT COUNT(any_id) FROM " + UDYNMEMB_TABLE + " WHERE group_id=?"); + query.setParameter(1, group.getKey()); + + return ((Number) query.getSingleResult()).longValue(); + } + @Transactional(readOnly = true) @Override public List findADynMembers(final Group group) { @@ -345,48 +398,20 @@ public List findADynMembers(final Group group) { return result; } - @Transactional(readOnly = true) - @Override - public boolean existsAMembership(final String anyObjectKey, final String groupKey) { - Query query = entityManager.createNativeQuery( - "SELECT COUNT(*) FROM " + JPAAMembership.TABLE + " WHERE group_id=? AND anyobject_it=?"); - query.setParameter(1, groupKey); - query.setParameter(2, anyObjectKey); - - return ((Number) query.getSingleResult()).longValue() > 0; - } - - @Transactional(readOnly = true) - @Override - public boolean existsUMembership(final String userKey, final String groupKey) { - Query query = entityManager.createNativeQuery( - "SELECT COUNT(*) FROM " + JPAUMembership.TABLE + " WHERE group_id=? AND user_id=?"); - query.setParameter(1, groupKey); - query.setParameter(2, userKey); - - return ((Number) query.getSingleResult()).longValue() > 0; - } - - @Override - public long countADynMembers(final Group group) { - Query query = entityManager.createNativeQuery( - "SELECT COUNT(any_id) FROM " + ADYNMEMB_TABLE + " WHERE group_id=?"); - query.setParameter(1, group.getKey()); - - return ((Number) query.getSingleResult()).longValue(); - } - @Override - public long countUDynMembers(final Group group) { + public List findUDynMembers(final Group group) { if (group.getUDynMembership() == null) { - return 0; + return List.of(); } - Query query = entityManager.createNativeQuery( - "SELECT COUNT(any_id) FROM " + UDYNMEMB_TABLE + " WHERE group_id=?"); + Query query = entityManager.createNativeQuery("SELECT any_id FROM " + UDYNMEMB_TABLE + " WHERE group_id=?"); query.setParameter(1, group.getKey()); - return ((Number) query.getSingleResult()).longValue(); + @SuppressWarnings("unchecked") + List result = query.getResultList(); + return result.stream(). + map(Object::toString). + toList(); } @Override @@ -396,6 +421,13 @@ public void clearADynMembers(final Group group) { delete.executeUpdate(); } + @Override + public void clearUDynMembers(final Group group) { + Query delete = entityManager.createNativeQuery("DELETE FROM " + UDYNMEMB_TABLE + " WHERE group_id=?"); + delete.setParameter(1, group.getKey()); + delete.executeUpdate(); + } + protected List findWithADynMemberships(final AnyType anyType) { TypedQuery query = entityManager.createQuery( "SELECT e FROM " + JPAADynGroupMembership.class.getSimpleName() + " e WHERE e.anyType=:anyType", @@ -410,7 +442,8 @@ public Pair, Set> refreshDynMemberships(final AnyObject anyO Set before = new HashSet<>(); Set after = new HashSet<>(); findWithADynMemberships(anyObject.getType()).forEach(memb -> { - boolean matches = anyMatchDAO.matches(anyObject, buildDynMembershipCond(memb.getFIQLCond())); + boolean matches = anyMatchDAO.matches( + anyObject, SearchCondConverter.convert(searchCondVisitor, memb.getFIQLCond())); if (matches) { after.add(memb.getGroup().getKey()); } @@ -465,29 +498,6 @@ public Set removeDynMemberships(final AnyObject anyObject) { return before; } - @Override - public List findUDynMembers(final Group group) { - if (group.getUDynMembership() == null) { - return List.of(); - } - - Query query = entityManager.createNativeQuery("SELECT any_id FROM " + UDYNMEMB_TABLE + " WHERE group_id=?"); - query.setParameter(1, group.getKey()); - - @SuppressWarnings("unchecked") - List result = query.getResultList(); - return result.stream(). - map(Object::toString). - toList(); - } - - @Override - public void clearUDynMembers(final Group group) { - Query delete = entityManager.createNativeQuery("DELETE FROM " + UDYNMEMB_TABLE + " WHERE group_id=?"); - delete.setParameter(1, group.getKey()); - delete.executeUpdate(); - } - protected List findWithUDynMemberships() { TypedQuery query = entityManager.createQuery( "SELECT e FROM " + JPAUDynGroupMembership.class.getSimpleName() + " e", @@ -502,7 +512,8 @@ public Pair, Set> refreshDynMemberships(final User user) { Set before = new HashSet<>(); Set after = new HashSet<>(); findWithUDynMemberships().forEach(memb -> { - boolean matches = anyMatchDAO.matches(user, buildDynMembershipCond(memb.getFIQLCond())); + boolean matches = anyMatchDAO.matches( + user, SearchCondConverter.convert(searchCondVisitor, memb.getFIQLCond())); if (matches) { after.add(memb.getGroup().getKey()); } @@ -555,17 +566,4 @@ public Set removeDynMemberships(final User user) { return before; } - - @Transactional(readOnly = true) - @Override - public Collection findAllResourceKeys(final String key) { - return findById(key).map(Any::getResources). - orElse(List.of()). - stream().map(ExternalResource::getKey).toList(); - } - - @Override - public S save(final S group) { - return entityManager.merge(group); - } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/OIDCRPClientAppRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/OIDCRPClientAppRepoExt.java index 5f964dce24..95b15cd479 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/OIDCRPClientAppRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/OIDCRPClientAppRepoExt.java @@ -20,7 +20,7 @@ import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp; -public interface OIDCRPClientAppRepoExt { +public interface OIDCRPClientAppRepoExt extends ClientAppRepoExt { OIDCRPClientApp save(OIDCRPClientApp clientApp); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/PlainSchemaRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/PlainSchemaRepoExt.java index 4ced354e30..d3b195fa65 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/PlainSchemaRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/PlainSchemaRepoExt.java @@ -28,8 +28,6 @@ public interface PlainSchemaRepoExt { List findByAnyTypeClasses(Collection anyTypeClasses); - > List findAttrs(PlainSchema schema, Class reference); - > boolean hasAttrs(PlainSchema schema, Class reference); PlainSchema save(PlainSchema schema); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/PlainSchemaRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/PlainSchemaRepoExtImpl.java index 949747aff9..0175fa7521 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/PlainSchemaRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/PlainSchemaRepoExtImpl.java @@ -83,16 +83,6 @@ public List findByAnyTypeClasses(final Collection> List findAttrs(final PlainSchema schema, final Class reference) { - TypedQuery query = entityManager.createQuery( - "SELECT e FROM " + getEntityReference(reference).getSimpleName() - + " e WHERE e.schema=:schema", reference); - query.setParameter("schema", schema); - - return query.getResultList(); - } - @Override public > boolean hasAttrs(final PlainSchema schema, final Class reference) { String plainAttrTable = getTable(reference); @@ -111,6 +101,15 @@ public PlainSchema save(final PlainSchema schema) { return entityManager.merge(schema); } + protected > List findAttrs(final PlainSchema schema, final Class reference) { + TypedQuery query = entityManager.createQuery( + "SELECT e FROM " + getEntityReference(reference).getSimpleName() + + " e WHERE e.schema=:schema", reference); + query.setParameter("schema", schema); + + return query.getResultList(); + } + protected void deleteAttrs(final PlainSchema schema) { for (AnyTypeKind anyTypeKind : AnyTypeKind.values()) { findAttrs(schema, anyUtilsFactory.getInstance(anyTypeKind).plainAttrClass()).forEach(this::delete); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RemediationRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RemediationRepoExtImpl.java index 24db47485e..d249041efa 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RemediationRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RemediationRepoExtImpl.java @@ -73,7 +73,7 @@ public long count(final OffsetDateTime before, final OffsetDateTime after) { query.setParameter("after", after); } - return ((Number) query.getSingleResult()).intValue(); + return ((Number) query.getSingleResult()).longValue(); } @Override diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ReportExecRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ReportExecRepoExtImpl.java index a368c39a6e..09c6e206e6 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ReportExecRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ReportExecRepoExtImpl.java @@ -107,7 +107,7 @@ public long count( query.setParameter("after", after); } - return ((Number) query.getSingleResult()).intValue(); + return ((Number) query.getSingleResult()).longValue(); } protected String toOrderByStatement(final Stream orderByClauses) { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepoExtImpl.java index 7ecaa810c3..c7362e7c98 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepoExtImpl.java @@ -80,9 +80,9 @@ public Role saveAndRefreshDynMemberships(final Role role) { // refresh dynamic memberships clearDynMembers(merged); - if (merged.getDynMembership() != null) { + if (merged.getDynMembershipCond() != null) { List matching = anySearchDAO.search( - SearchCondConverter.convert(searchCondVisitor, merged.getDynMembership().getFIQLCond()), + SearchCondConverter.convert(searchCondVisitor, merged.getDynMembershipCond()), AnyTypeKind.USER); matching.forEach(user -> { @@ -120,7 +120,7 @@ public void delete(final Role role) { @Override public List findDynMembers(final Role role) { - if (role.getDynMembership() == null) { + if (role.getDynMembershipCond() == null) { return List.of(); } @@ -145,11 +145,12 @@ public void clearDynMembers(final Role role) { @Override public void refreshDynMemberships(final User user) { entityManager.createQuery( - "SELECT e FROM " + JPARole.class.getSimpleName() + " e WHERE e.dynMembership IS NOT NULL", Role.class). + "SELECT e FROM " + JPARole.class.getSimpleName() + " e " + + "WHERE e.dynMembershipCond IS NOT NULL", Role.class). getResultStream().forEach(role -> { boolean matches = anyMatchDAO.matches( user, - SearchCondConverter.convert(searchCondVisitor, role.getDynMembership().getFIQLCond())); + SearchCondConverter.convert(searchCondVisitor, role.getDynMembershipCond())); Query find = entityManager.createNativeQuery( "SELECT any_id FROM " + DYNMEMB_TABLE + " WHERE role_id=?"); @@ -174,8 +175,7 @@ public void refreshDynMemberships(final User user) { @Override public void removeDynMemberships(final String key) { - Query delete = entityManager. - createNativeQuery("DELETE FROM " + DYNMEMB_TABLE + " WHERE any_id=?"); + Query delete = entityManager.createNativeQuery("DELETE FROM " + DYNMEMB_TABLE + " WHERE any_id=?"); delete.setParameter(1, key); delete.executeUpdate(); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/SAML2SPClientAppRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/SAML2SPClientAppRepoExt.java index 4efbc65056..e4397aad0b 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/SAML2SPClientAppRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/SAML2SPClientAppRepoExt.java @@ -20,7 +20,7 @@ import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp; -public interface SAML2SPClientAppRepoExt { +public interface SAML2SPClientAppRepoExt extends ClientAppRepoExt { SAML2SPClientApp save(SAML2SPClientApp clientApp); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExt.java index f3c922b465..16d87a4c05 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExt.java @@ -31,15 +31,6 @@ public interface UserRepoExt extends AnyRepoExt { - /** - * Checks if the calling user is authorized to access the User matching the provided key, under the given - * realm. - * - * @param authRealms realms for which the calling user owns entitlement(s) to check - * @param key User key - * @param realm User's realm full path - * @param groups group the User is member of - */ void securityChecks(Set authRealms, String key, String realm, Collection groups); Map countByRealm(); @@ -65,10 +56,4 @@ public interface UserRepoExt extends AnyRepoExt { Pair, Set> saveAndGetDynGroupMembs(User user); boolean linkedAccountExists(String userKey, String connObjectKeyValue); - - @Override - S save(S user); - - @Override - void delete(User user); } 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 cad13f8a77..8d4b1d8908 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 @@ -47,10 +47,10 @@ 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.User; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; import org.apache.syncope.core.persistence.jpa.entity.user.JPALinkedAccount; import org.apache.syncope.core.persistence.jpa.entity.user.JPAUMembership; import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser; -import org.apache.syncope.core.provisioning.api.utils.RealmUtils; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.core.spring.security.DelegatedAdministrationException; import org.apache.syncope.core.spring.security.SecurityProperties; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/VirSchemaRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/VirSchemaRepoExtImpl.java index 47e91e99f5..a84e2cfab2 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/VirSchemaRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/VirSchemaRepoExtImpl.java @@ -67,12 +67,7 @@ public VirSchema save(final VirSchema schema) { @Override public void deleteById(final String key) { - VirSchema schema = entityManager.find(JPAVirSchema.class, key); - if (schema == null) { - return; - } - - delete(schema); + Optional.ofNullable(entityManager.find(JPAVirSchema.class, key)).ifPresent(this::delete); } @Override diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAny.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAny.java index 1542b9f3db..ba4f98f0a1 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAny.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractAny.java @@ -26,7 +26,7 @@ import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.PlainAttr; import org.apache.syncope.core.persistence.api.entity.Realm; -import org.apache.syncope.core.persistence.jpa.validation.entity.AnyCheck; +import org.apache.syncope.core.persistence.common.validation.AnyCheck; @AnyCheck @MappedSuperclass diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDynMembership.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDynMembership.java index 005a3cd2c9..0fcbd32e78 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDynMembership.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractDynMembership.java @@ -41,5 +41,4 @@ public String getFIQLCond() { public void setFIQLCond(final String fiql) { this.fiql = fiql; } - } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttr.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttr.java index b6953d9d2b..d7f49bd703 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttr.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttr.java @@ -25,14 +25,14 @@ import jakarta.validation.constraints.NotNull; import java.util.Collections; import java.util.List; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.entity.Any; 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.PlainAttrUniqueValue; import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; import org.apache.syncope.core.persistence.api.entity.PlainSchema; -import org.apache.syncope.core.persistence.jpa.validation.entity.PlainAttrCheck; +import org.apache.syncope.core.persistence.common.validation.PlainAttrCheck; @MappedSuperclass @PlainAttrCheck diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttrValue.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttrValue.java index beba3aab19..813df8750a 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttrValue.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttrValue.java @@ -29,11 +29,11 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.AttrSchemaType; -import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException; +import org.apache.syncope.core.persistence.api.attrvalue.ParsingValidationException; import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; import org.apache.syncope.core.persistence.api.entity.PlainSchema; -import org.apache.syncope.core.persistence.jpa.validation.entity.PlainAttrValueCheck; -import org.apache.syncope.core.provisioning.api.utils.FormatUtils; +import org.apache.syncope.core.persistence.api.utils.FormatUtils; +import org.apache.syncope.core.persistence.common.validation.PlainAttrValueCheck; import org.apache.syncope.core.spring.ApplicationContextProvider; import org.apache.syncope.core.spring.security.Encryptor; @@ -145,42 +145,42 @@ public void parseValue(final PlainSchema schema, final String value) { case Long: try { - this.setLongValue(schema.getConversionPattern() == null - ? Long.valueOf(value) - : FormatUtils.parseNumber(value, schema.getConversionPattern()).longValue()); - } catch (Exception pe) { - exception = pe; - } - break; + this.setLongValue(schema.getConversionPattern() == null + ? Long.valueOf(value) + : FormatUtils.parseNumber(value, schema.getConversionPattern()).longValue()); + } catch (Exception pe) { + exception = pe; + } + break; case Double: try { - this.setDoubleValue(schema.getConversionPattern() == null - ? Double.valueOf(value) - : FormatUtils.parseNumber(value, schema.getConversionPattern()).doubleValue()); - } catch (Exception pe) { - exception = pe; - } - break; + this.setDoubleValue(schema.getConversionPattern() == null + ? Double.valueOf(value) + : FormatUtils.parseNumber(value, schema.getConversionPattern()).doubleValue()); + } catch (Exception pe) { + exception = pe; + } + break; case Date: try { - this.setDateValue(schema.getConversionPattern() == null - ? FormatUtils.parseDate(value) - : FormatUtils.parseDate(value, schema.getConversionPattern())); - } catch (Exception pe) { - exception = pe; - } - break; + this.setDateValue(schema.getConversionPattern() == null + ? FormatUtils.parseDate(value) + : FormatUtils.parseDate(value, schema.getConversionPattern())); + } catch (Exception pe) { + exception = pe; + } + break; case Encrypted: try { - this.setStringValue(Encryptor.getInstance(getSecretKey(schema)). - encode(value, schema.getCipherAlgorithm())); - } catch (Exception pe) { - exception = pe; - } - break; + this.setStringValue(Encryptor.getInstance(getSecretKey(schema)). + encode(value, schema.getCipherAlgorithm())); + } catch (Exception pe) { + exception = pe; + } + break; case Binary: this.setBinaryValue(Base64.getDecoder().decode(value)); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractSchema.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractSchema.java index f8bea88f15..8c29dd5e56 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractSchema.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractSchema.java @@ -36,7 +36,7 @@ import java.util.Map; import java.util.Optional; import org.apache.syncope.core.persistence.api.entity.Schema; -import org.apache.syncope.core.persistence.jpa.validation.entity.SchemaKeyCheck; +import org.apache.syncope.core.persistence.common.validation.SchemaKeyCheck; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @Entity diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyType.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyType.java index 47b2048040..cd3006b88f 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyType.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyType.java @@ -32,7 +32,7 @@ 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.AnyTypeClass; -import org.apache.syncope.core.persistence.jpa.validation.entity.AnyTypeCheck; +import org.apache.syncope.core.persistence.common.validation.AnyTypeCheck; @Entity @Table(name = JPAAnyType.TABLE) diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtilsFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtilsFactory.java deleted file mode 100644 index 287b9fb883..0000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtilsFactory.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.entity; - -import org.apache.syncope.common.lib.types.AnyTypeKind; -import org.apache.syncope.core.persistence.api.entity.Any; -import org.apache.syncope.core.persistence.api.entity.AnyUtils; -import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; -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; - -public class JPAAnyUtilsFactory implements AnyUtilsFactory { - - protected final AnyUtils userAnyUtils; - - protected final AnyUtils linkedAccountAnyUtils; - - protected final AnyUtils groupAnyUtils; - - protected final AnyUtils anyObjectAnyUtils; - - public JPAAnyUtilsFactory( - final AnyUtils userAnyUtils, - final AnyUtils linkedAccountAnyUtils, - final AnyUtils groupAnyUtils, - final AnyUtils anyObjectAnyUtils) { - - this.userAnyUtils = userAnyUtils; - this.linkedAccountAnyUtils = linkedAccountAnyUtils; - this.groupAnyUtils = groupAnyUtils; - this.anyObjectAnyUtils = anyObjectAnyUtils; - } - - @Override - public AnyUtils getInstance(final AnyTypeKind anyTypeKind) { - switch (anyTypeKind) { - case ANY_OBJECT: - return anyObjectAnyUtils; - - case GROUP: - return groupAnyUtils; - - case USER: - default: - return userAnyUtils; - } - } - - @Override - public AnyUtils getInstance(final Any any) { - AnyTypeKind anyTypeKind = null; - if (any instanceof User) { - anyTypeKind = AnyTypeKind.USER; - } else if (any instanceof Group) { - anyTypeKind = AnyTypeKind.GROUP; - } else if (any instanceof AnyObject) { - anyTypeKind = AnyTypeKind.ANY_OBJECT; - } - - if (anyTypeKind == null) { - throw new IllegalArgumentException("Any type not supported: " + any.getClass().getName()); - } - - return getInstance(anyTypeKind); - } - - @Override - public AnyUtils getLinkedAccountInstance() { - return linkedAccountAnyUtils; - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAApplication.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAApplication.java index 6e3cdbdf72..dcd024043d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAApplication.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAApplication.java @@ -28,7 +28,7 @@ import java.util.Optional; import org.apache.syncope.core.persistence.api.entity.Application; import org.apache.syncope.core.persistence.api.entity.Privilege; -import org.apache.syncope.core.persistence.jpa.validation.entity.ApplicationCheck; +import org.apache.syncope.core.persistence.common.validation.ApplicationCheck; @Entity @Table(name = JPAApplication.TABLE) diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstance.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstance.java index 195c55d497..7ebfbb7664 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstance.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstance.java @@ -41,12 +41,12 @@ import java.util.Set; import org.apache.commons.lang3.StringUtils; 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.entity.ConnInstance; -import org.apache.syncope.core.persistence.api.entity.ConnPoolConf; import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Realm; -import org.apache.syncope.core.persistence.jpa.validation.entity.ConnInstanceCheck; +import org.apache.syncope.core.persistence.common.validation.ConnInstanceCheck; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @Entity @@ -126,7 +126,7 @@ public class JPAConnInstance extends AbstractGeneratedKeyEntity implements ConnI */ private Integer connRequestTimeout = DEFAULT_TIMEOUT; - private JPAConnPoolConf poolConf; + private String poolConf; @Override public Realm getAdminRealm() { @@ -235,13 +235,12 @@ public void setConnRequestTimeout(final Integer timeout) { @Override public ConnPoolConf getPoolConf() { - return poolConf; + return Optional.ofNullable(poolConf).map(pc -> POJOHelper.deserialize(pc, ConnPoolConf.class)).orElse(null); } @Override public void setPoolConf(final ConnPoolConf poolConf) { - checkType(poolConf, JPAConnPoolConf.class); - this.poolConf = (JPAConnPoolConf) poolConf; + this.poolConf = Optional.ofNullable(poolConf).map(POJOHelper::serialize).orElse(null); } protected void json2list(final boolean clearFirst) { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnPoolConf.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnPoolConf.java deleted file mode 100644 index aa90aed187..0000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnPoolConf.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.entity; - -import jakarta.persistence.Embeddable; -import java.io.Serializable; -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.syncope.core.persistence.api.entity.ConnPoolConf; - -@Embeddable -public class JPAConnPoolConf implements ConnPoolConf, Serializable { - - private static final long serialVersionUID = -34259572059178970L; - - private Integer maxObjects; - - private Integer minIdle; - - private Integer maxIdle; - - private Long maxWait; - - private Long minEvictableIdleTimeMillis; - - @Override - public Integer getMaxObjects() { - return maxObjects; - } - - @Override - public void setMaxObjects(final Integer maxObjects) { - this.maxObjects = maxObjects; - } - - @Override - public Integer getMinIdle() { - return minIdle; - } - - @Override - public void setMinIdle(final Integer minIdle) { - this.minIdle = minIdle; - } - - @Override - public Integer getMaxIdle() { - return maxIdle; - } - - @Override - public void setMaxIdle(final Integer maxIdle) { - this.maxIdle = maxIdle; - } - - @Override - public Long getMaxWait() { - return maxWait; - } - - @Override - public void setMaxWait(final Long maxWait) { - this.maxWait = maxWait; - } - - @Override - public Long getMinEvictableIdleTimeMillis() { - return minEvictableIdleTimeMillis; - } - - @Override - public void setMinEvictableIdleTimeMillis(final Long minEvictableIdleTimeMillis) { - this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; - } - - @Override - public int hashCode() { - return new HashCodeBuilder(). - append(maxObjects). - append(minIdle). - append(maxIdle). - append(maxWait). - append(minEvictableIdleTimeMillis). - build(); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final JPAConnPoolConf other = (JPAConnPoolConf) obj; - return new EqualsBuilder(). - append(maxObjects, other.maxObjects). - append(minIdle, other.minIdle). - append(maxIdle, other.maxIdle). - append(maxWait, other.maxWait). - append(minEvictableIdleTimeMillis, other.minEvictableIdleTimeMillis). - build(); - } - - @Override - public String toString() { - return new ToStringBuilder(this). - append(maxObjects). - append(minIdle). - append(maxIdle). - append(maxWait). - append(minEvictableIdleTimeMillis). - build(); - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADelegation.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADelegation.java index c816b58af7..a63da4ff0c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADelegation.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADelegation.java @@ -32,8 +32,8 @@ import org.apache.syncope.core.persistence.api.entity.Delegation; import org.apache.syncope.core.persistence.api.entity.Role; import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.common.validation.DelegationCheck; import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser; -import org.apache.syncope.core.persistence.jpa.validation.entity.DelegationCheck; @Entity @Table(name = JPADelegation.TABLE, uniqueConstraints = diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADerSchema.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADerSchema.java index ee712bc9ee..9df4d2f857 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADerSchema.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADerSchema.java @@ -88,5 +88,4 @@ public boolean isUniqueConstraint() { public boolean isReadonly() { return Boolean.FALSE; } - } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealm.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealm.java index 3d0adc565f..d78f54b466 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealm.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealm.java @@ -30,7 +30,7 @@ import org.apache.syncope.core.persistence.api.entity.AnyType; import org.apache.syncope.core.persistence.api.entity.DynRealm; import org.apache.syncope.core.persistence.api.entity.DynRealmMembership; -import org.apache.syncope.core.persistence.jpa.validation.entity.DynRealmCheck; +import org.apache.syncope.core.persistence.common.validation.DynRealmCheck; @Entity @Table(name = JPADynRealm.TABLE) @@ -62,5 +62,4 @@ public Optional getDynMembership(final AnyType any public List getDynMemberships() { return dynMemberships; } - } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealmMembership.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealmMembership.java index d55bf81e86..0996ef8087 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealmMembership.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADynRealmMembership.java @@ -75,5 +75,4 @@ public String getFIQLCond() { public void setFIQLCond(final String fiql) { this.fiql = fiql; } - } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java index eb645ff6b3..279dabde7d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java @@ -28,7 +28,6 @@ import org.apache.syncope.core.persistence.api.entity.AuditConf; import org.apache.syncope.core.persistence.api.entity.Batch; import org.apache.syncope.core.persistence.api.entity.ConnInstance; -import org.apache.syncope.core.persistence.api.entity.ConnPoolConf; import org.apache.syncope.core.persistence.api.entity.Delegation; import org.apache.syncope.core.persistence.api.entity.DerSchema; import org.apache.syncope.core.persistence.api.entity.DynRealm; @@ -91,7 +90,6 @@ import org.apache.syncope.core.persistence.api.entity.task.PullTask; import org.apache.syncope.core.persistence.api.entity.task.PushTask; import org.apache.syncope.core.persistence.api.entity.task.SchedTask; -import org.apache.syncope.core.persistence.api.entity.user.DynRoleMembership; import org.apache.syncope.core.persistence.api.entity.user.LAPlainAttr; import org.apache.syncope.core.persistence.api.entity.user.LAPlainAttrUniqueValue; import org.apache.syncope.core.persistence.api.entity.user.LAPlainAttrValue; @@ -144,7 +142,6 @@ import org.apache.syncope.core.persistence.jpa.entity.task.JPAPullTask; import org.apache.syncope.core.persistence.jpa.entity.task.JPAPushTask; import org.apache.syncope.core.persistence.jpa.entity.task.JPASchedTask; -import org.apache.syncope.core.persistence.jpa.entity.user.JPADynRoleMembership; import org.apache.syncope.core.persistence.jpa.entity.user.JPALAPlainAttr; import org.apache.syncope.core.persistence.jpa.entity.user.JPALAPlainAttrUniqueValue; import org.apache.syncope.core.persistence.jpa.entity.user.JPALAPlainAttrValue; @@ -280,8 +277,6 @@ public E newEntity(final Class reference) { result = (E) new JPASecurityQuestion(); } else if (reference.equals(AuditConf.class)) { result = (E) new JPAAuditConf(); - } else if (reference.equals(DynRoleMembership.class)) { - result = (E) new JPADynRoleMembership(); } else if (reference.equals(ADynGroupMembership.class)) { result = (E) new JPAADynGroupMembership(); } else if (reference.equals(UDynGroupMembership.class)) { @@ -334,18 +329,13 @@ public E newEntity(final Class reference) { throw new IllegalArgumentException("Could not find a JPA implementation of " + reference.getName()); } - if (result instanceof AbstractGeneratedKeyEntity abstractGeneratedKeyEntity) { - abstractGeneratedKeyEntity.setKey(SecureRandomUtils.generateRandomUUID().toString()); + if (result instanceof AbstractGeneratedKeyEntity generatedKeyEntity) { + generatedKeyEntity.setKey(SecureRandomUtils.generateRandomUUID().toString()); } return result; } - @Override - public ConnPoolConf newConnPoolConf() { - return new JPAConnPoolConf(); - } - @Override public Class userClass() { return JPAUser.class; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAExternalResource.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAExternalResource.java index 60a733a801..53fce81e02 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAExternalResource.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAExternalResource.java @@ -59,17 +59,14 @@ import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; +import org.apache.syncope.core.persistence.common.validation.ExternalResourceCheck; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPasswordPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPropagationPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPullPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPushPolicy; -import org.apache.syncope.core.persistence.jpa.validation.entity.ExternalResourceCheck; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; -/** - * Resource for propagation and pull. - */ @Entity @Table(name = JPAExternalResource.TABLE) @ExternalResourceCheck diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAImplementation.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAImplementation.java index bb2bd5432f..7ba7891c47 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAImplementation.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAImplementation.java @@ -27,7 +27,7 @@ import jakarta.persistence.Table; import org.apache.syncope.common.lib.types.ImplementationEngine; import org.apache.syncope.core.persistence.api.entity.Implementation; -import org.apache.syncope.core.persistence.jpa.validation.entity.ImplementationCheck; +import org.apache.syncope.core.persistence.common.validation.ImplementationCheck; @Entity @Table(name = JPAImplementation.TABLE) diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAMailTemplate.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAMailTemplate.java index 43b695370a..b6875a33f6 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAMailTemplate.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAMailTemplate.java @@ -58,5 +58,4 @@ public String getHTMLTemplate() { public void setHTMLTemplate(final String htmlTemplate) { this.htmlTemplate = htmlTemplate; } - } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPlainSchema.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPlainSchema.java index 09a1ba9c23..3bb3c6f622 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPlainSchema.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPlainSchema.java @@ -34,7 +34,7 @@ import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; import org.apache.syncope.core.persistence.api.entity.Implementation; import org.apache.syncope.core.persistence.api.entity.PlainSchema; -import org.apache.syncope.core.persistence.jpa.validation.entity.PlainSchemaCheck; +import org.apache.syncope.core.persistence.common.validation.PlainSchemaCheck; @Entity @Table(name = JPAPlainSchema.TABLE) @@ -218,5 +218,4 @@ public String getMimeType() { public void setMimeType(final String mimeType) { this.mimeType = mimeType; } - } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPrivilege.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPrivilege.java index 5d7380b8d0..1485de8e4b 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPrivilege.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPrivilege.java @@ -24,7 +24,7 @@ import jakarta.persistence.Table; import org.apache.syncope.core.persistence.api.entity.Application; import org.apache.syncope.core.persistence.api.entity.Privilege; -import org.apache.syncope.core.persistence.jpa.validation.entity.PrivilegeCheck; +import org.apache.syncope.core.persistence.common.validation.PrivilegeCheck; @Entity @Table(name = JPAPrivilege.TABLE) @@ -73,5 +73,4 @@ public String getSpec() { public void setSpec(final String spec) { this.spec = spec; } - } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java index cb70e05c08..0f4c84758b 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java @@ -46,13 +46,13 @@ import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; +import org.apache.syncope.core.persistence.common.validation.RealmCheck; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccessPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAttrReleasePolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAuthPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPasswordPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPATicketExpirationPolicy; -import org.apache.syncope.core.persistence.jpa.validation.entity.RealmCheck; @Entity @Table(name = JPARealm.TABLE, uniqueConstraints = diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARelationshipType.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARelationshipType.java index 04c1ba5d75..ebd7cc759c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARelationshipType.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARelationshipType.java @@ -22,7 +22,7 @@ import jakarta.persistence.Entity; import jakarta.persistence.Table; import org.apache.syncope.core.persistence.api.entity.RelationshipType; -import org.apache.syncope.core.persistence.jpa.validation.entity.RelationshipTypeCheck; +import org.apache.syncope.core.persistence.common.validation.RelationshipTypeCheck; @Entity @Table(name = JPARelationshipType.TABLE) diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARemediation.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARemediation.java index e845355d55..47a7e22530 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARemediation.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARemediation.java @@ -32,8 +32,8 @@ import org.apache.syncope.core.persistence.api.entity.AnyType; 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.common.validation.RemediationCheck; import org.apache.syncope.core.persistence.jpa.entity.task.JPAPullTask; -import org.apache.syncope.core.persistence.jpa.validation.entity.RemediationCheck; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @Entity diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java index 19275ce564..1c5df1f142 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java @@ -32,7 +32,7 @@ import org.apache.syncope.core.persistence.api.entity.Implementation; import org.apache.syncope.core.persistence.api.entity.Report; import org.apache.syncope.core.persistence.api.entity.ReportExec; -import org.apache.syncope.core.persistence.jpa.validation.entity.ReportCheck; +import org.apache.syncope.core.persistence.common.validation.ReportCheck; @Entity @Table(name = JPAReport.TABLE) diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java index 2a09b7f115..8530a1f057 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java @@ -20,14 +20,12 @@ import com.fasterxml.jackson.core.type.TypeReference; import jakarta.persistence.Cacheable; -import jakarta.persistence.CascadeType; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinTable; import jakarta.persistence.Lob; import jakarta.persistence.ManyToMany; -import jakarta.persistence.OneToOne; import jakarta.persistence.PostLoad; import jakarta.persistence.PostPersist; import jakarta.persistence.PostUpdate; @@ -47,15 +45,11 @@ import org.apache.syncope.core.persistence.api.entity.Privilege; 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.DynRoleMembership; -import org.apache.syncope.core.persistence.jpa.entity.user.JPADynRoleMembership; -import org.apache.syncope.core.persistence.jpa.validation.entity.RoleCheck; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @Entity @Table(name = JPARole.TABLE) @Cacheable -@RoleCheck public class JPARole extends AbstractProvidedKeyEntity implements Role { private static final long serialVersionUID = -7657701119422588832L; @@ -71,6 +65,11 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role { @Transient private Set entitlementsSet = new HashSet<>(); + private String dynMembershipCond; + + @Lob + private String anyLayout; + @ManyToMany(fetch = FetchType.EAGER) @JoinTable(joinColumns = @JoinColumn(name = "role_id"), @@ -91,13 +90,6 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role { @Valid private List dynRealms = new ArrayList<>(); - @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "role") - @Valid - private JPADynRoleMembership dynMembership; - - @Lob - private String anyLayout; - @ManyToMany(fetch = FetchType.EAGER) @JoinTable(joinColumns = @JoinColumn(name = "role_id"), @@ -113,6 +105,16 @@ public Set getEntitlements() { return entitlementsSet; } + @Override + public String getDynMembershipCond() { + return dynMembershipCond; + } + + @Override + public void setDynMembershipCond(final String dynMembershipCond) { + this.dynMembershipCond = dynMembershipCond; + } + @Override public boolean add(final Realm realm) { checkType(realm, JPARealm.class); @@ -135,17 +137,6 @@ public List getDynRealms() { return dynRealms; } - @Override - public DynRoleMembership getDynMembership() { - return dynMembership; - } - - @Override - public void setDynMembership(final DynRoleMembership dynMembership) { - checkType(dynMembership, JPADynRoleMembership.class); - this.dynMembership = (JPADynRoleMembership) dynMembership; - } - @Override public String getAnyLayout() { return anyLayout; @@ -179,8 +170,7 @@ protected void json2list(final boolean clearFirst) { getEntitlements().clear(); } if (entitlements != null) { - getEntitlements().addAll( - POJOHelper.deserialize(entitlements, TYPEREF)); + getEntitlements().addAll(POJOHelper.deserialize(entitlements, TYPEREF)); } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASRARoute.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASRARoute.java index f0a75bf44b..ad8b444987 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASRARoute.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASRARoute.java @@ -33,7 +33,7 @@ import org.apache.syncope.common.lib.types.SRARoutePredicate; import org.apache.syncope.common.lib.types.SRARouteType; import org.apache.syncope.core.persistence.api.entity.SRARoute; -import org.apache.syncope.core.persistence.jpa.validation.entity.SRARouteCheck; +import org.apache.syncope.core.persistence.common.validation.SRARouteCheck; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @Entity @@ -155,9 +155,9 @@ public void setOrder(final int order) { @Override public List getFilters() { - return filters == null - ? List.of() - : List.of(POJOHelper.deserialize(filters, SRARouteFilter[].class)); + return Optional.ofNullable(filters). + map(f -> List.of(POJOHelper.deserialize(f, SRARouteFilter[].class))). + orElse(List.of()); } @Override @@ -167,9 +167,9 @@ public void setFilters(final List filters) { @Override public List getPredicates() { - return predicates == null - ? List.of() - : List.of(POJOHelper.deserialize(predicates, SRARoutePredicate[].class)); + return Optional.ofNullable(predicates). + map(f -> List.of(POJOHelper.deserialize(f, SRARoutePredicate[].class))). + orElse(List.of()); } @Override diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthModule.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthModule.java index b4afa99513..bc3ac02516 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthModule.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAAuthModule.java @@ -146,5 +146,4 @@ public void postSave() { public void list2json() { items = POJOHelper.serialize(getItems()); } - } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAClientAppUtils.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAClientAppUtils.java deleted file mode 100644 index f1f453f2e0..0000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAClientAppUtils.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.entity.am; - -import org.apache.syncope.common.lib.types.ClientAppType; -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.ClientAppUtils; -import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp; -import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp; - -public class JPAClientAppUtils implements ClientAppUtils { - - private final ClientAppType type; - - protected JPAClientAppUtils(final ClientAppType type) { - this.type = type; - } - - @Override - public ClientAppType getType() { - return type; - } - - @Override - public Class clientAppClass() { - switch (type) { - case OIDCRP: - return OIDCRPClientApp.class; - case CASSP: - return CASSPClientApp.class; - case SAML2SP: - default: - return SAML2SPClientApp.class; - } - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAClientAppUtilsFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAClientAppUtilsFactory.java deleted file mode 100644 index ccf5d1a785..0000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAClientAppUtilsFactory.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.entity.am; - -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.ClientAppType; -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.ClientAppUtils; -import org.apache.syncope.core.persistence.api.entity.am.ClientAppUtilsFactory; -import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp; -import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp; - -public class JPAClientAppUtilsFactory implements ClientAppUtilsFactory { - - @Override - public ClientAppUtils getInstance(final ClientAppType type) { - return new JPAClientAppUtils(type); - } - - @Override - public ClientAppUtils getInstance(final ClientApp clientApp) { - ClientAppType type; - if (clientApp instanceof SAML2SPClientApp) { - type = ClientAppType.SAML2SP; - } else if (clientApp instanceof CASSPClientApp) { - type = ClientAppType.CASSP; - } else if (clientApp instanceof OIDCRPClientApp) { - type = ClientAppType.OIDCRP; - } else { - throw new IllegalArgumentException("Invalid client app: " + clientApp); - } - - return getInstance(type); - } - - @Override - public ClientAppUtils getInstance(final Class clientAppClass) { - ClientAppType type; - if (clientAppClass == SAML2SPClientAppTO.class) { - type = ClientAppType.SAML2SP; - } else if (clientAppClass == CASSPClientAppTO.class) { - type = ClientAppType.CASSP; - } else if (clientAppClass == OIDCRPClientAppTO.class) { - type = ClientAppType.OIDCRP; - } else { - throw new IllegalArgumentException("Invalid ClientAppTO app: " + clientAppClass.getName()); - } - - return getInstance(type); - } - - @Override - public ClientAppUtils getInstance(final ClientAppTO clientAppTO) { - return getInstance(clientAppTO.getClass()); - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAWAConfigEntry.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAWAConfigEntry.java index b85b8c7bda..e234d5ab0d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAWAConfigEntry.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/am/JPAWAConfigEntry.java @@ -23,6 +23,7 @@ import jakarta.persistence.Lob; import jakarta.persistence.Table; import java.util.List; +import java.util.Optional; import org.apache.syncope.core.persistence.api.entity.am.WAConfigEntry; import org.apache.syncope.core.persistence.jpa.entity.AbstractProvidedKeyEntity; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @@ -43,9 +44,7 @@ public class JPAWAConfigEntry extends AbstractProvidedKeyEntity implements WACon @Override public List getValues() { - return waConfigValues == null - ? List.of() - : POJOHelper.deserialize(waConfigValues, TYPEREF); + return Optional.ofNullable(waConfigValues).map(v -> POJOHelper.deserialize(v, TYPEREF)).orElse(List.of()); } @Override diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java index 525d6634f9..f3ed177cd3 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java @@ -42,11 +42,11 @@ import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttr; import org.apache.syncope.core.persistence.api.entity.anyobject.ARelationship; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.persistence.common.validation.AnyObjectCheck; import org.apache.syncope.core.persistence.jpa.entity.AbstractGroupableRelatable; import org.apache.syncope.core.persistence.jpa.entity.JPAAnyType; import org.apache.syncope.core.persistence.jpa.entity.JPAAnyTypeClass; import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource; -import org.apache.syncope.core.persistence.jpa.validation.entity.AnyObjectCheck; @Entity @Table(name = JPAAnyObject.TABLE, uniqueConstraints = diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java index b963388b9e..5541dd4c12 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java @@ -45,13 +45,13 @@ import org.apache.syncope.core.persistence.api.entity.group.TypeExtension; import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.common.validation.GroupCheck; import org.apache.syncope.core.persistence.jpa.entity.AbstractAny; import org.apache.syncope.core.persistence.jpa.entity.JPAAnyTypeClass; import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource; import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADynGroupMembership; import org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership; import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser; -import org.apache.syncope.core.persistence.jpa.validation.entity.GroupCheck; import org.apache.syncope.core.spring.ApplicationContextProvider; @Entity @@ -81,7 +81,8 @@ public class JPAGroup extends AbstractAny implements Group { @JoinColumn(name = "group_id"), inverseJoinColumns = @JoinColumn(name = "resource_id"), - uniqueConstraints = @UniqueConstraint(columnNames = { "group_id", "resource_id" })) + uniqueConstraints = + @UniqueConstraint(columnNames = { "group_id", "resource_id" })) private List resources = new ArrayList<>(); @ManyToMany(fetch = FetchType.LAZY) @@ -89,7 +90,8 @@ public class JPAGroup extends AbstractAny implements Group { @JoinColumn(name = "group_id"), inverseJoinColumns = @JoinColumn(name = "anyTypeClass_id"), - uniqueConstraints = @UniqueConstraint(columnNames = { "group_id", "anyTypeClass_id" })) + uniqueConstraints = + @UniqueConstraint(columnNames = { "group_id", "anyTypeClass_id" })) private List auxClasses = new ArrayList<>(); @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "group") @@ -169,8 +171,8 @@ public boolean remove(final GPlainAttr attr) { @Override public Optional getPlainAttr(final String plainSchema) { - return getPlainAttrs().stream().filter(plainAttr - -> plainAttr != null && plainAttr.getSchema() != null + return getPlainAttrs().stream(). + filter(plainAttr -> plainAttr != null && plainAttr.getSchema() != null && plainSchema.equals(plainAttr.getSchema().getKey())).findFirst(); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/AbstractPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/AbstractPolicy.java index debbff810a..39d8137bfc 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/AbstractPolicy.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/AbstractPolicy.java @@ -23,8 +23,8 @@ import jakarta.persistence.InheritanceType; import jakarta.validation.constraints.NotNull; import org.apache.syncope.core.persistence.api.entity.policy.Policy; +import org.apache.syncope.core.persistence.common.validation.PolicyCheck; import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity; -import org.apache.syncope.core.persistence.jpa.validation.entity.PolicyCheck; @Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtils.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtils.java deleted file mode 100644 index e6bd285d28..0000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtils.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.entity.policy; - -import org.apache.syncope.common.lib.types.PolicyType; -import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy; -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.PasswordPolicy; -import org.apache.syncope.core.persistence.api.entity.policy.Policy; -import org.apache.syncope.core.persistence.api.entity.policy.PolicyUtils; -import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; -import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; -import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; -import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; - -public class JPAPolicyUtils implements PolicyUtils { - - private final PolicyType type; - - protected JPAPolicyUtils(final PolicyType type) { - this.type = type; - } - - @Override - public PolicyType getType() { - return type; - } - - @Override - public Class policyClass() { - switch (type) { - case ACCOUNT: - return AccountPolicy.class; - - case PASSWORD: - return PasswordPolicy.class; - - case AUTH: - return AuthPolicy.class; - - case ATTR_RELEASE: - return AttrReleasePolicy.class; - - case ACCESS: - return AccessPolicy.class; - - case TICKET_EXPIRATION: - return TicketExpirationPolicy.class; - - case PROPAGATION: - return PropagationPolicy.class; - - case PULL: - return PullPolicy.class; - - case PUSH: - default: - return PushPolicy.class; - } - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtilsFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtilsFactory.java deleted file mode 100644 index 0a0de4440f..0000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtilsFactory.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.entity.policy; - -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.PasswordPolicyTO; -import org.apache.syncope.common.lib.policy.PolicyTO; -import org.apache.syncope.common.lib.policy.PropagationPolicyTO; -import org.apache.syncope.common.lib.policy.PullPolicyTO; -import org.apache.syncope.common.lib.policy.PushPolicyTO; -import org.apache.syncope.common.lib.policy.TicketExpirationPolicyTO; -import org.apache.syncope.common.lib.types.PolicyType; -import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy; -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.PasswordPolicy; -import org.apache.syncope.core.persistence.api.entity.policy.Policy; -import org.apache.syncope.core.persistence.api.entity.policy.PolicyUtils; -import org.apache.syncope.core.persistence.api.entity.policy.PolicyUtilsFactory; -import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; -import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; -import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; -import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; - -public class JPAPolicyUtilsFactory implements PolicyUtilsFactory { - - @Override - public PolicyUtils getInstance(final PolicyType type) { - return new JPAPolicyUtils(type); - } - - @Override - public PolicyUtils getInstance(final Policy policy) { - PolicyType type; - if (policy instanceof AccountPolicy) { - type = PolicyType.ACCOUNT; - } else if (policy instanceof PasswordPolicy) { - type = PolicyType.PASSWORD; - } else if (policy instanceof PropagationPolicy) { - type = PolicyType.PROPAGATION; - } else if (policy instanceof PullPolicy) { - type = PolicyType.PULL; - } else if (policy instanceof PushPolicy) { - type = PolicyType.PUSH; - } else if (policy instanceof AuthPolicy) { - type = PolicyType.AUTH; - } else if (policy instanceof AccessPolicy) { - type = PolicyType.ACCESS; - } else if (policy instanceof AttrReleasePolicy) { - type = PolicyType.ATTR_RELEASE; - } else if (policy instanceof TicketExpirationPolicy) { - type = PolicyType.TICKET_EXPIRATION; - } else { - throw new IllegalArgumentException("Invalid policy: " + policy); - } - - return getInstance(type); - } - - @Override - public PolicyUtils getInstance(final Class policyClass) { - PolicyType type; - if (policyClass == AccountPolicyTO.class) { - type = PolicyType.ACCOUNT; - } else if (policyClass == PasswordPolicyTO.class) { - type = PolicyType.PASSWORD; - } else if (policyClass == PropagationPolicyTO.class) { - type = PolicyType.PROPAGATION; - } else if (policyClass == PullPolicyTO.class) { - type = PolicyType.PULL; - } else if (policyClass == PushPolicyTO.class) { - type = PolicyType.PUSH; - } else if (policyClass == AuthPolicyTO.class) { - type = PolicyType.AUTH; - } else if (policyClass == AccessPolicyTO.class) { - type = PolicyType.ACCESS; - } else if (policyClass == AttrReleasePolicyTO.class) { - type = PolicyType.ATTR_RELEASE; - } else if (policyClass == TicketExpirationPolicyTO.class) { - type = PolicyType.TICKET_EXPIRATION; - } else { - throw new IllegalArgumentException("Invalid PolicyTO class: " + policyClass.getName()); - } - - return getInstance(type); - } - - @Override - public PolicyUtils getInstance(final PolicyTO policyTO) { - return getInstance(policyTO.getClass()); - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java index 653f9812e8..edcab017ac 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java @@ -31,8 +31,8 @@ import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask; import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.common.validation.ProvisioningTaskCheck; import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource; -import org.apache.syncope.core.persistence.jpa.validation.entity.ProvisioningTaskCheck; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @MappedSuperclass diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPropagationTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPropagationTask.java index f76e64c36a..6616d67ed6 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPropagationTask.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPropagationTask.java @@ -35,8 +35,8 @@ import org.apache.syncope.core.persistence.api.entity.task.PropagationData; import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.apache.syncope.core.persistence.common.validation.PropagationTaskCheck; import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource; -import org.apache.syncope.core.persistence.jpa.validation.entity.PropagationTaskCheck; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; /** diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java index 5077113d86..df564d1233 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java @@ -34,8 +34,8 @@ import org.apache.syncope.core.persistence.api.entity.Implementation; 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.common.validation.SchedTaskCheck; import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation; -import org.apache.syncope.core.persistence.jpa.validation.entity.SchedTaskCheck; @Entity @Table(name = JPASchedTask.TABLE) diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPALinkedAccount.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPALinkedAccount.java index 877dd3b248..25d93d67ef 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPALinkedAccount.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPALinkedAccount.java @@ -151,7 +151,7 @@ public void setCipherAlgorithm(final CipherAlgorithm cipherAlgorithm) { throw new IllegalArgumentException("Cannot override existing cipher algorithm"); } } - + @Override public boolean canDecodeSecrets() { return this.cipherAlgorithm != null && this.cipherAlgorithm.isInvertible(); @@ -173,8 +173,8 @@ public void setPassword(final String password) { try { this.password = ENCRYPTOR.encode(password, cipherAlgorithm == null ? CipherAlgorithm.valueOf(ApplicationContextProvider.getBeanFactory().getBean(ConfParamOps.class). - get(AuthContextUtils.getDomain(), "password.cipher.algorithm", CipherAlgorithm.AES.name(), - String.class)) + get(AuthContextUtils.getDomain(), "password.cipher.algorithm", CipherAlgorithm.AES.name(), + String.class)) : cipherAlgorithm); } catch (Exception e) { LOG.error("Could not encode password", e); @@ -206,8 +206,8 @@ public boolean remove(final LAPlainAttr attr) { @Override public Optional getPlainAttr(final String plainSchema) { - return getPlainAttrs().stream().filter(plainAttr - -> plainAttr != null && plainAttr.getSchema() != null + return getPlainAttrs().stream(). + filter(plainAttr -> plainAttr != null && plainAttr.getSchema() != null && plainSchema.equals(plainAttr.getSchema().getKey())).findFirst(); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPASecurityQuestion.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPASecurityQuestion.java index 06d0e7a76f..7b86f38f26 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPASecurityQuestion.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPASecurityQuestion.java @@ -44,5 +44,4 @@ public String getContent() { public void setContent(final String content) { this.content = content; } - } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java index 7a9cd7a2f3..fd1593701b 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java @@ -97,9 +97,6 @@ public class JPAUser @Valid protected List plainAttrs = new ArrayList<>(); - @Column(nullable = true) - protected String status; - @Lob protected String token; @@ -267,16 +264,6 @@ protected List internalGetPlainAttrs() { return plainAttrs; } - @Override - public String getStatus() { - return status; - } - - @Override - public void setStatus(final String status) { - this.status = status; - } - @Override public void generateToken(final int tokenLength, final int tokenExpireTime) { this.token = SecureRandomUtils.generateRandomPassword(tokenLength); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/openjpa/EntityValidationListener.java similarity index 87% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/openjpa/EntityValidationListener.java index 0fdaa0ead2..01f6391c1e 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/openjpa/EntityValidationListener.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.jpa.openjpa; import jakarta.persistence.PrePersist; import jakarta.persistence.PreUpdate; @@ -24,7 +24,7 @@ import jakarta.validation.Validator; import java.util.Set; import org.apache.commons.lang3.ClassUtils; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.DynMembership; import org.apache.syncope.core.persistence.api.entity.Entity; @@ -47,7 +47,7 @@ public class EntityValidationListener { @PrePersist @PreUpdate public void validate(final Object object) { - final Validator validator = ApplicationContextProvider.getBeanFactory().getBean(Validator.class); + Validator validator = ApplicationContextProvider.getBeanFactory().getBean(Validator.class); Set> violations = validator.validate(object); if (!violations.isEmpty()) { LOG.warn("Bean validation errors found: {}", violations); @@ -68,8 +68,7 @@ public void validate(final Object object) { } } - throw new InvalidEntityException(entityInt == null - ? "Entity" : entityInt.getSimpleName(), violations); + throw new InvalidEntityException(entityInt == null ? "Entity" : entityInt.getSimpleName(), violations); } } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java index 4f0afc8748..7c4283ca44 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java @@ -19,18 +19,24 @@ package org.apache.syncope.core.persistence.jpa.spring; import jakarta.persistence.ValidationMode; +import java.sql.Connection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.sql.DataSource; import org.apache.syncope.core.persistence.api.DomainHolder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.datasource.DataSourceUtils; import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor; /** * Container for common configuration options among all EntityManagerFactory entities (one for each domain). * Acts as a commodity place for fetching each domain's {@link DataSource}.. */ -public class CommonEntityManagerFactoryConf implements DomainHolder { +public class CommonEntityManagerFactoryConf implements DomainHolder { + + private static final Logger LOG = LoggerFactory.getLogger(DomainHolder.class); private final Map domains = new ConcurrentHashMap<>(); @@ -47,6 +53,22 @@ public Map getDomains() { return domains; } + @Override + public Map getHealthInfo() { + Map healthInfo = new HashMap<>(domains.size()); + + domains.forEach((domain, dataSource) -> { + try (Connection conn = DataSourceUtils.getConnection(dataSource)) { + healthInfo.put(domain, !conn.isValid(0)); + } catch (Exception e) { + healthInfo.put(domain, false); + LOG.debug("When attempting to connect to Domain {}", domain, e); + } + }); + + return healthInfo; + } + public String[] getPackagesToScan() { return packagesToScan; } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java index 33aa958ce2..51baabcb7e 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java @@ -59,31 +59,6 @@ public DomainRoutingEntityManagerFactory(final CommonEntityManagerFactoryConf co protected final Map delegates = new ConcurrentHashMap<>(); - protected EntityManagerFactory delegate() { - return delegates.computeIfAbsent(AuthContextUtils.getDomain(), domain -> { - throw new IllegalStateException("Could not find EntityManagerFactory for domain " + domain); - }); - } - - public void initJPASchema() { - OpenJPAEntityManagerFactorySPI emfspi = delegate().unwrap(OpenJPAEntityManagerFactorySPI.class); - JDBCConfiguration jdbcConf = (JDBCConfiguration) emfspi.getConfiguration(); - - MappingRepository mappingRepo = jdbcConf.getMappingRepositoryInstance(); - Collection> classes = mappingRepo.loadPersistentTypes(false, getClass().getClassLoader()); - - String action = "buildSchema(ForeignKeys=true)"; - String props = Configurations.getProperties(action); - action = Configurations.getClassName(action); - MappingTool mappingTool = new MappingTool(jdbcConf, action, false, getClass().getClassLoader()); - Configurations.configureInstance(mappingTool, jdbcConf, props, "SynchronizeMappings"); - - // initialize the schema - classes.forEach(mappingTool::run); - - mappingTool.record(); - } - public void master( final PersistenceProperties props, final JndiObjectFactoryBean dataSource) { @@ -147,6 +122,31 @@ public void remove(final String domain) { close(domain, emf); } + protected EntityManagerFactory delegate() { + return delegates.computeIfAbsent(AuthContextUtils.getDomain(), domain -> { + throw new IllegalStateException("Could not find EntityManagerFactory for domain " + domain); + }); + } + + public void initJPASchema() { + OpenJPAEntityManagerFactorySPI emfspi = delegate().unwrap(OpenJPAEntityManagerFactorySPI.class); + JDBCConfiguration jdbcConf = (JDBCConfiguration) emfspi.getConfiguration(); + + MappingRepository mappingRepo = jdbcConf.getMappingRepositoryInstance(); + Collection> classes = mappingRepo.loadPersistentTypes(false, getClass().getClassLoader()); + + String action = "buildSchema(ForeignKeys=true)"; + String props = Configurations.getProperties(action); + action = Configurations.getClassName(action); + MappingTool mappingTool = new MappingTool(jdbcConf, action, false, getClass().getClassLoader()); + Configurations.configureInstance(mappingTool, jdbcConf, props, "SynchronizeMappings"); + + // initialize the schema + classes.forEach(mappingTool::run); + + mappingTool.record(); + } + @Override public EntityManager createEntityManager() { return delegate().createEntityManager(); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/SyncopeJPARepository.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/SyncopeJPARepository.java index 14ceaa695b..c16dcf6e6b 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/SyncopeJPARepository.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/SyncopeJPARepository.java @@ -26,7 +26,10 @@ public class SyncopeJPARepository extends SimpleJpaRepository { protected final EntityManager entityManager; - public SyncopeJPARepository(final JpaEntityInformation entityInformation, final EntityManager entityManager) { + public SyncopeJPARepository( + final JpaEntityInformation entityInformation, + final EntityManager entityManager) { + super(entityInformation, entityManager); this.entityManager = entityManager; } diff --git a/core/persistence-jpa/src/main/resources/META-INF/spring-orm-oracle.xml b/core/persistence-jpa/src/main/resources/META-INF/spring-orm-oracle.xml index e1778dc308..836556d2f8 100644 --- a/core/persistence-jpa/src/main/resources/META-INF/spring-orm-oracle.xml +++ b/core/persistence-jpa/src/main/resources/META-INF/spring-orm-oracle.xml @@ -26,7 +26,7 @@ under the License. - + diff --git a/core/persistence-jpa/src/main/resources/META-INF/spring-orm-sqlserver.xml b/core/persistence-jpa/src/main/resources/META-INF/spring-orm-sqlserver.xml index 90714ae8b9..e51b9b1da7 100644 --- a/core/persistence-jpa/src/main/resources/META-INF/spring-orm-sqlserver.xml +++ b/core/persistence-jpa/src/main/resources/META-INF/spring-orm-sqlserver.xml @@ -26,7 +26,7 @@ under the License. - + diff --git a/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml b/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml index 4963e4f71c..1602fecee3 100644 --- a/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml +++ b/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml @@ -26,7 +26,7 @@ under the License. - + diff --git a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml index d34c640b3e..868dd99699 100644 --- a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml +++ b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml @@ -29,14 +29,14 @@ under the License. + body="org.apache.syncope.core.persistence.common.attrvalue.EmailAddressValidator"/> + body="org.apache.syncope.core.persistence.common.attrvalue.BinaryValidator"/> diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java index 3ceb876eba..23f7a972a9 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java @@ -21,6 +21,7 @@ import jakarta.persistence.EntityManagerFactory; import java.util.ArrayList; import java.util.List; +import javax.sql.DataSource; import org.apache.syncope.common.keymaster.client.api.ConfParamOps; import org.apache.syncope.common.keymaster.client.api.DomainOps; import org.apache.syncope.common.lib.SyncopeConstants; @@ -85,7 +86,7 @@ public String anonymousUser() { @Bean public TestInitializer testInitializer( final StartupDomainLoader domainLoader, - final DomainHolder domainHolder, + final DomainHolder domainHolder, final ContentLoader contentLoader, final DomainRoutingEntityManagerFactory entityManagerFactory, final ConfigurableApplicationContext ctx) { diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/TestInitializer.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/TestInitializer.java index 604984d768..30c64e217a 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/TestInitializer.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/TestInitializer.java @@ -18,6 +18,7 @@ */ package org.apache.syncope.core.persistence.jpa; +import javax.sql.DataSource; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.content.ContentLoader; @@ -32,7 +33,7 @@ public class TestInitializer implements InitializingBean { private final StartupDomainLoader domainLoader; - private final DomainHolder domainHolder; + private final DomainHolder domainHolder; private final ContentLoader contentLoader; @@ -42,7 +43,7 @@ public class TestInitializer implements InitializingBean { public TestInitializer( final StartupDomainLoader domainLoader, - final DomainHolder domainHolder, + final DomainHolder domainHolder, final ContentLoader contentLoader, final DomainRoutingEntityManagerFactory entityManagerFactory, final ConfigurableApplicationContext ctx) { @@ -61,16 +62,12 @@ public void afterPropertiesSet() throws Exception { domainLoader.load(); - contentLoader.load( - SyncopeConstants.MASTER_DOMAIN, - domainHolder.getDomains().get(SyncopeConstants.MASTER_DOMAIN)); + contentLoader.load(SyncopeConstants.MASTER_DOMAIN); if (domainHolder.getDomains().containsKey("Two")) { AuthContextUtils.runAsAdmin("Two", () -> entityManagerFactory.initJPASchema()); - AuthContextUtils.runAsAdmin("Two", () -> contentLoader.load( - "Two", - domainHolder.getDomains().get("Two"))); + AuthContextUtils.runAsAdmin("Two", () -> contentLoader.load("Two")); } } } diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AccessTokenTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AccessTokenTest.java similarity index 98% rename from core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AccessTokenTest.java rename to core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AccessTokenTest.java index ae7f5aa37d..1954dbe872 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AccessTokenTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AccessTokenTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.outer; +package org.apache.syncope.core.persistence.jpa.inner; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; 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 f0d4909389..4d1cc5652e 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 @@ -60,11 +60,10 @@ import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr; import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.api.utils.FormatUtils; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; import org.apache.syncope.core.persistence.jpa.AbstractTest; -import org.apache.syncope.core.provisioning.api.utils.FormatUtils; -import org.apache.syncope.core.provisioning.api.utils.RealmUtils; import org.apache.syncope.core.spring.security.AuthContextUtils; -import org.apache.syncope.core.spring.security.AuthDataAccessor; import org.apache.syncope.core.spring.security.SyncopeAuthenticationDetails; import org.apache.syncope.core.spring.security.SyncopeGrantedAuthority; import org.junit.jupiter.api.BeforeEach; @@ -275,7 +274,7 @@ public void searchByPageAndSize() { assertTrue(cond.isValid()); - int count = searchDAO.count( + long count = searchDAO.count( realmDAO.getRoot(), true, SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.USER); assertEquals(1, count); @@ -391,7 +390,7 @@ public void searchByResource() { } @Test - public void searchByBooleanAnyCond() { + public void searchByBooleanAttrCond() { AttrCond booleanCond = new AttrCond(AnyCond.Type.EQ); booleanCond.setSchema("show"); booleanCond.setExpression("true"); @@ -632,7 +631,7 @@ public void member() { public void asGroupOwner() { // prepare authentication Map> entForRealms = new HashMap<>(); - roleDAO.findById(AuthDataAccessor.GROUP_OWNER_ROLE).orElseThrow().getEntitlements().forEach(entitlement -> { + roleDAO.findById(RoleDAO.GROUP_OWNER_ROLE).orElseThrow().getEntitlements().forEach(entitlement -> { Set realms = Optional.ofNullable(entForRealms.get(entitlement)).orElseGet(() -> { Set r = new HashSet<>(); entForRealms.put(entitlement, r); @@ -756,7 +755,7 @@ public void issueSYNCOPE433() { SearchCond searchCond = SearchCond.getOr( SearchCond.getLeaf(isNullCond), SearchCond.getLeaf(likeCond)); - int count = searchDAO.count( + long count = searchDAO.count( realmDAO.getRoot(), true, SyncopeConstants.FULL_ADMIN_REALMS, searchCond, AnyTypeKind.USER); assertTrue(count > 0); } 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 1311351de6..68fe4c32c8 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 @@ -26,7 +26,7 @@ import java.util.List; import org.apache.syncope.common.lib.types.AnyTypeKind; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.entity.AnyType; 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 0db45a97bf..07b9229dc0 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 @@ -24,7 +24,7 @@ import java.util.List; import org.apache.syncope.common.lib.types.EntityViolationType; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; import org.apache.syncope.core.persistence.api.entity.DerSchema; import org.apache.syncope.core.persistence.jpa.AbstractTest; @@ -45,7 +45,7 @@ public void findAll() { } @Test - public void search() { + public void findByIdLike() { List schemas = derSchemaDAO.findByIdLike("mderivedd%"); assertEquals(1, schemas.size()); } diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCJWKSTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCJWKSTest.java index 99e6977faa..d1c7c6ca4f 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCJWKSTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/OIDCJWKSTest.java @@ -52,6 +52,5 @@ public void save() throws Exception { jwks = jwksDAO.save(jwks); assertNotNull(jwks); assertNotNull(jwks.getKey()); - } } 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 b135adc751..f6a6dcaa28 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 @@ -35,8 +35,8 @@ import org.apache.syncope.common.lib.types.AttrSchemaType; import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.apache.syncope.common.lib.types.EntityViolationType; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; 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.dao.UserDAO; 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 892799b4d3..6099bb83c0 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 @@ -30,14 +30,13 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.AttrSchemaType; import org.apache.syncope.common.lib.types.EntityViolationType; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; 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.PlainSchema; import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr; import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr; import org.apache.syncope.core.persistence.jpa.AbstractTest; -import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; @@ -58,7 +57,7 @@ public void findAll() { } @Test - public void search() { + public void findByIdLike() { List schemas = plainSchemaDAO.findByIdLike("fullna%"); assertEquals(1, schemas.size()); assertEquals(0, schemas.get(0).getLabels().size()); @@ -73,22 +72,6 @@ public void findByName() { assertFalse(schema.getLabel(Locale.KOREAN).isPresent()); } - @Tag("plainAttrTable") - @Test - public void findAttrs() { - PlainSchema schema = plainSchemaDAO.findById("icon").orElseThrow(); - - List gattrs = plainSchemaDAO.findAttrs(schema, GPlainAttr.class); - assertNotNull(gattrs); - assertFalse(gattrs.isEmpty()); - - schema = plainSchemaDAO.findById("aLong").orElseThrow(); - - List uattrs = plainSchemaDAO.findAttrs(schema, UPlainAttr.class); - assertNotNull(uattrs); - assertTrue(uattrs.isEmpty()); - } - @Test public void hasAttrs() { PlainSchema schema = plainSchemaDAO.findById("icon").orElseThrow(); 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 d50eae8008..3dba860060 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 @@ -19,7 +19,6 @@ package org.apache.syncope.core.persistence.jpa.inner; 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; @@ -43,6 +42,7 @@ import org.apache.syncope.core.persistence.api.dao.PolicyDAO; import org.apache.syncope.core.persistence.api.entity.Implementation; import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy; +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.PasswordPolicy; @@ -75,9 +75,17 @@ public class PolicyTest extends AbstractTest { @Test public void findAll() { - List policies = policyDAO.findAll(); - assertNotNull(policies); - assertFalse(policies.isEmpty()); + assertEquals(16, policyDAO.findAll().size()); + + assertEquals(1, policyDAO.findAll(AccessPolicy.class).size()); + assertEquals(2, policyDAO.findAll(AccountPolicy.class).size()); + assertEquals(2, policyDAO.findAll(AttrReleasePolicy.class).size()); + assertEquals(2, policyDAO.findAll(AuthPolicy.class).size()); + assertEquals(3, policyDAO.findAll(PasswordPolicy.class).size()); + assertEquals(1, policyDAO.findAll(PropagationPolicy.class).size()); + assertEquals(4, policyDAO.findAll(PullPolicy.class).size()); + assertEquals(1, policyDAO.findAll(PushPolicy.class).size()); + assertEquals(0, policyDAO.findAll(TicketExpirationPolicy.class).size()); } @Test @@ -91,7 +99,7 @@ public void findByKey() { PullPolicy pullPolicy = policyDAO.findById( "880f8553-069b-4aed-9930-2cd53873f544", PullPolicy.class).orElseThrow(); - PullCorrelationRuleEntity pullCR = pullPolicy.getCorrelationRule(AnyTypeKind.USER.name()).orElse(null); + PullCorrelationRuleEntity pullCR = pullPolicy.getCorrelationRule(AnyTypeKind.USER.name()).orElseThrow(); DefaultPullCorrelationRuleConf pullCRConf = POJOHelper.deserialize(pullCR.getImplementation().getBody(), DefaultPullCorrelationRuleConf.class); assertNotNull(pullCRConf); @@ -102,7 +110,7 @@ public void findByKey() { PushPolicy pushPolicy = policyDAO.findById( "fb6530e5-892d-4f47-a46b-180c5b6c5c83", PushPolicy.class).orElseThrow(); - PushCorrelationRuleEntity pushCR = pushPolicy.getCorrelationRule(AnyTypeKind.USER.name()).orElse(null); + PushCorrelationRuleEntity pushCR = pushPolicy.getCorrelationRule(AnyTypeKind.USER.name()).orElseThrow(); DefaultPushCorrelationRuleConf pushCRConf = POJOHelper.deserialize(pushCR.getImplementation().getBody(), DefaultPushCorrelationRuleConf.class); assertNotNull(pushCRConf); @@ -122,36 +130,9 @@ public void findByKey() { assertTrue(policyDAO.findById(UUID.randomUUID().toString(), AttrReleasePolicy.class).isEmpty()); } - @Test - public void findByType() { - List propagationPolicies = policyDAO.findAll(PropagationPolicy.class); - assertNotNull(propagationPolicies); - assertFalse(propagationPolicies.isEmpty()); - - List pullPolicies = policyDAO.findAll(PullPolicy.class); - assertNotNull(pullPolicies); - assertFalse(pullPolicies.isEmpty()); - - List accessPolicies = policyDAO.findAll(AccessPolicy.class); - assertNotNull(accessPolicies); - assertEquals(1, accessPolicies.size()); - - List authPolicies = policyDAO.findAll(AuthPolicy.class); - assertNotNull(authPolicies); - assertEquals(2, authPolicies.size()); - - List attrReleasePolicies = policyDAO.findAll(AttrReleasePolicy.class); - assertNotNull(attrReleasePolicies); - assertEquals(2, attrReleasePolicies.size()); - - List ticketExpirationPolicies = policyDAO.findAll(TicketExpirationPolicy.class); - assertNotNull(ticketExpirationPolicies); - assertEquals(0, ticketExpirationPolicies.size()); - } - @Test public void createPropagation() { - int beforeCount = policyDAO.findAll().size(); + long beforeCount = policyDAO.findAll().size(); PropagationPolicy propagationPolicy = entityFactory.newEntity(PropagationPolicy.class); propagationPolicy.setName("Propagation policy"); @@ -165,7 +146,7 @@ public void createPropagation() { assertEquals(BackOffStrategy.EXPONENTIAL, propagationPolicy.getBackOffStrategy()); assertEquals(BackOffStrategy.EXPONENTIAL.getDefaultBackOffParams(), propagationPolicy.getBackOffParams()); - int afterCount = policyDAO.findAll().size(); + long afterCount = policyDAO.findAll().size(); assertEquals(afterCount, beforeCount + 1); } @@ -259,7 +240,7 @@ public void createPush() { @Test public void createAccess() { - int beforeCount = policyDAO.findAll().size(); + long beforeCount = policyDAO.findAll().size(); AccessPolicy accessPolicy = entityFactory.newEntity(AccessPolicy.class); accessPolicy.setName("AttrReleasePolicyAllowEverything"); @@ -273,13 +254,13 @@ public void createAccess() { assertNotNull(accessPolicy); assertNotNull(accessPolicy.getKey()); - int afterCount = policyDAO.findAll().size(); + long afterCount = policyDAO.findAll().size(); assertEquals(afterCount, beforeCount + 1); } @Test public void createAuth() { - int beforeCount = policyDAO.findAll().size(); + long beforeCount = policyDAO.findAll().size(); AuthPolicy authPolicy = entityFactory.newEntity(AuthPolicy.class); authPolicy.setName("AuthPolicyTest"); @@ -294,13 +275,13 @@ public void createAuth() { assertNotNull(authPolicy); assertNotNull(authPolicy.getKey()); - int afterCount = policyDAO.findAll().size(); + long afterCount = policyDAO.findAll().size(); assertEquals(afterCount, beforeCount + 1); } @Test public void createAttrRelease() { - int beforeCount = policyDAO.findAll().size(); + long beforeCount = policyDAO.findAll().size(); AttrReleasePolicy attrReleasePolicy = entityFactory.newEntity(AttrReleasePolicy.class); attrReleasePolicy.setName("AttrReleasePolicyAllowEverything"); @@ -318,13 +299,13 @@ public void createAttrRelease() { assertNotNull(attrReleasePolicy.getStatus()); assertNotNull(((DefaultAttrReleasePolicyConf) attrReleasePolicy.getConf()).getAllowedAttrs()); - int afterCount = policyDAO.findAll().size(); + long afterCount = policyDAO.findAll().size(); assertEquals(afterCount, beforeCount + 1); } @Test public void createTicketExpiration() { - int beforeCount = policyDAO.findAll().size(); + long beforeCount = policyDAO.findAll().size(); TicketExpirationPolicy ticketExpirationPolicy = entityFactory.newEntity(TicketExpirationPolicy.class); ticketExpirationPolicy.setName("TicketExpirationPolicyTest"); @@ -346,7 +327,7 @@ public void createTicketExpiration() { assertNotNull(((DefaultTicketExpirationPolicyConf) ticketExpirationPolicy.getConf()).getTgtConf()); assertNotNull(((DefaultTicketExpirationPolicyConf) ticketExpirationPolicy.getConf()).getStConf()); - int afterCount = policyDAO.findAll().size(); + long afterCount = policyDAO.findAll().size(); assertEquals(afterCount, beforeCount + 1); } 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 a2af00ec79..ad90c22d55 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 @@ -28,7 +28,7 @@ import java.util.List; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.EntityViolationType; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; import org.apache.syncope.core.persistence.api.dao.MalformedPathException; import org.apache.syncope.core.persistence.api.dao.PolicyDAO; import org.apache.syncope.core.persistence.api.dao.RealmDAO; 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 eb3a1f605e..22d644ff3d 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 @@ -25,7 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; import org.apache.syncope.core.persistence.api.entity.RelationshipType; 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 9d8b70596b..d59971c79a 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 @@ -31,7 +31,7 @@ import org.apache.syncope.common.lib.types.EntityViolationType; import org.apache.syncope.common.lib.types.ResourceOperation; import org.apache.syncope.common.lib.types.TaskType; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; 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; 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 7cc9a0b49a..ad7a22ce31 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 @@ -35,7 +35,7 @@ import org.apache.syncope.common.lib.types.EntityViolationType; import org.apache.syncope.common.lib.types.IdMEntitlement; import org.apache.syncope.common.lib.types.MappingPurpose; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; 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; @@ -309,8 +309,7 @@ public void saveWithGroupMappingType() { entityManager.flush(); assertNotNull(actual); - assertEquals(3, actual.getProvisionByAnyType(AnyTypeKind.USER.name()). - get().getMapping().getItems().size()); + assertEquals(3, actual.getProvisionByAnyType(AnyTypeKind.USER.name()).get().getMapping().getItems().size()); } @Test diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPEntityTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPEntityTest.java index 6c6cde5bb9..324e6e0cfc 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPEntityTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/SAML2SPEntityTest.java @@ -25,6 +25,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.math.BigInteger; +import java.nio.charset.StandardCharsets; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; @@ -33,7 +34,7 @@ import java.security.cert.CertificateFactory; import java.util.Date; import java.util.UUID; -import org.apache.commons.io.IOUtils; +import org.apache.cxf.helpers.IOUtils; import org.apache.syncope.core.persistence.api.dao.SAML2SPEntityDAO; import org.apache.syncope.core.persistence.api.entity.am.SAML2SPEntity; import org.apache.syncope.core.persistence.jpa.AbstractTest; @@ -98,7 +99,8 @@ private static Certificate createSelfSignedCert(final KeyPair keyPair) throws Ex private SAML2SPEntity create(final String owner) throws Exception { SAML2SPEntity entity = entityFactory.newEntity(SAML2SPEntity.class); entity.setKey(owner); - entity.setMetadata(IOUtils.toByteArray(new ClassPathResource("sp-metadata.xml").getInputStream())); + entity.setMetadata(IOUtils.toString( + new ClassPathResource("sp-metadata.xml").getInputStream()).getBytes(StandardCharsets.UTF_8)); KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); char[] pwdArray = "password".toCharArray(); 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 f60626f677..55a9bc8a10 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 @@ -30,8 +30,8 @@ import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; import org.apache.syncope.core.persistence.api.entity.task.TaskExec; import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory; +import org.apache.syncope.core.persistence.api.utils.FormatUtils; import org.apache.syncope.core.persistence.jpa.AbstractTest; -import org.apache.syncope.core.provisioning.api.utils.FormatUtils; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Pageable; diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/VirSchemaTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/VirSchemaTest.java index cfc593ec19..7a07d93c9a 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/VirSchemaTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/VirSchemaTest.java @@ -25,7 +25,7 @@ import java.util.List; import org.apache.syncope.common.lib.types.EntityViolationType; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO; @@ -54,7 +54,7 @@ public void findAll() { } @Test - public void search() { + public void findByIdLike() { List schemas = virSchemaDAO.findByIdLike("rvirtuald%"); assertEquals(1, schemas.size()); } 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 280ced6964..5fbd9a0512 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 @@ -31,7 +31,7 @@ import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.common.lib.types.IdRepoEntitlement; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; @@ -45,12 +45,11 @@ import org.apache.syncope.core.persistence.api.entity.Role; import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr; import org.apache.syncope.core.persistence.api.entity.group.Group; -import org.apache.syncope.core.persistence.api.entity.user.DynRoleMembership; import org.apache.syncope.core.persistence.api.entity.user.UMembership; import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr; import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; import org.apache.syncope.core.persistence.jpa.AbstractTest; -import org.apache.syncope.core.provisioning.api.utils.RealmUtils; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; @@ -90,12 +89,7 @@ public void searchByDynMembership() { role.add(realmDAO.findByFullPath("/even/two").orElseThrow()); role.getEntitlements().add(IdRepoEntitlement.AUDIT_LIST); role.getEntitlements().add(IdRepoEntitlement.AUDIT_SET); - - DynRoleMembership dynMembership = entityFactory.newEntity(DynRoleMembership.class); - dynMembership.setFIQLCond("cool==true"); - dynMembership.setRole(role); - - role.setDynMembership(dynMembership); + role.setDynMembershipCond("cool==true"); role = roleDAO.saveAndRefreshDynMemberships(role); assertNotNull(role); 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 147527164b..a478b1b6bf 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 @@ -25,7 +25,6 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.FIQLQuery; import org.apache.syncope.core.persistence.jpa.AbstractTest; -import org.apache.syncope.core.persistence.jpa.entity.JPAFIQLQuery; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; @@ -50,7 +49,7 @@ public void findByOwner() { entityManager.flush(); - JPAFIQLQuery fiqlQuery = new JPAFIQLQuery(); + FIQLQuery fiqlQuery = entityFactory.newEntity(FIQLQuery.class); fiqlQuery.setOwner(userDAO.findByUsername("rossini").orElseThrow()); assertEquals( 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 7078033195..3ffa362824 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 @@ -34,8 +34,8 @@ import java.util.stream.Collectors; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.AnyTypeKind; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainAttrTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainAttrTest.java index 02126b9f6b..e913040b6d 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainAttrTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/PlainAttrTest.java @@ -23,7 +23,7 @@ import static org.junit.jupiter.api.Assertions.fail; import org.apache.syncope.common.lib.types.AnyTypeKind; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr; 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 e83c5ffb55..3e9499abba 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 @@ -20,7 +20,6 @@ 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 jakarta.persistence.Query; @@ -32,7 +31,7 @@ import java.util.Set; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.IdRepoEntitlement; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; import org.apache.syncope.core.persistence.api.dao.DelegationDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; @@ -41,12 +40,10 @@ 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.Role; -import org.apache.syncope.core.persistence.api.entity.user.DynRoleMembership; import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.jpa.AbstractTest; import org.apache.syncope.core.persistence.jpa.dao.repo.RoleRepoExt; -import org.apache.syncope.core.persistence.jpa.entity.user.JPADynRoleMembership; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; @@ -121,12 +118,7 @@ public void dynMembership() { role.add(realmDAO.findByFullPath("/even/two").orElseThrow()); role.getEntitlements().add(IdRepoEntitlement.AUDIT_LIST); role.getEntitlements().add(IdRepoEntitlement.AUDIT_SET); - - DynRoleMembership dynMembership = entityFactory.newEntity(DynRoleMembership.class); - dynMembership.setFIQLCond("cool==true"); - dynMembership.setRole(role); - - role.setDynMembership(dynMembership); + role.setDynMembershipCond("cool==true"); Role actual = roleDAO.saveAndRefreshDynMemberships(role); assertNotNull(actual); @@ -135,9 +127,7 @@ public void dynMembership() { // 2. verify that dynamic membership is there actual = roleDAO.findById(actual.getKey()).orElseThrow(); - assertNotNull(actual.getDynMembership()); - assertNotNull(actual.getDynMembership().getKey()); - assertEquals(actual, actual.getDynMembership().getRole()); + assertNotNull(actual.getDynMembershipCond()); // 3. verify that expected users have the created role dynamically assigned List members = roleDAO.findDynMembers(actual); @@ -147,7 +137,7 @@ public void dynMembership() { user = userDAO.findById("c9b2dec2-00a7-4855-97c0-d854842b4b24").orElseThrow(); Collection dynRoleMemberships = findDynRoles(user); assertEquals(1, dynRoleMemberships.size()); - assertTrue(dynRoleMemberships.contains(actual.getDynMembership().getRole())); + assertTrue(dynRoleMemberships.contains(actual)); // 4. delete the new user and verify that dynamic membership was updated userDAO.deleteById(newUserKey); @@ -160,14 +150,10 @@ public void dynMembership() { assertEquals("c9b2dec2-00a7-4855-97c0-d854842b4b24", members.get(0)); // 5. delete role and verify that dynamic membership was also removed - String dynMembershipKey = actual.getDynMembership().getKey(); - roleDAO.delete(actual); entityManager.flush(); - assertNull(entityManager.find(JPADynRoleMembership.class, dynMembershipKey)); - dynRoleMemberships = findDynRoles(user); assertTrue(dynRoleMemberships.isEmpty()); } 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 feebba0d81..468cc74785 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 @@ -39,7 +39,7 @@ import org.apache.syncope.common.lib.types.ResourceOperation; import org.apache.syncope.common.lib.types.TaskType; import org.apache.syncope.common.lib.types.UnmatchingRule; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; 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.RealmDAO; @@ -88,8 +88,7 @@ public class TaskTest extends AbstractTest { @Test public void read() { - Task task = taskDAO.findById( - TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); + Task task = taskDAO.findById(TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); assertNotNull(task); assertNotNull(task.getExecs()); @@ -151,6 +150,7 @@ public void addPropagationTaskExecution() { execution.setStart(OffsetDateTime.now()); execution.setExecutor("admin"); task.add(execution); + execution.setTask(task); taskDAO.save(task); entityManager.flush(); @@ -177,6 +177,7 @@ public void addPullTaskExecution() { execution.setMessage("A message"); execution.setExecutor("admin"); task.add(execution); + execution.setTask(task); taskDAO.save(task); entityManager.flush(); @@ -203,6 +204,7 @@ public void addPushTaskExecution() { execution.setMessage("A message"); execution.setExecutor("admin"); task.add(execution); + execution.setTask(task); taskDAO.save(task); entityManager.flush(); @@ -284,7 +286,7 @@ public void savePullTask() { task.setResource(resourceDAO.findById("ws-target-resource-1").orElseThrow()); // this save() finally works - task = (PullTask) taskDAO.save(task); + task = taskDAO.save(task); assertNotNull(task); PullTask actual = (PullTask) taskDAO.findById(TaskType.PULL, task.getKey()).orElseThrow(); @@ -314,7 +316,7 @@ public void issueSYNCOPE144() { task.setMatchingRule(MatchingRule.UPDATE); task.setUnmatchingRule(UnmatchingRule.PROVISION); - task = (PullTask) taskDAO.save(task); + task = taskDAO.save(task); assertNotNull(task); PullTask actual = (PullTask) taskDAO.findById(TaskType.PULL, task.getKey()).orElseThrow(); @@ -325,7 +327,7 @@ public void issueSYNCOPE144() { actual.setName("issueSYNCOPE144_2"); actual.setDescription("issueSYNCOPE144 Description_2"); - actual = (PullTask) taskDAO.save(actual); + actual = taskDAO.save(actual); assertNotNull(actual); assertEquals("issueSYNCOPE144_2", actual.getName()); assertEquals("issueSYNCOPE144 Description_2", actual.getDescription()); 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 1335468725..3a282dd0ed 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 @@ -29,7 +29,7 @@ import java.util.Objects; import java.util.UUID; import org.apache.syncope.common.lib.types.CipherAlgorithm; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.ApplicationDAO; import org.apache.syncope.core.persistence.api.dao.DelegationDAO; 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 ab1e797198..460415affc 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 @@ -22,10 +22,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayOutputStream; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.List; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.core.persistence.api.content.ContentExporter; @@ -51,12 +49,10 @@ public void issueSYNCOPE1128() throws Exception { exporter.export(SyncopeConstants.MASTER_DOMAIN, 100, baos); - String exported = baos.toString(Charset.defaultCharset()); + String exported = baos.toString(StandardCharsets.UTF_8); assertTrue(StringUtils.isNotBlank(exported)); - List realms = IOUtils.readLines( - IOUtils.toInputStream(exported, StandardCharsets.UTF_8), StandardCharsets.UTF_8.name()).stream(). - filter(row -> row.trim().startsWith(" realms = exported.lines().filter(row -> row.trim().startsWith(" + body="org.apache.syncope.core.persistence.common.attrvalue.EmailAddressValidator"/> @@ -353,7 +353,7 @@ under the License. mimeType="image/jpeg"/> + body="org.apache.syncope.core.persistence.common.attrvalue.BinaryValidator"/> diff --git a/core/persistence-jpa/src/test/resources/domains/TwoContent.xml b/core/persistence-jpa/src/test/resources/domains/TwoContent.xml index 872380a4fc..ef38cf3766 100644 --- a/core/persistence-jpa/src/test/resources/domains/TwoContent.xml +++ b/core/persistence-jpa/src/test/resources/domains/TwoContent.xml @@ -27,14 +27,14 @@ under the License. + body="org.apache.syncope.core.persistence.common.attrvalue.EmailAddressValidator"/> + body="org.apache.syncope.core.persistence.common.attrvalue.BinaryValidator"/> diff --git a/core/persistence-neo4j/pom.xml b/core/persistence-neo4j/pom.xml new file mode 100644 index 0000000000..326516a854 --- /dev/null +++ b/core/persistence-neo4j/pom.xml @@ -0,0 +1,211 @@ + + + + + 4.0.0 + + + org.apache.syncope + syncope-core + 4.0.0-SNAPSHOT + + + Apache Syncope Core Persistence Neo4j + Apache Syncope Core Persistence Neo4j + org.apache.syncope.core + syncope-core-persistence-neo4j + jar + + + file:${bundles.directory}/ + ${basedir}/../.. + + + + + org.springframework.boot + spring-boot-starter-data-neo4j + + + + org.apache.syncope.core + syncope-core-persistence-common + ${project.version} + + + + + org.springframework.boot + spring-boot-starter-validation + test + + + org.slf4j + slf4j-simple + test + + + org.slf4j + jcl-over-slf4j + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.bouncycastle + bcpkix-jdk18on + test + + + org.bouncycastle + bcprov-jdk18on + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + + + + src/main/resources + true + + + + + src/test/resources + true + + + + + + + neo4j + + + clean verify + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + **/*Test.java + + + classpath:core-test.properties + ${docker.container.neo4j.ip} + ${docker.container.neo4jTwo.ip} + file:${bundles.directory}/ + + + + + + io.fabric8 + docker-maven-plugin + + + + neo4j + neo4j:${docker.neo4j.version} + + + none + ["apoc"] + + + Started. + + + + /data:rw + /logs:rw + /var/lib/neo4j/data:rw + /var/lib/neo4j/logs:rw + /var/lib/neo4j/metrics:rw + + + + + neo4jTwo + neo4j:${docker.neo4j.version} + + + none + ["apoc"] + + + Started. + + + + /data:rw + /logs:rw + /var/lib/neo4j/data:rw + /var/lib/neo4j/logs:rw + /var/lib/neo4j/metrics:rw + + + + + + + + start-neo4j + pre-integration-test + + start + + + + stop-neo4j + post-integration-test + + stop + remove + + + + + + + + + diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/DomainProperties.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/DomainProperties.java new file mode 100644 index 0000000000..100182e012 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/DomainProperties.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.persistence.neo4j; + +import java.net.URI; +import org.apache.syncope.core.persistence.common.AbstractDomainProperties; +import org.neo4j.driver.internal.async.pool.PoolSettings; + +public class DomainProperties extends AbstractDomainProperties { + + private URI uri; + + private String username; + + private String password; + + private int maxConnectionPoolSize = PoolSettings.DEFAULT_MAX_CONNECTION_POOL_SIZE; + + public URI getUri() { + return uri; + } + + public void setUri(final URI uri) { + this.uri = uri; + } + + public String getUsername() { + return username; + } + + public void setUsername(final String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(final String password) { + this.password = password; + } + + public int getMaxConnectionPoolSize() { + return maxConnectionPoolSize; + } + + public void setMaxConnectionPoolSize(final int maxConnectionPoolSize) { + this.maxConnectionPoolSize = maxConnectionPoolSize; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/MasterDomain.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/MasterDomain.java new file mode 100644 index 0000000000..196b4981a4 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/MasterDomain.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.neo4j; + +import java.io.IOException; +import java.io.InputStream; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Config; +import org.neo4j.driver.Driver; +import org.neo4j.driver.GraphDatabase; +import org.neo4j.driver.Logging; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ResourceLoader; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager; +import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager; + +@EnableConfigurationProperties(PersistenceProperties.class) +@Configuration(proxyBeanMethods = false) +public class MasterDomain { + + @ConditionalOnMissingBean(name = "MasterDriver") + @Bean(name = "MasterDriver") + public Driver masterDriver(final PersistenceProperties props) { + return GraphDatabase.driver( + props.getDomain().get(0).getUri(), + AuthTokens.basic(props.getDomain().get(0).getUsername(), props.getDomain().get(0).getPassword()), + Config.builder(). + withMaxConnectionPoolSize(props.getDomain().get(0).getMaxConnectionPoolSize()). + withDriverMetrics(). + withLogging(Logging.slf4j()).build()); + } + + @ConditionalOnMissingBean(name = "MasterNeo4jClient") + @Bean(name = "MasterNeo4jClient") + public Neo4jClient masterNeo4jClient( + @Qualifier("MasterDriver") + final Driver driver, + final Neo4jBookmarkManager bookmarkManager) { + + return Neo4jClient. + with(driver). + withNeo4jBookmarkManager(bookmarkManager). + build(); + } + + @ConditionalOnMissingBean(name = "MasterNeo4jTransactionManager") + @Bean(name = "MasterNeo4jTransactionManager") + public Neo4jTransactionManager masterNeo4jTransactionManager( + @Qualifier("MasterDriver") + final Driver driver, + final Neo4jBookmarkManager bookmarkManager) { + + return Neo4jTransactionManager. + with(driver). + withBookmarkManager(bookmarkManager). + build(); + } + + @Bean(name = "MasterContentXML") + public InputStream masterContentXML( + final ResourceLoader resourceLoader, + final PersistenceProperties props) throws IOException { + + return resourceLoader.getResource(props.getDomain().get(0).getContent()).getInputStream(); + } + + @Bean(name = "MasterKeymasterConfParamsJSON") + public InputStream masterKeymasterConfParamsJSON( + final ResourceLoader resouceLoader, + final PersistenceProperties props) throws IOException { + + return resouceLoader.getResource(props.getDomain().get(0).getKeymasterConfParams()).getInputStream(); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainHolder.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainHolder.java new file mode 100644 index 0000000000..308f2810ec --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainHolder.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.core.persistence.neo4j; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.apache.syncope.core.persistence.api.DomainHolder; +import org.neo4j.driver.Driver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Neo4jDomainHolder implements DomainHolder { + + private static final Logger LOG = LoggerFactory.getLogger(DomainHolder.class); + + private final Map domains = new ConcurrentHashMap<>(); + + @Override + public Map getDomains() { + return domains; + } + + @Override + public Map getHealthInfo() { + Map healthInfo = new HashMap<>(domains.size()); + + domains.forEach((domain, driver) -> { + try { + driver.verifyConnectivity(); + healthInfo.put(domain, true); + } catch (Exception e) { + healthInfo.put(domain, false); + LOG.debug("When attempting to connect to Domain {}", domain, e); + } + }); + + return healthInfo; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainRegistry.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainRegistry.java new file mode 100644 index 0000000000..0df170a95c --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainRegistry.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j; + +import java.io.ByteArrayInputStream; +import java.net.URI; +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.neo4j.spring.DomainRoutingNeo4jClient; +import org.apache.syncope.core.persistence.neo4j.spring.DomainRoutingNeo4jTransactionManager; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Config; +import org.neo4j.driver.Driver; +import org.neo4j.driver.GraphDatabase; +import org.neo4j.driver.Logging; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager; +import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager; + +public class Neo4jDomainRegistry implements DomainRegistry { + + protected final ConfigurableApplicationContext ctx; + + public Neo4jDomainRegistry(final ConfigurableApplicationContext ctx) { + this.ctx = ctx; + } + + protected DefaultListableBeanFactory beanFactory() { + return (DefaultListableBeanFactory) ctx.getBeanFactory(); + } + + protected void unregisterSingleton(final String name) { + if (beanFactory().containsSingleton(name)) { + beanFactory().destroySingleton(name); + } + } + + protected void registerSingleton(final String name, final Object bean) { + unregisterSingleton(name); + beanFactory().registerSingleton(name, bean); + } + + @SuppressWarnings("unchecked") + protected DomainHolder domainHolder() { + return beanFactory().getBean(DomainHolder.class); + } + + @Override + public void register(final Domain domain) { + // domainDriver + Driver driver = GraphDatabase.driver( + URI.create(domain.getJdbcURL()), + AuthTokens.basic(domain.getDbUsername(), domain.getDbPassword()), + Config.builder(). + withMaxConnectionPoolSize(domain.getPoolMaxActive()). + withDriverMetrics(). + withLogging(Logging.slf4j()).build()); + registerSingleton(domain.getKey().toLowerCase() + "Driver", driver); + + domainHolder().getDomains().put(domain.getKey(), driver); + + // domainNeo4jClient + Neo4jClient neo4jClient = Neo4jClient. + with(driver). + withNeo4jBookmarkManager(ctx.getBean(Neo4jBookmarkManager.class)).build(); + registerSingleton(domain.getKey().toLowerCase() + "Neo4jClient", neo4jClient); + + // DomainRoutingNeo4jClient#add + beanFactory().getBean(DomainRoutingNeo4jClient.class).add(domain.getKey(), neo4jClient); + + // domainNeo4jTransactionManager + Neo4jTransactionManager transactionManager = Neo4jTransactionManager. + with(driver). + withBookmarkManager(ctx.getBean(Neo4jBookmarkManager.class)). + build(); + registerSingleton(domain.getKey().toLowerCase() + "Neo4jTransactionManager", transactionManager); + + // DomainRoutingTransactionManager#add + beanFactory().getBean(DomainRoutingNeo4jTransactionManager.class).add(domain.getKey(), transactionManager); + + // domainContentXML + beanFactory().registerBeanDefinition(domain.getKey() + "ContentXML", + BeanDefinitionBuilder.rootBeanDefinition(ByteArrayInputStream.class). + addConstructorArgValue(domain.getContent().getBytes()). + getBeanDefinition()); + + // domainKeymasterConfParamsJSON + beanFactory().registerBeanDefinition(domain.getKey() + "KeymasterConfParamsJSON", + BeanDefinitionBuilder.rootBeanDefinition(ByteArrayInputStream.class). + addConstructorArgValue(domain.getKeymasterConfParams().getBytes()). + getBeanDefinition()); + } + + @Override + public void unregister(final String domain) { + // domainKeymasterConfParamsJSON + unregisterSingleton(domain + "KeymasterConfParamsJSON"); + beanFactory().removeBeanDefinition(domain + "KeymasterConfParamsJSON"); + + // domainContentXML + unregisterSingleton(domain + "ContentXML"); + beanFactory().removeBeanDefinition(domain + "ContentXML"); + + // domainTransactionManager + unregisterSingleton(domain + "Neo4jTransactionManager"); + // DomainRoutingTransactionManager#remove + beanFactory().getBean(DomainRoutingNeo4jTransactionManager.class).remove(domain); + + // domainNeo4jClient + unregisterSingleton(domain + "Neo4jClient"); + // DomainRoutingNeo4jClient#remove + beanFactory().getBean(DomainRoutingNeo4jClient.class).remove(domain); + + // domainDriver + unregisterSingleton(domain + "Driver"); + + // domainDataSource + unregisterSingleton(domain + "Driver"); + + beanFactory().getBean(DomainHolder.class).getDomains().remove(domain); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java new file mode 100644 index 0000000000..7ef4332525 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java @@ -0,0 +1,1236 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j; + +import jakarta.validation.Validator; +import java.util.Map; +import org.apache.syncope.common.keymaster.client.api.DomainOps; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.core.persistence.api.DomainHolder; +import org.apache.syncope.core.persistence.api.DomainRegistry; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; +import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO; +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.AnyTypeClassDAO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.dao.ApplicationDAO; +import org.apache.syncope.core.persistence.api.dao.AttrRepoDAO; +import org.apache.syncope.core.persistence.api.dao.AuditConfDAO; +import org.apache.syncope.core.persistence.api.dao.AuditEntryDAO; +import org.apache.syncope.core.persistence.api.dao.AuthModuleDAO; +import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO; +import org.apache.syncope.core.persistence.api.dao.BatchDAO; +import org.apache.syncope.core.persistence.api.dao.CASSPClientAppDAO; +import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO; +import org.apache.syncope.core.persistence.api.dao.DelegationDAO; +import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; +import org.apache.syncope.core.persistence.api.dao.EntityCacheDAO; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.FIQLQueryDAO; +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.JobStatusDAO; +import org.apache.syncope.core.persistence.api.dao.MailTemplateDAO; +import org.apache.syncope.core.persistence.api.dao.NotificationDAO; +import org.apache.syncope.core.persistence.api.dao.OIDCJWKSDAO; +import org.apache.syncope.core.persistence.api.dao.OIDCRPClientAppDAO; +import org.apache.syncope.core.persistence.api.dao.PersistenceInfoDAO; +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.RelationshipTypeDAO; +import org.apache.syncope.core.persistence.api.dao.RemediationDAO; +import org.apache.syncope.core.persistence.api.dao.ReportDAO; +import org.apache.syncope.core.persistence.api.dao.ReportExecDAO; +import org.apache.syncope.core.persistence.api.dao.RoleDAO; +import org.apache.syncope.core.persistence.api.dao.SAML2IdPEntityDAO; +import org.apache.syncope.core.persistence.api.dao.SAML2SPClientAppDAO; +import org.apache.syncope.core.persistence.api.dao.SAML2SPEntityDAO; +import org.apache.syncope.core.persistence.api.dao.SRARouteDAO; +import org.apache.syncope.core.persistence.api.dao.SecurityQuestionDAO; +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.dao.UserDAO; +import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.WAConfigDAO; +import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory; +import org.apache.syncope.core.persistence.api.search.SearchCondVisitor; +import org.apache.syncope.core.persistence.common.CommonPersistenceContext; +import org.apache.syncope.core.persistence.common.RuntimeDomainLoader; +import org.apache.syncope.core.persistence.neo4j.content.XMLContentExporter; +import org.apache.syncope.core.persistence.neo4j.content.XMLContentLoader; +import org.apache.syncope.core.persistence.neo4j.dao.Neo4jAnyMatchDAO; +import org.apache.syncope.core.persistence.neo4j.dao.Neo4jAnySearchDAO; +import org.apache.syncope.core.persistence.neo4j.dao.Neo4jAuditEntryDAO; +import org.apache.syncope.core.persistence.neo4j.dao.Neo4jBatchDAO; +import org.apache.syncope.core.persistence.neo4j.dao.Neo4jEntityCacheDAO; +import org.apache.syncope.core.persistence.neo4j.dao.Neo4jJobStatusDAO; +import org.apache.syncope.core.persistence.neo4j.dao.Neo4jOIDCJWKSDAO; +import org.apache.syncope.core.persistence.neo4j.dao.Neo4jPersistenceInfoDAO; +import org.apache.syncope.core.persistence.neo4j.dao.Neo4jPolicyDAO; +import org.apache.syncope.core.persistence.neo4j.dao.Neo4jRealmDAO; +import org.apache.syncope.core.persistence.neo4j.dao.Neo4jTaskDAO; +import org.apache.syncope.core.persistence.neo4j.dao.Neo4jTaskExecDAO; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AccessTokenRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AnyObjectRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AnyObjectRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AnyObjectRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AnyTypeClassRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AnyTypeClassRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AnyTypeClassRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AnyTypeRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AnyTypeRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AnyTypeRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ApplicationRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ApplicationRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ApplicationRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AttrRepoRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AttrRepoRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AttrRepoRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AuditConfRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AuthModuleRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AuthModuleRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AuthModuleRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AuthProfileRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.CASSPClientAppRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.CASSPClientAppRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.CASSPClientAppRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ConnInstanceRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ConnInstanceRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ConnInstanceRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.DelegationRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.DelegationRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.DelegationRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.DerSchemaRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.DerSchemaRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.DerSchemaRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.DynRealmRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.DynRealmRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.DynRealmRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ExternalResourceRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ExternalResourceRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ExternalResourceRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.FIQLQueryRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.FIQLQueryRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.FIQLQueryRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.GroupRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.GroupRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.GroupRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ImplementationRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ImplementationRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ImplementationRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.MailTemplateRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.NotificationRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.NotificationRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.NotificationRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.OIDCRPClientAppRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.OIDCRPClientAppRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.OIDCRPClientAppRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.PlainSchemaRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.PlainSchemaRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.PlainSchemaRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.RelationshipTypeRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.RelationshipTypeRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.RelationshipTypeRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.RemediationRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.RemediationRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.RemediationRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ReportExecRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ReportExecRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ReportExecRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ReportRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ReportRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ReportRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.RoleRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.RoleRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.RoleRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.SAML2IdPEntityRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.SAML2SPClientAppRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.SAML2SPClientAppRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.SAML2SPClientAppRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.SAML2SPEntityRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.SRARouteRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.SecurityQuestionRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.SecurityQuestionRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.SecurityQuestionRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.UserRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.UserRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.UserRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.VirSchemaRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.VirSchemaRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.VirSchemaRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.WAConfigRepo; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jEntityFactory; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAPlainAttr; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGPlainAttr; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jTaskUtilsFactory; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jLAPlainAttr; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUPlainAttr; +import org.apache.syncope.core.persistence.neo4j.spring.DomainRoutingNeo4jClient; +import org.apache.syncope.core.persistence.neo4j.spring.DomainRoutingNeo4jTransactionManager; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.apache.syncope.core.persistence.neo4j.spring.PlainsAttrsConverter; +import org.apache.syncope.core.spring.security.SecurityProperties; +import org.neo4j.cypherdsl.core.renderer.Dialect; +import org.neo4j.driver.Driver; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Lazy; +import org.springframework.context.annotation.Primary; +import org.springframework.core.env.Environment; +import org.springframework.core.io.ResourceLoader; +import org.springframework.data.neo4j.config.Neo4jEntityScanner; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jOperations; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.data.neo4j.core.convert.Neo4jConversions; +import org.springframework.data.neo4j.core.convert.Neo4jPersistentPropertyToMapConverter; +import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager; +import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager; +import org.springframework.data.neo4j.repository.config.Neo4jRepositoryConfigurationExtension; +import org.springframework.data.neo4j.repository.support.SyncopeNeo4jRepositoryFactory; +import org.springframework.transaction.PlatformTransactionManager; + +@EnableConfigurationProperties(PersistenceProperties.class) +@Import(CommonPersistenceContext.class) +@Configuration(proxyBeanMethods = false) +public class PersistenceContext { + + @ConditionalOnMissingBean + @Bean + public org.neo4j.cypherdsl.core.renderer.Configuration cypherDslConfiguration() { + return org.neo4j.cypherdsl.core.renderer.Configuration.newConfig().withDialect(Dialect.NEO4J_5).build(); + } + + @ConditionalOnMissingBean + @Bean + public Neo4jConversions neo4jConversions() { + return new Neo4jConversions(); + } + + @ConditionalOnMissingBean + @Bean + public Neo4jMappingContext neo4jMappingContext(final Neo4jConversions neo4jConversions) + throws ClassNotFoundException { + + Neo4jMappingContext mappingContext = new Neo4jMappingContext(neo4jConversions); + mappingContext.setInitialEntitySet( + Neo4jEntityScanner.get().scan("org.apache.syncope.core.persistence.neo4j.entity")); + return mappingContext; + } + + @ConditionalOnMissingBean + @Bean + public Neo4jBookmarkManager bookmarkManager() { + return Neo4jBookmarkManager.create(); + } + + @Primary + @Bean(Neo4jRepositoryConfigurationExtension.DEFAULT_NEO4J_CLIENT_BEAN_NAME) + public Neo4jClient neo4jClient( + @Qualifier("MasterNeo4jClient") + final Neo4jClient masterNeo4jClient) { + + DomainRoutingNeo4jClient neo4jClient = new DomainRoutingNeo4jClient(); + neo4jClient.add(SyncopeConstants.MASTER_DOMAIN, masterNeo4jClient); + return neo4jClient; + } + + @Bean(Neo4jRepositoryConfigurationExtension.DEFAULT_NEO4J_TEMPLATE_BEAN_NAME) + public Neo4jOperations neo4jTemplate( + @Qualifier(Neo4jRepositoryConfigurationExtension.DEFAULT_NEO4J_CLIENT_BEAN_NAME) + final Neo4jClient neo4jClient, + final Neo4jMappingContext mappingContext) { + + return new Neo4jTemplate(neo4jClient, mappingContext); + } + + @Bean(Neo4jRepositoryConfigurationExtension.DEFAULT_TRANSACTION_MANAGER_BEAN_NAME) + public PlatformTransactionManager transactionManager( + @Qualifier("MasterNeo4jTransactionManager") + final Neo4jTransactionManager masterNeo4jTransactionManager) { + + DomainRoutingNeo4jTransactionManager transactionManager = new DomainRoutingNeo4jTransactionManager(); + transactionManager.add(SyncopeConstants.MASTER_DOMAIN, masterNeo4jTransactionManager); + return transactionManager; + } + + @Bean(name = "uPlainAttrsConverter") + public Neo4jPersistentPropertyToMapConverter> uPlainAttrsConverter() { + return new PlainsAttrsConverter<>(Neo4jUPlainAttr.class); + } + + @Bean(name = "laPlainAttrsConverter") + public Neo4jPersistentPropertyToMapConverter> laPlainAttrsConverter() { + return new PlainsAttrsConverter<>(Neo4jLAPlainAttr.class); + } + + @Bean(name = "gPlainAttrsConverter") + public Neo4jPersistentPropertyToMapConverter> gPlainAttrsConverter() { + return new PlainsAttrsConverter<>(Neo4jGPlainAttr.class); + } + + @Bean(name = "aPlainAttrsConverter") + public Neo4jPersistentPropertyToMapConverter> aPlainAttrsConverter() { + return new PlainsAttrsConverter<>(Neo4jAPlainAttr.class); + } + + @ConditionalOnMissingBean + @Bean + public DomainHolder domainHolder( + @Qualifier("MasterDriver") + final Driver driver) { + + Neo4jDomainHolder domainHolder = new Neo4jDomainHolder(); + domainHolder.getDomains().put(SyncopeConstants.MASTER_DOMAIN, driver); + return domainHolder; + } + + @ConditionalOnMissingBean + @Bean + public NodeValidator nodeValidator(final Validator validator) { + return new NodeValidator(validator); + } + + @ConditionalOnMissingBean + @Bean + public XMLContentLoader xmlContentLoader( + final DomainHolder domainHolder, + final Neo4jMappingContext mappingContext, + final PersistenceProperties persistenceProperties, + final ResourceLoader resourceLoader, + final Environment env) { + + return new XMLContentLoader( + domainHolder, + mappingContext, + resourceLoader.getResource(persistenceProperties.getIndexesXML()), + env); + } + + @ConditionalOnMissingBean + @Bean + public XMLContentExporter xmlContentExporter( + final DomainHolder domainHolder, + final Neo4jMappingContext mappingContext) { + + return new XMLContentExporter(domainHolder, mappingContext); + } + + @ConditionalOnMissingBean + @Bean + public DomainRegistry domainRegistry(final ConfigurableApplicationContext ctx) { + return new Neo4jDomainRegistry(ctx); + } + + @ConditionalOnMissingBean + @Bean + public RuntimeDomainLoader runtimeDomainLoader( + final DomainHolder domainHolder, + final DomainRegistry domainRegistry, + final ConfigurableApplicationContext ctx) { + + return new RuntimeDomainLoader(domainHolder, domainRegistry, ctx); + } + + @ConditionalOnMissingBean + @Bean + public StartupDomainLoader startupDomainLoader( + final PersistenceProperties persistenceProperties, + final ResourceLoader resourceLoader, + final DomainOps domainOps, + final DomainHolder domainHolder, + final DomainRegistry domainRegistry) { + + return new StartupDomainLoader(domainOps, domainHolder, persistenceProperties, resourceLoader, domainRegistry); + } + + @ConditionalOnMissingBean + @Bean + public EntityFactory entityFactory() { + return new Neo4jEntityFactory(); + } + + @ConditionalOnMissingBean + @Bean + public TaskUtilsFactory taskUtilsFactory() { + return new Neo4jTaskUtilsFactory(); + } + + @ConditionalOnMissingBean + @Bean + public SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory( + final Neo4jOperations neo4jOperations, + final Neo4jMappingContext mappingContext) { + + return new SyncopeNeo4jRepositoryFactory(neo4jOperations, mappingContext); + } + + @ConditionalOnMissingBean + @Bean + public AccessTokenDAO accessTokenDAO(final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory) { + return neo4jRepositoryFactory.getRepository(AccessTokenRepo.class); + } + + @ConditionalOnMissingBean + @Bean + public AnyMatchDAO anyMatchDAO( + final @Lazy UserDAO userDAO, + final @Lazy GroupDAO groupDAO, + final @Lazy AnyObjectDAO anyObjectDAO, + final RealmDAO realmDAO, + final PlainSchemaDAO plainSchemaDAO, + final AnyUtilsFactory anyUtilsFactory, + final PlainAttrValidationManager validator, + final EntityFactory entityFactory) { + + return new Neo4jAnyMatchDAO( + userDAO, + groupDAO, + anyObjectDAO, + realmDAO, + plainSchemaDAO, + anyUtilsFactory, + validator, + entityFactory); + } + + @ConditionalOnMissingBean + @Bean + public AnyObjectRepoExt anyObjectRepoExt( + final AnyUtilsFactory anyUtilsFactory, + final @Lazy PlainSchemaDAO plainSchemaDAO, + final @Lazy DerSchemaDAO derSchemaDAO, + final @Lazy DynRealmDAO dynRealmDAO, + final @Lazy UserDAO userDAO, + final @Lazy GroupDAO groupDAO, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new AnyObjectRepoExtImpl( + anyUtilsFactory, + plainSchemaDAO, + derSchemaDAO, + dynRealmDAO, + userDAO, + groupDAO, + neo4jTemplate, + neo4jClient, + nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public AnyObjectDAO anyObjectDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final AnyObjectRepoExt anyObjectRepoExt) { + + return neo4jRepositoryFactory.getRepository(AnyObjectRepo.class, anyObjectRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public AnySearchDAO anySearchDAO( + final RealmDAO realmDAO, + final @Lazy DynRealmDAO dynRealmDAO, + final @Lazy UserDAO userDAO, + final @Lazy GroupDAO groupDAO, + final @Lazy AnyObjectDAO anyObjectDAO, + final PlainSchemaDAO schemaDAO, + final EntityFactory entityFactory, + final AnyUtilsFactory anyUtilsFactory, + final PlainAttrValidationManager validator, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient) { + + return new Neo4jAnySearchDAO( + realmDAO, + dynRealmDAO, + userDAO, + groupDAO, + anyObjectDAO, + schemaDAO, + entityFactory, + anyUtilsFactory, + validator, + neo4jTemplate, + neo4jClient); + } + + @ConditionalOnMissingBean + @Bean + public AnyTypeClassRepoExt anyTypeClassRepoExt( + final AnyTypeDAO anyTypeDAO, + final PlainSchemaDAO plainSchemaDAO, + final DerSchemaDAO derSchemaDAO, + final VirSchemaDAO virSchemaDAO, + final @Lazy GroupDAO groupDAO, + final ExternalResourceDAO resourceDAO, + final Neo4jTemplate neo4jTemplate, + final NodeValidator nodeValidator) { + + return new AnyTypeClassRepoExtImpl( + anyTypeDAO, + plainSchemaDAO, + derSchemaDAO, + virSchemaDAO, + groupDAO, + resourceDAO, + neo4jTemplate, + nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public AnyTypeClassDAO anyTypeClassDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final AnyTypeClassRepoExt anyTypeClassRepoExt) { + + return neo4jRepositoryFactory.getRepository(AnyTypeClassRepo.class, anyTypeClassRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public AnyTypeRepoExt anyTypeRepoExt( + final RemediationDAO remediationDAO, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient) { + + return new AnyTypeRepoExtImpl(remediationDAO, neo4jTemplate, neo4jClient); + } + + @ConditionalOnMissingBean + @Bean + public AnyTypeDAO anyTypeDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final AnyTypeRepoExt anyTypeRepoExt) { + + return neo4jRepositoryFactory.getRepository(AnyTypeRepo.class, anyTypeRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public ApplicationRepoExt applicationRepoExt( + final RoleDAO roleDAO, + final @Lazy UserDAO userDAO, + final Neo4jTemplate neo4jTemplate, + final NodeValidator nodeValidator) { + + return new ApplicationRepoExtImpl(roleDAO, userDAO, neo4jTemplate, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public ApplicationDAO applicationDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final ApplicationRepoExt applicationRepoExt) { + + return neo4jRepositoryFactory.getRepository(ApplicationRepo.class, applicationRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public AuditConfDAO auditConfDAO(final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory) { + return neo4jRepositoryFactory.getRepository(AuditConfRepo.class); + } + + @ConditionalOnMissingBean + @Bean + public AuditEntryDAO auditEntryDAO( + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient) { + + return new Neo4jAuditEntryDAO(neo4jTemplate, neo4jClient); + } + + @ConditionalOnMissingBean + @Bean + public AttrRepoRepoExt attrRepoRepoExt( + final Neo4jTemplate neo4jTemplate, + final NodeValidator nodeValidator) { + + return new AttrRepoRepoExtImpl(neo4jTemplate, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public AttrRepoDAO attrRepoDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final AttrRepoRepoExt attrRepoRepoExt) { + + return neo4jRepositoryFactory.getRepository(AttrRepoRepo.class, attrRepoRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public AuthModuleRepoExt authModuleRepoExt( + final PolicyDAO policyDAO, + final Neo4jTemplate neo4jTemplate, + final NodeValidator nodeValidator) { + + return new AuthModuleRepoExtImpl(policyDAO, neo4jTemplate, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public AuthModuleDAO authModuleDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final AuthModuleRepoExt authModuleRepoExt) { + + return neo4jRepositoryFactory.getRepository(AuthModuleRepo.class, authModuleRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public AuthProfileDAO authProfileDAO(final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory) { + return neo4jRepositoryFactory.getRepository(AuthProfileRepo.class); + } + + @ConditionalOnMissingBean + @Bean + public BatchDAO batchDAO( + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new Neo4jBatchDAO(neo4jTemplate, neo4jClient, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public CASSPClientAppRepoExt casSPClientAppRepoExt( + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient) { + + return new CASSPClientAppRepoExtImpl(neo4jTemplate, neo4jClient); + } + + @ConditionalOnMissingBean + @Bean + public CASSPClientAppDAO casSPClientAppDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final CASSPClientAppRepoExt casSPClientAppRepoExt) { + + return neo4jRepositoryFactory.getRepository(CASSPClientAppRepo.class, casSPClientAppRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public ConnInstanceRepoExt connInstanceRepoExt( + final @Lazy ExternalResourceDAO resourceDAO, + final Neo4jTemplate neo4jTemplate, + final NodeValidator nodeValidator) { + + return new ConnInstanceRepoExtImpl(resourceDAO, neo4jTemplate, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public ConnInstanceDAO connInstanceDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final ConnInstanceRepoExt connInstanceRepoExt) { + + return neo4jRepositoryFactory.getRepository(ConnInstanceRepo.class, connInstanceRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public DelegationRepoExt delegationRepoExt( + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient) { + + return new DelegationRepoExtImpl(neo4jTemplate, neo4jClient); + } + + @ConditionalOnMissingBean + @Bean + public DelegationDAO delegationDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final DelegationRepoExt delegationRepoExt) { + + return neo4jRepositoryFactory.getRepository(DelegationRepo.class, delegationRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public DerSchemaRepoExt derSchemaRepoExt( + final @Lazy ExternalResourceDAO resourceDAO, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new DerSchemaRepoExtImpl(resourceDAO, neo4jTemplate, neo4jClient, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public DerSchemaDAO derSchemaDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final DerSchemaRepoExt derSchemaRepoExt) { + + return neo4jRepositoryFactory.getRepository(DerSchemaRepo.class, derSchemaRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public DynRealmRepoExt dynRealmRepoExt( + final ApplicationEventPublisher publisher, + final @Lazy UserDAO userDAO, + final @Lazy GroupDAO groupDAO, + final @Lazy AnyObjectDAO anyObjectDAO, + final AnySearchDAO anySearchDAO, + final AnyMatchDAO anyMatchDAO, + final SearchCondVisitor searchCondVisitor, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new DynRealmRepoExtImpl( + publisher, + userDAO, + groupDAO, + anyObjectDAO, + anySearchDAO, + anyMatchDAO, + searchCondVisitor, + neo4jTemplate, + neo4jClient, + nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public DynRealmDAO dynRealmDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final DynRealmRepoExt dynRealmRepoExt) { + + return neo4jRepositoryFactory.getRepository(DynRealmRepo.class, dynRealmRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public EntityCacheDAO entityCacheDAO() { + return new Neo4jEntityCacheDAO(); + } + + @ConditionalOnMissingBean + @Bean + public FIQLQueryRepoExt fiqlQueryRepoExt( + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient) { + + return new FIQLQueryRepoExtImpl(neo4jTemplate, neo4jClient); + } + + @ConditionalOnMissingBean + @Bean + public FIQLQueryDAO fiqlQueryDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final FIQLQueryRepoExt fiqlQueryRepoExt) { + + return neo4jRepositoryFactory.getRepository(FIQLQueryRepo.class, fiqlQueryRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public GroupRepoExt groupRepoExt( + final ApplicationEventPublisher publisher, + final AnyUtilsFactory anyUtilsFactory, + final @Lazy PlainSchemaDAO plainSchemaDAO, + final @Lazy DerSchemaDAO derSchemaDAO, + final @Lazy DynRealmDAO dynRealmDAO, + final AnyMatchDAO anyMatchDAO, + final @Lazy UserDAO userDAO, + final @Lazy AnyObjectDAO anyObjectDAO, + final AnySearchDAO anySearchDAO, + final SearchCondVisitor searchCondVisitor, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new GroupRepoExtImpl( + anyUtilsFactory, + publisher, + plainSchemaDAO, + derSchemaDAO, + dynRealmDAO, + anyMatchDAO, + userDAO, + anyObjectDAO, + anySearchDAO, + searchCondVisitor, + neo4jTemplate, + neo4jClient, + nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public GroupDAO groupDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final GroupRepoExt groupRepoExt) { + + return neo4jRepositoryFactory.getRepository(GroupRepo.class, groupRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public ImplementationRepoExt implementationRepoExt( + final Neo4jTemplate neo4jTemplate, + final NodeValidator nodeValidator) { + + return new ImplementationRepoExtImpl(neo4jTemplate, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public ImplementationDAO implementationDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final ImplementationRepoExt implementationRepoExt) { + + return neo4jRepositoryFactory.getRepository(ImplementationRepo.class, implementationRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public JobStatusDAO jobStatusDAO(final Neo4jTemplate neo4jTemplate, final NodeValidator nodeValidator) { + return new Neo4jJobStatusDAO(neo4jTemplate, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public MailTemplateDAO mailTemplateDAO(final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory) { + return neo4jRepositoryFactory.getRepository(MailTemplateRepo.class); + } + + @ConditionalOnMissingBean + @Bean + public NotificationRepoExt notificationRepoExt( + final TaskDAO taskDAO, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new NotificationRepoExtImpl(taskDAO, neo4jTemplate, neo4jClient, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public NotificationDAO notificationDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final NotificationRepoExt notificationRepoExt) { + + return neo4jRepositoryFactory.getRepository(NotificationRepo.class, notificationRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public OIDCJWKSDAO oidcJWKSDAO(final Neo4jTemplate neo4jTemplate, final NodeValidator nodeValidator) { + return new Neo4jOIDCJWKSDAO(neo4jTemplate, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public OIDCRPClientAppRepoExt oidcRPClientAppRepoExt( + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new OIDCRPClientAppRepoExtImpl(neo4jTemplate, neo4jClient, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public OIDCRPClientAppDAO oidcRPClientAppDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final OIDCRPClientAppRepoExt oidcRPClientAppRepoExt) { + + return neo4jRepositoryFactory.getRepository(OIDCRPClientAppRepo.class, oidcRPClientAppRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public PersistenceInfoDAO persistenceInfoDAO(final Driver driver) { + return new Neo4jPersistenceInfoDAO(driver); + } + + @ConditionalOnMissingBean + @Bean + public PlainSchemaRepoExt plainSchemaRepoExt( + final AnyUtilsFactory anyUtilsFactory, + final @Lazy ExternalResourceDAO resourceDAO, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new PlainSchemaRepoExtImpl(anyUtilsFactory, resourceDAO, neo4jTemplate, neo4jClient, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public PlainSchemaDAO plainSchemaDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final PlainSchemaRepoExt plainSchemaRepoExt) { + + return neo4jRepositoryFactory.getRepository(PlainSchemaRepo.class, plainSchemaRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public PolicyDAO policyDAO( + final @Lazy RealmDAO realmDAO, + final @Lazy ExternalResourceDAO resourceDAO, + final @Lazy CASSPClientAppDAO casSPClientAppDAO, + final @Lazy OIDCRPClientAppDAO oidcRPClientAppDAO, + final @Lazy SAML2SPClientAppDAO saml2SPClientAppDAO, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new Neo4jPolicyDAO( + realmDAO, + resourceDAO, + casSPClientAppDAO, + oidcRPClientAppDAO, + saml2SPClientAppDAO, + neo4jTemplate, + neo4jClient, + nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public RelationshipTypeRepoExt relationshipTypeRepoExt( + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient) { + + return new RelationshipTypeRepoExtImpl(neo4jTemplate, neo4jClient); + } + + @ConditionalOnMissingBean + @Bean + public RelationshipTypeDAO relationshipTypeDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final RelationshipTypeRepoExt relationshipTypeRepoExt) { + + return neo4jRepositoryFactory.getRepository(RelationshipTypeRepo.class, relationshipTypeRepoExt); + } + + @ConditionalOnMissingBean(name = { "realmDAO", "delegateRealmDAO" }) + @Bean(name = { "realmDAO", "delegateRealmDAO" }) + public RealmDAO realmDAO( + final @Lazy RoleDAO roleDAO, + final ApplicationEventPublisher publisher, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new Neo4jRealmDAO(roleDAO, publisher, neo4jTemplate, neo4jClient, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public RemediationRepoExt remediationRepoExt( + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new RemediationRepoExtImpl(neo4jTemplate, neo4jClient, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public RemediationDAO remediationDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final RemediationRepoExt remediationRepoExt) { + + return neo4jRepositoryFactory.getRepository(RemediationRepo.class, remediationRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public ReportRepoExt reportRepoExt(final Neo4jTemplate neo4jTemplate) { + return new ReportRepoExtImpl(neo4jTemplate); + } + + @ConditionalOnMissingBean + @Bean + public ReportDAO reportDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final ReportRepoExt reportRepoExt) { + + return neo4jRepositoryFactory.getRepository(ReportRepo.class, reportRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public ReportExecRepoExt reportExecRepoExt( + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new ReportExecRepoExtImpl(neo4jTemplate, neo4jClient, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public ReportExecDAO reportExecDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final ReportExecRepoExt reportExecRepoExt) { + + return neo4jRepositoryFactory.getRepository(ReportExecRepo.class, reportExecRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public ExternalResourceRepoExt resourceRepoExt( + final TaskDAO taskDAO, + final AnyObjectDAO anyObjectDAO, + final UserDAO userDAO, + final GroupDAO groupDAO, + final PolicyDAO policyDAO, + final VirSchemaDAO virSchemaDAO, + final RealmDAO realmDAO, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new ExternalResourceRepoExtImpl( + taskDAO, + anyObjectDAO, + userDAO, + groupDAO, + policyDAO, + virSchemaDAO, + realmDAO, + neo4jTemplate, + neo4jClient, + nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public ExternalResourceDAO resourceDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final ExternalResourceRepoExt resourceRepoExt) { + + return neo4jRepositoryFactory.getRepository(ExternalResourceRepo.class, resourceRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public RoleRepoExt roleRepoExt( + final ApplicationEventPublisher publisher, + final @Lazy AnyMatchDAO anyMatchDAO, + final @Lazy AnySearchDAO anySearchDAO, + final DelegationDAO delegationDAO, + final SearchCondVisitor searchCondVisitor, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new RoleRepoExtImpl( + publisher, + anyMatchDAO, + anySearchDAO, + delegationDAO, + searchCondVisitor, + neo4jTemplate, + neo4jClient, + nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public RoleDAO roleDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final RoleRepoExt roleRepoExt) { + + return neo4jRepositoryFactory.getRepository(RoleRepo.class, roleRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public SAML2IdPEntityDAO saml2IdPEntityDAO(final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory) { + return neo4jRepositoryFactory.getRepository(SAML2IdPEntityRepo.class); + } + + @ConditionalOnMissingBean + @Bean + public SAML2SPClientAppRepoExt saml2SPClientAppRepoExt( + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new SAML2SPClientAppRepoExtImpl(neo4jTemplate, neo4jClient, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public SAML2SPClientAppDAO saml2SPClientAppDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final SAML2SPClientAppRepoExt saml2SPClientAppRepoExt) { + + return neo4jRepositoryFactory.getRepository(SAML2SPClientAppRepo.class, saml2SPClientAppRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public SAML2SPEntityDAO saml2SPEntityDAO(final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory) { + return neo4jRepositoryFactory.getRepository(SAML2SPEntityRepo.class); + } + + @ConditionalOnMissingBean + @Bean + public SecurityQuestionRepoExt securityQuestionRepoExt( + final UserDAO userDAO, + final Neo4jTemplate neo4jTemplate) { + + return new SecurityQuestionRepoExtImpl(userDAO, neo4jTemplate); + } + + @ConditionalOnMissingBean + @Bean + public SecurityQuestionDAO securityQuestionDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final SecurityQuestionRepoExt securityQuestionRepoExt) { + + return neo4jRepositoryFactory.getRepository(SecurityQuestionRepo.class, securityQuestionRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public SRARouteDAO sraRouteDAO(final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory) { + return neo4jRepositoryFactory.getRepository(SRARouteRepo.class); + } + + @ConditionalOnMissingBean + @Bean + public TaskDAO taskDAO( + final RealmDAO realmDAO, + final RemediationDAO remediationDAO, + final TaskUtilsFactory taskUtilsFactory, + final SecurityProperties securityProperties, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new Neo4jTaskDAO( + realmDAO, + remediationDAO, + taskUtilsFactory, + securityProperties, + neo4jTemplate, + neo4jClient, + nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public TaskExecDAO taskExecDAO( + final TaskDAO taskDAO, + final TaskUtilsFactory taskUtilsFactory, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new Neo4jTaskExecDAO(taskDAO, taskUtilsFactory, neo4jTemplate, neo4jClient, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public UserRepoExt userRepoExt( + final SecurityProperties securityProperties, + final AnyUtilsFactory anyUtilsFactory, + final @Lazy PlainSchemaDAO plainSchemaDAO, + final @Lazy DerSchemaDAO derSchemaDAO, + final @Lazy DynRealmDAO dynRealmDAO, + final RoleDAO roleDAO, + final AccessTokenDAO accessTokenDAO, + final @Lazy GroupDAO groupDAO, + final DelegationDAO delegationDAO, + final FIQLQueryDAO fiqlQueryDAO, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new UserRepoExtImpl( + anyUtilsFactory, + plainSchemaDAO, + derSchemaDAO, + dynRealmDAO, + roleDAO, + accessTokenDAO, + groupDAO, + delegationDAO, + fiqlQueryDAO, + securityProperties, + neo4jTemplate, + neo4jClient, + nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public UserDAO userDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final UserRepoExt userRepoExt) { + + return neo4jRepositoryFactory.getRepository(UserRepo.class, userRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public VirSchemaRepoExt virSchemaRepoExt( + final @Lazy ExternalResourceDAO resourceDAO, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + return new VirSchemaRepoExtImpl(resourceDAO, neo4jTemplate, neo4jClient, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public VirSchemaDAO virSchemaDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final VirSchemaRepoExt virSchemaRepoExt) { + + return neo4jRepositoryFactory.getRepository(VirSchemaRepo.class, virSchemaRepoExt); + } + + @ConditionalOnMissingBean + @Bean + public WAConfigDAO waConfigDAO(final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory) { + return neo4jRepositoryFactory.getRepository(WAConfigRepo.class); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceProperties.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceProperties.java new file mode 100644 index 0000000000..c30292ce16 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceProperties.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j; + +import org.apache.syncope.core.persistence.common.AbstractPersistenceProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("persistence") +public class PersistenceProperties extends AbstractPersistenceProperties { +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/StartupDomainLoader.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/StartupDomainLoader.java new file mode 100644 index 0000000000..6cdb749906 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/StartupDomainLoader.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.persistence.neo4j; + +import java.io.IOException; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import org.apache.cxf.helpers.IOUtils; +import org.apache.syncope.common.keymaster.client.api.DomainOps; +import org.apache.syncope.common.keymaster.client.api.model.Domain; +import org.apache.syncope.common.lib.SyncopeConstants; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.Ordered; +import org.springframework.core.io.ResourceLoader; + +public class StartupDomainLoader implements SyncopeCoreLoader { + + protected static final Logger LOG = LoggerFactory.getLogger(StartupDomainLoader.class); + + protected final DomainOps domainOps; + + protected final DomainHolder domainHolder; + + protected final PersistenceProperties persistenceProperties; + + protected final ResourceLoader resourceLoader; + + protected final DomainRegistry domainRegistry; + + public StartupDomainLoader( + final DomainOps domainOps, + final DomainHolder domainHolder, + final PersistenceProperties persistenceProperties, + final ResourceLoader resourceLoader, + final DomainRegistry domainRegistry) { + + this.domainOps = domainOps; + this.domainHolder = domainHolder; + this.persistenceProperties = persistenceProperties; + this.resourceLoader = resourceLoader; + this.domainRegistry = domainRegistry; + } + + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; + } + + @Override + public void load() { + Map keymasterDomains = domainOps.list().stream(). + collect(Collectors.toMap(Domain::getKey, Function.identity())); + + persistenceProperties.getDomain().stream(). + filter(d -> !SyncopeConstants.MASTER_DOMAIN.equals(d.getKey()) + && !domainHolder.getDomains().containsKey(d.getKey())).forEach(domainProps -> { + + if (keymasterDomains.containsKey(domainProps.getKey())) { + LOG.info("Domain {} initialization", domainProps.getKey()); + + domainRegistry.register(keymasterDomains.get(domainProps.getKey())); + + LOG.info("Domain {} successfully inited", domainProps.getKey()); + } else { + Domain.Builder builder = new Domain.Builder(domainProps.getKey()). + adminPassword(domainProps.getAdminPassword()). + adminCipherAlgorithm(domainProps.getAdminCipherAlgorithm()). + jdbcURL(domainProps.getUri().toASCIIString()). + dbUsername(domainProps.getUsername()). + dbPassword(domainProps.getPassword()). + poolMaxActive(domainProps.getMaxConnectionPoolSize()); + + try { + builder.content(IOUtils.toString( + resourceLoader.getResource(domainProps.getContent()).getInputStream())); + } catch (IOException e) { + LOG.error("While loading {}", domainProps.getContent(), e); + } + + try { + builder.keymasterConfParams(IOUtils.toString( + resourceLoader.getResource(domainProps.getKeymasterConfParams()).getInputStream())); + } catch (IOException e) { + LOG.error("While loading {}", domainProps.getKeymasterConfParams(), e); + } + + domainOps.create(builder.build()); + } + }); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/ContentLoaderHandler.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/ContentLoaderHandler.java new file mode 100644 index 0000000000..a12ea91b0c --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/ContentLoaderHandler.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.core.persistence.neo4j.content; + +import jakarta.xml.bind.DatatypeConverter; +import java.time.OffsetDateTime; +import java.time.format.DateTimeParseException; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import org.apache.commons.text.StringEscapeUtils; +import org.apache.syncope.core.persistence.api.utils.FormatUtils; +import org.apache.syncope.core.persistence.common.content.AbstractContentLoaderHandler; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDerSchema; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jPlainSchema; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jSchema; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jVirSchema; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccessPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccountPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAttrReleasePolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAuthPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPasswordPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPropagationPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPullPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPushPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jTicketExpirationPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jMacroTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jProvisioningTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jPullTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jPushTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jSchedTask; +import org.neo4j.driver.Driver; +import org.neo4j.driver.Session; +import org.springframework.core.env.Environment; +import org.springframework.data.neo4j.core.mapping.GraphPropertyDescription; +import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.core.mapping.NodeDescription; +import org.xml.sax.Attributes; + +/** + * SAX handler for generating CREATE statements out of given XML file. + */ +public class ContentLoaderHandler extends AbstractContentLoaderHandler { + + protected static record Node(String id, Map props) { + + } + + protected static record Relationship(String leftId, String rightId, String type) { + + } + + protected static record Query(String statement, Map props) { + + } + + protected static String nodelabels(final String primaryLabel) { + switch (primaryLabel) { + case Neo4jPlainSchema.NODE -> { + return Neo4jPlainSchema.NODE + ":" + Neo4jSchema.NODE; + } + case Neo4jDerSchema.NODE -> { + return Neo4jDerSchema.NODE + ":" + Neo4jSchema.NODE; + } + case Neo4jVirSchema.NODE -> { + return Neo4jVirSchema.NODE + ":" + Neo4jSchema.NODE; + } + + case Neo4jAccessPolicy.NODE -> { + return Neo4jAccessPolicy.NODE + ":" + Neo4jPolicy.NODE; + } + case Neo4jAccountPolicy.NODE -> { + return Neo4jAccountPolicy.NODE + ":" + Neo4jPolicy.NODE; + } + case Neo4jAttrReleasePolicy.NODE -> { + return Neo4jAttrReleasePolicy.NODE + ":" + Neo4jPolicy.NODE; + } + case Neo4jAuthPolicy.NODE -> { + return Neo4jAuthPolicy.NODE + ":" + Neo4jPolicy.NODE; + } + case Neo4jPasswordPolicy.NODE -> { + return Neo4jPasswordPolicy.NODE + ":" + Neo4jPolicy.NODE; + } + case Neo4jPropagationPolicy.NODE -> { + return Neo4jPropagationPolicy.NODE + ":" + Neo4jPolicy.NODE; + } + case Neo4jPushPolicy.NODE -> { + return Neo4jPushPolicy.NODE + ":" + Neo4jPolicy.NODE; + } + case Neo4jPullPolicy.NODE -> { + return Neo4jPullPolicy.NODE + ":" + Neo4jPolicy.NODE; + } + case Neo4jTicketExpirationPolicy.NODE -> { + return Neo4jTicketExpirationPolicy.NODE + ":" + Neo4jPolicy.NODE; + } + + case Neo4jPushTask.NODE -> { + return Neo4jPushTask.NODE + ":" + Neo4jProvisioningTask.NODE + ":" + Neo4jSchedTask.NODE; + } + case Neo4jPullTask.NODE -> { + return Neo4jPullTask.NODE + ":" + Neo4jProvisioningTask.NODE + ":" + Neo4jSchedTask.NODE; + } + case Neo4jMacroTask.NODE -> { + return Neo4jMacroTask.NODE + ":" + Neo4jSchedTask.NODE; + } + + default -> { + return primaryLabel; + } + } + } + + protected static String escape(final String k) { + return k.startsWith("plainAttrs.") ? k.replace('.', '_') : k; + } + + protected final Driver driver; + + protected final Neo4jMappingContext mappingContext; + + public ContentLoaderHandler( + final Driver driver, + final Neo4jMappingContext mappingContext, + final String rootElement, + final boolean continueOnError, + final Environment env) { + + super(rootElement, continueOnError, env); + this.driver = driver; + this.mappingContext = mappingContext; + } + + @Override + protected void fetch(final Attributes atts) { + try (Session session = driver.session()) { + String value = session.run(atts.getValue("query")).single().get(0).asString(); + String key = atts.getValue("key"); + fetches.put(key, value); + } catch (Exception e) { + LOG.error("While running '{}'", atts.getValue("query"), e); + } + } + + protected Optional parseNode(final NodeDescription nodeDesc, final Attributes atts) { + String id = null; + Map props = new HashMap<>(); + for (int i = 0; i < atts.getLength(); i++) { + String originalName = atts.getQName(i); + String originalValue = atts.getValue(i); + + if ("id".equalsIgnoreCase(originalName)) { + id = originalValue; + props.put("id", originalValue); + } else { + String name = nodeDesc.getGraphProperties().stream(). + filter(prop -> prop.getPropertyName().equalsIgnoreCase(originalName)). + map(GraphPropertyDescription::getPropertyName). + findFirst().orElseGet(() -> originalName.startsWith("plainAttrs.") ? originalName : null); + if (name == null) { + LOG.error("Property {} not matching for {}", originalName, nodeDesc.getPrimaryLabel()); + continue; + } + + Class type = nodeDesc.getGraphProperties().stream(). + filter(prop -> prop.getPropertyName().equalsIgnoreCase(name)). + findFirst().map(GraphPropertyDescription::getActualType). + orElseGet(() -> { + if (!name.startsWith("plainAttrs.")) { + LOG.warn("No type found for property {}#{}", nodeDesc.getPrimaryLabel(), name); + } + return String.class; + }); + + String value = paramSubstitutor.replace(atts.getValue(i)); + if (value == null) { + LOG.warn("Variable ${} could not be resolved", atts.getValue(i)); + value = atts.getValue(i); + } + value = StringEscapeUtils.unescapeXml(value); + + if (int.class.isAssignableFrom(type) || Integer.class.isAssignableFrom(type)) { + try { + props.put(name, Integer.valueOf(value)); + } catch (NumberFormatException e) { + LOG.error("Unparsable Integer '{}'", value); + } + } else if (long.class.isAssignableFrom(type) || Long.class.isAssignableFrom(type)) { + try { + props.put(name, Long.valueOf(value)); + } catch (NumberFormatException e) { + LOG.error("Unparsable Long '{}'", value); + } + } else if (float.class.isAssignableFrom(type) || Float.class.isAssignableFrom(type)) { + try { + props.put(name, Float.valueOf(value)); + } catch (NumberFormatException e) { + LOG.error("Unparsable Float '{}'", value); + } + } else if (double.class.isAssignableFrom(type) || Double.class.isAssignableFrom(type)) { + try { + props.put(name, Double.valueOf(value)); + } catch (NumberFormatException e) { + LOG.error("Unparsable Double '{}'", value); + } + } else if (Date.class.isAssignableFrom(type) || OffsetDateTime.class.isAssignableFrom(type)) { + try { + props.put(name, FormatUtils.parseDate(value)); + } catch (DateTimeParseException e) { + LOG.error("Unparsable Date '{}'", value); + } + } else if (boolean.class.isAssignableFrom(type) || Boolean.class.isAssignableFrom(type)) { + props.put(name, "1".equals(value) ? Boolean.TRUE : Boolean.FALSE); + } else if (byte[].class.isAssignableFrom(type)) { + try { + props.put(name, DatatypeConverter.parseHexBinary(value)); + } catch (IllegalArgumentException e) { + LOG.warn("Error decoding hex string to specify a blob parameter", e); + } + } + if (!props.containsKey(name)) { + props.put(name, value); + } + } + } + + return id == null ? Optional.empty() : Optional.of(new Node(id, props)); + } + + protected Optional parseRelationship( + final NodeDescription nodeDesc, final String rightNode, final Attributes atts) { + + String left = null; + String right = null; + String type = null; + for (int i = 0; i < atts.getLength(); i++) { + if ("left".equalsIgnoreCase(atts.getQName(i))) { + left = atts.getValue(i); + } else if ("right".equalsIgnoreCase(atts.getQName(i))) { + right = atts.getValue(i); + } else if ("type".equalsIgnoreCase(atts.getQName(i))) { + type = atts.getValue(i); + } + } + if (left == null || right == null) { + LOG.warn("Could not find left and/or right attribute in {}_{}", nodeDesc.getPrimaryLabel(), rightNode); + return Optional.empty(); + } + + String leftId = left; + String rightId = right; + String relType = type; + return nodeDesc.getRelationships().stream(). + filter(rel -> rightNode.equals(rel.getTarget().getPrimaryLabel()) + && (relType == null || relType.equals(rel.getType()))). + findFirst().map(rel -> new Relationship(leftId, rightId, rel.getType())); + } + + @Override + protected void create(final String qName, final Attributes atts) { + Optional query; + if (qName.contains("_")) { + String[] split = qName.split("_"); + query = parseRelationship(mappingContext.getNodeDescription(split[0]), split[1], atts). + map(rel -> new Query( + "MATCH (a:" + split[0] + " {id: '" + rel.leftId() + "'}), " + + "(b:" + split[1] + " {id: '" + rel.rightId() + "'}) " + + "CREATE (a)-[:" + rel.type() + "]->(b)", + Map.of())); + } else { + query = parseNode(mappingContext.getNodeDescription(qName), atts).map(node -> { + StringBuilder q = new StringBuilder("CREATE (n:").append(nodelabels(qName)).append(" {"); + q.append(node.props().entrySet().stream(). + map(e -> "`" + e.getKey() + "`" + ": $" + escape(e.getKey())). + collect(Collectors.joining(", "))); + q.append("})"); + return new Query(q.toString(), node.props().entrySet().stream(). + collect(Collectors.toMap(e -> escape(e.getKey()), Map.Entry::getValue))); + }); + } + + query.ifPresent(q -> { + LOG.debug("About to run: {}", q); + + try (Session session = driver.session()) { + session.run(q.statement(), q.props()); + } catch (Exception e) { + LOG.error("While processing {}", qName, e); + if (!continueOnError) { + throw e; + } + } + }); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/XMLContentExporter.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/XMLContentExporter.java new file mode 100644 index 0000000000..855588da08 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/XMLContentExporter.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.persistence.neo4j.content; + +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; +import java.util.stream.Collectors; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.sax.TransformerHandler; +import org.apache.syncope.common.lib.types.AuditLoggerName; +import org.apache.syncope.core.persistence.api.DomainHolder; +import org.apache.syncope.core.persistence.common.content.AbstractXMLContentExporter; +import org.apache.syncope.core.persistence.common.content.MultiParentNode; +import org.apache.syncope.core.persistence.common.content.MultiParentNodeOp; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jJobStatus; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jSchema; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jMacroTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jProvisioningTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jSchedTask; +import org.neo4j.driver.Driver; +import org.neo4j.driver.Record; +import org.neo4j.driver.Session; +import org.neo4j.driver.types.Node; +import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.core.mapping.Neo4jPersistentEntity; +import org.springframework.data.neo4j.core.mapping.RelationshipDescription; +import org.springframework.data.neo4j.core.schema.Relationship; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +public class XMLContentExporter extends AbstractXMLContentExporter { + + protected static final Set LABELS_TO_BE_EXCLUDED = Set.of( + Neo4jSchema.NODE, Neo4jPolicy.NODE, Neo4jProvisioningTask.NODE, + Neo4jJobStatus.NODE, AuditLoggerName.class.getSimpleName()); + + protected static final Comparator REALM_COMPARATOR = + Comparator.comparing(record -> record.get("n").asNode().get("fullPath").asString()); + + protected final DomainHolder domainHolder; + + protected final Neo4jMappingContext mappingContext; + + public XMLContentExporter( + final DomainHolder domainHolder, + final Neo4jMappingContext mappingContext) { + + this.domainHolder = domainHolder; + this.mappingContext = mappingContext; + } + + protected List> persistentEntities() { + Map> entities = mappingContext.getPersistentEntities().stream(). + filter(e -> !LABELS_TO_BE_EXCLUDED.contains(e.getPrimaryLabel()) + && !e.getPrimaryLabel().startsWith("Abstract") + && !e.getPrimaryLabel().contains("PlainAttr")). + collect(Collectors.toMap(Neo4jPersistentEntity::getPrimaryLabel, e -> e)); + + Set> roots = new HashSet<>(); + Map> exploited = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + + entities.forEach((label, entity) -> { + MultiParentNode node = Optional.ofNullable(exploited.get(label)). + orElseGet(() -> { + MultiParentNode n = new MultiParentNode<>(label); + roots.add(n); + exploited.put(label, n); + return n; + }); + + Set refEntityLabels = new HashSet<>(); + entity.getRelationships().stream().filter(r -> r.getDirection() == Relationship.Direction.OUTGOING). + forEach(r -> refEntityLabels.add(r.getTarget().getPrimaryLabel())); + + refEntityLabels.stream(). + filter(refEntityLabel -> !label.equalsIgnoreCase(refEntityLabel)). + forEach(refEntityLabel -> { + + MultiParentNode pkNode = Optional.ofNullable(exploited.get(refEntityLabel)). + orElseGet(() -> { + MultiParentNode n = new MultiParentNode<>(refEntityLabel); + roots.add(n); + exploited.put(refEntityLabel, n); + return n; + }); + + pkNode.addChild(node); + + if (roots.contains(node)) { + roots.remove(node); + } + }); + }); + + List sortedEntityLabels = new ArrayList<>(entities.size()); + MultiParentNodeOp.traverseTree(roots, sortedEntityLabels); + + // remove from sortedEntityLabels any table possibly added during lookup + sortedEntityLabels.retainAll(entities.keySet()); + + LOG.debug("Entities after retainAll {}", sortedEntityLabels); + + Collections.reverse(sortedEntityLabels); + + return sortedEntityLabels.stream().map(entities::get).toList(); + } + + protected void exportNode( + final Neo4jPersistentEntity entity, + final Record record, + final Session session, + final TransformerHandler handler) throws SAXException { + + LOG.debug("Export entity {}", entity.getPrimaryLabel()); + + Node node = record.get("n").asNode(); + + AttributesImpl attrs = new AttributesImpl(); + node.asMap().forEach((key, value) -> attrs.addAttribute("", "", key, "CDATA", value.toString())); + + handler.startElement("", "", entity.getPrimaryLabel(), attrs); + handler.endElement("", "", entity.getPrimaryLabel()); + + for (org.neo4j.driver.types.Relationship rel : record.get("rels").asList().stream(). + map(org.neo4j.driver.types.Relationship.class::cast).toList()) { + + Optional relDesc = entity.getRelationships().stream(). + filter(r -> r.getType().equals(rel.type()) && r.getDirection() == Relationship.Direction.OUTGOING). + findFirst(); + if (relDesc.isPresent()) { + AttributesImpl rattrs = new AttributesImpl(); + rattrs.addAttribute("", "", "type", "CDATA", rel.type()); + rattrs.addAttribute("", "", "left", "CDATA", node.get("id").asString()); + + String rightId = session.run( + "MATCH (n:" + relDesc.get().getTarget().getPrimaryLabel() + ") " + + "WHERE elementId(n) = $endNodeElementId RETURN n.id", + Map.of("endNodeElementId", rel.endNodeElementId())). + single().get("n.id").asString(); + rattrs.addAttribute("", "", "right", "CDATA", rightId); + + String elementName = entity.getPrimaryLabel() + + "_" + + relDesc.get().getTarget().getPrimaryLabel(); + handler.startElement("", "", elementName, rattrs); + handler.endElement("", "", elementName); + } + } + } + + @Override + public void export( + final String domain, + final int threshold, + final OutputStream os) throws SAXException, TransformerConfigurationException { + + TransformerHandler handler = start(os); + + for (Neo4jPersistentEntity entity : persistentEntities()) { + try (Session session = domainHolder.getDomains().get(domain).session()) { + StringBuilder query = new StringBuilder("MATCH (n:" + entity.getPrimaryLabel() + ")-[r]-() "); + if (Neo4jSchedTask.NODE.equals(entity.getPrimaryLabel())) { + query.append("WHERE NOT n:").append(Neo4jMacroTask.NODE). + append(" AND NOT n:").append(Neo4jProvisioningTask.NODE).append(' '); + } + query.append("RETURN n, collect(r) AS rels ORDER BY n.id"); + + List records = session.run(query.toString()).list(); + if (Neo4jRealm.NODE.equals(entity.getPrimaryLabel())) { + records.sort(REALM_COMPARATOR); + } + for (Record record : records) { + exportNode(entity, record, session, handler); + } + } catch (Exception e) { + LOG.error("While exporting database content", e); + } + } + + end(handler); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/XMLContentLoader.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/XMLContentLoader.java new file mode 100644 index 0000000000..dab80f049f --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/XMLContentLoader.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.persistence.neo4j.content; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import org.apache.syncope.core.persistence.api.DomainHolder; +import org.apache.syncope.core.persistence.common.content.AbstractXMLContentLoader; +import org.apache.syncope.core.spring.ApplicationContextProvider; +import org.neo4j.driver.Driver; +import org.neo4j.driver.Session; +import org.springframework.core.env.Environment; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PropertiesLoaderUtils; +import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; + +public class XMLContentLoader extends AbstractXMLContentLoader { + + protected final DomainHolder domainHolder; + + protected final Neo4jMappingContext mappingContext; + + protected final Resource indexesXML; + + public XMLContentLoader( + final DomainHolder domainHolder, + final Neo4jMappingContext mappingContext, + final Resource indexesXML, + final Environment env) { + + super(env); + this.domainHolder = domainHolder; + this.mappingContext = mappingContext; + this.indexesXML = indexesXML; + } + + @Override + protected boolean existingData(final String domain) { + boolean existingData; + try (Session session = domainHolder.getDomains().get(domain).session()) { + existingData = session.run("MATCH (n:Realm) WITH COUNT(n) > 0 AS node_exists RETURN node_exists"). + single().get(0).asBoolean(); + } catch (Exception e) { + LOG.error("[{}] Could not access node Realm", domain, e); + existingData = true; + } + return existingData; + } + + @Override + protected void createViews(final String domain) throws IOException { + // nothing to do + } + + @Override + protected void createIndexes(final String domain) throws IOException { + LOG.debug("[{}] Creating indexes", domain); + + try (Session session = domainHolder.getDomains().get(domain).session()) { + Properties indexes = PropertiesLoaderUtils.loadProperties(indexesXML); + indexes.stringPropertyNames().stream().sorted().forEach(idx -> { + LOG.debug("[{}] Creating index {}", domain, indexes.get(idx).toString()); + try { + session.run(indexes.getProperty(idx)); + } catch (Exception e) { + LOG.error("[{}] Could not create index", domain, e); + } + }); + } + + LOG.debug("Indexes created"); + } + + @Override + protected void loadDefaultContent(final String domain, final String contentXML) throws Exception { + InputStream in = ApplicationContextProvider.getBeanFactory().getBean(contentXML, InputStream.class); + try (in) { + saxParser().parse(in, new ContentLoaderHandler( + domainHolder.getDomains().get(domain), mappingContext, ROOT_ELEMENT, true, env)); + LOG.debug("[{}] Default content successfully loaded", domain); + } + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/AbstractDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/AbstractDAO.java new file mode 100644 index 0000000000..d3a6dbfdb6 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/AbstractDAO.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.persistence.neo4j.dao; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import org.apache.syncope.core.persistence.api.entity.Entity; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractNode; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public abstract class AbstractDAO { + + protected final Neo4jTemplate neo4jTemplate; + + protected final Neo4jClient neo4jClient; + + public AbstractDAO(final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient) { + this.neo4jTemplate = neo4jTemplate; + this.neo4jClient = neo4jClient; + } + + protected List findByRelationship( + final String leftNode, + final String rightNode, + final String rightNodeKey, + final Class leftDomainType) { + + return toList(neo4jClient.query( + "MATCH (n:" + leftNode + ")-[]-(p:" + rightNode + " {id: $id}) " + + "RETURN n.id").bindAll(Map.of("id", rightNodeKey)).fetch().all(), + "n.id", + leftDomainType); + } + + protected List findByRelationship( + final String leftNode, + final String rightNode, + final String rightNodeKey, + final String relationshipType, + final Class leftDomainType) { + + return toList(neo4jClient.query( + "MATCH (n:" + leftNode + ")-[:" + relationshipType + "]-(p:" + rightNode + " {id: $id}) " + + "RETURN n.id").bindAll(Map.of("id", rightNodeKey)).fetch().all(), + "n.id", + leftDomainType); + } + + @SuppressWarnings("unchecked") + protected Function, Optional> toOptional( + final String property, final Class domainType) { + + return found -> neo4jTemplate.findById(found.get(property), domainType).map(n -> (E) n); + } + + @SuppressWarnings("unchecked") + protected List toList( + final Collection> result, + final String property, + final Class domainType) { + + return result.stream().map(found -> neo4jTemplate.findById(found.get(property), domainType)). + filter(Optional::isPresent).map(Optional::get).map(n -> (E) n).toList(); + } + + protected void cascadeDelete( + final String leftNode, + final String rightNode, + final String rightNodeKey, + final Class leftDomainType) { + + neo4jClient.query( + "MATCH (n:" + leftNode + ")-[]-(p:" + rightNode + " {id: $id}) " + + "RETURN n.id").bindAll(Map.of("id", rightNodeKey)).fetch().all(). + forEach(r -> neo4jTemplate.deleteById(r.get("n.id").toString(), leftDomainType)); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnyMatchDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnyMatchDAO.java new file mode 100644 index 0000000000..809c405582 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnyMatchDAO.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.core.persistence.neo4j.dao; + +import java.beans.PropertyDescriptor; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; +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.RealmDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.dao.search.AnyCond; +import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.common.dao.AbstractAnyMatchDAO; + +public class Neo4jAnyMatchDAO extends AbstractAnyMatchDAO { + + public Neo4jAnyMatchDAO( + final UserDAO userDAO, + final GroupDAO groupDAO, + final AnyObjectDAO anyObjectDAO, + final RealmDAO realmDAO, + final PlainSchemaDAO plainSchemaDAO, + final AnyUtilsFactory anyUtilsFactory, + final PlainAttrValidationManager validator, + final EntityFactory entityFactory) { + + super(userDAO, groupDAO, anyObjectDAO, realmDAO, plainSchemaDAO, anyUtilsFactory, validator, entityFactory); + } + + @Override + protected void relationshipFieldMatches( + final PropertyDescriptor pd, + final AnyCond cond, + final PlainSchema schema) { + + LOG.warn("Match condition not supported by Neo4j"); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnySearchDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnySearchDAO.java new file mode 100644 index 0000000000..70e855dd4a --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnySearchDAO.java @@ -0,0 +1,963 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao; + +import java.time.format.DateTimeFormatter; +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 java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.tuple.Triple; +import org.apache.commons.text.TextStringBuilder; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.rest.api.service.JAXRSService; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; +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.RealmDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.dao.search.AnyCond; +import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond; +import org.apache.syncope.core.persistence.api.dao.search.AttrCond; +import org.apache.syncope.core.persistence.api.dao.search.AuxClassCond; +import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond; +import org.apache.syncope.core.persistence.api.dao.search.MemberCond; +import org.apache.syncope.core.persistence.api.dao.search.MembershipCond; +import org.apache.syncope.core.persistence.api.dao.search.PrivilegeCond; +import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond; +import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond; +import org.apache.syncope.core.persistence.api.dao.search.ResourceCond; +import org.apache.syncope.core.persistence.api.dao.search.RoleCond; +import org.apache.syncope.core.persistence.api.dao.search.SearchCond; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.AnyUtils; +import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +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.utils.RealmUtils; +import org.apache.syncope.core.persistence.common.dao.AbstractAnySearchDAO; +import org.apache.syncope.core.persistence.neo4j.dao.repo.AnyRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.DynRealmRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.GroupRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.RoleRepoExt; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyType; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyTypeClass; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDynRealm; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jPrivilege; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRelationshipType; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRole; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAMembership; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jARelationship; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAnyObject; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUMembership; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jURelationship; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Order; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.data.util.Streamable; + +public class Neo4jAnySearchDAO extends AbstractAnySearchDAO { + + protected static record AdminRealmsFilter(String filter, Set dynRealmKeys, Set groupOwners) { + + } + + protected static record QueryInfo(TextStringBuilder query, Set fields, Set plainSchemas) { + + } + + protected static final String ALWAYS_FALSE_ASSERTION = "1=2"; + + protected static String setParameter(final Map parameters, final Object parameter) { + String name = "param" + parameters.size(); + parameters.put(name, parameter); + return name; + } + + protected static void appendPlainAttrCond( + final TextStringBuilder query, final PlainSchema schema, final String cond) { + + if (schema.isUniqueConstraint()) { + query.append(schema.getKey()).append('.').append(key(schema.getType())).append(cond); + } else { + query.append("any(k IN ").append(schema.getKey()). + append(" WHERE k").append('.').append(key(schema.getType())).append(cond). + append(")"); + } + } + + protected static String escapeIfString(final String value, final boolean isStr) { + return isStr + ? new StringBuilder().append('"').append(value).append('"').toString() + : value; + } + + protected final Neo4jTemplate neo4jTemplate; + + protected final Neo4jClient neo4jClient; + + public Neo4jAnySearchDAO( + final RealmDAO realmDAO, + final DynRealmDAO dynRealmDAO, + final UserDAO userDAO, + final GroupDAO groupDAO, + final AnyObjectDAO anyObjectDAO, + final PlainSchemaDAO plainSchemaDAO, + final EntityFactory entityFactory, + final AnyUtilsFactory anyUtilsFactory, + final PlainAttrValidationManager validator, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient) { + + super( + realmDAO, + dynRealmDAO, + userDAO, + groupDAO, + anyObjectDAO, + plainSchemaDAO, + entityFactory, + anyUtilsFactory, + validator); + this.neo4jTemplate = neo4jTemplate; + this.neo4jClient = neo4jClient; + } + + @Override + protected boolean isPatternMatch(final String clause) { + return clause.indexOf('*') != -1; + } + + protected String buildAdminRealmsFilter( + final Set realmKeys, + final Map parameters) { + + if (realmKeys.isEmpty()) { + return "(n)-[]-(:" + Neo4jRealm.NODE + ")"; + } + + return "(n)-[]-(r:" + Neo4jRealm.NODE + ") WHERE r.id IN $" + setParameter(parameters, realmKeys); + } + + protected AdminRealmsFilter getAdminRealmsFilter( + final Realm base, + final boolean recursive, + final Set adminRealms, + final Map parameters) { + + Set realmKeys = new HashSet<>(); + Set dynRealmKeys = new HashSet<>(); + Set groupOwners = new HashSet<>(); + + if (recursive) { + adminRealms.forEach(realmPath -> RealmUtils.parseGroupOwnerRealm(realmPath).ifPresentOrElse( + goRealm -> groupOwners.add(goRealm.getRight()), + () -> { + if (realmPath.startsWith("/")) { + Realm realm = realmDAO.findByFullPath(realmPath).orElseThrow(() -> { + SyncopeClientException noRealm = + SyncopeClientException.build(ClientExceptionType.InvalidRealm); + noRealm.getElements().add("Invalid realm specified: " + realmPath); + return noRealm; + }); + + realmKeys.addAll(realmDAO.findDescendants(realm.getFullPath(), base.getFullPath())); + } else { + dynRealmDAO.findById(realmPath).ifPresentOrElse( + dynRealm -> dynRealmKeys.add(dynRealm.getKey()), + () -> LOG.warn("Ignoring invalid dynamic realm {}", realmPath)); + } + })); + if (!dynRealmKeys.isEmpty()) { + realmKeys.clear(); + } + } else { + if (adminRealms.stream().anyMatch(r -> r.startsWith(base.getFullPath()))) { + realmKeys.add(base.getKey()); + } + } + + return new AdminRealmsFilter(buildAdminRealmsFilter(realmKeys, parameters), dynRealmKeys, groupOwners); + } + + protected String getQuery( + final AnyTypeCond cond, + final boolean not, + final Map parameters) { + + return "MATCH (n) " + + "WHERE " + (not ? "NOT " : "") + "(n)-[]-" + + "(:" + Neo4jAnyType.NODE + " {id: $" + setParameter(parameters, cond.getAnyTypeKey()) + "}) "; + } + + protected String getQuery( + final AuxClassCond cond, + final boolean not, + final Map parameters) { + + return "MATCH (n) " + + "WHERE " + (not ? "NOT " : "") + "(n)-[]-" + + "(:" + Neo4jAnyTypeClass.NODE + " {id: $" + setParameter(parameters, cond.getAuxClass()) + "}) "; + } + + protected String getQuery( + final AnyTypeKind kind, + final RelationshipTypeCond cond, + final boolean not, + final Map parameters) { + + String relTypeNode = kind == AnyTypeKind.ANY_OBJECT + ? Neo4jARelationship.NODE + : Neo4jURelationship.NODE; + + return "MATCH (n) " + + "WHERE " + (not ? "NOT " : "") + "EXISTS { MATCH (n)-[]-(r:" + relTypeNode + ")-[]-" + + "(t:" + Neo4jRelationshipType.NODE + + " {id: $ " + setParameter(parameters, cond.getRelationshipTypeKey()) + "}) } "; + } + + protected String getQuery( + final AnyTypeKind kind, + final RelationshipCond cond, + final boolean not, + final Map parameters) { + + Set rightAnyObjects = check(cond); + + String relTypeNode = kind == AnyTypeKind.ANY_OBJECT + ? Neo4jARelationship.NODE + : Neo4jURelationship.NODE; + String destRelType = kind == AnyTypeKind.ANY_OBJECT + ? Neo4jARelationship.DEST_REL + : Neo4jURelationship.DEST_REL; + + return "MATCH (n) " + + "WHERE EXISTS { " + + "MATCH(n)-[]-(:" + relTypeNode + ")-[:" + destRelType + "]-(anyObject:" + Neo4jAnyObject.NODE + ") " + + "WHERE anyObject.id " + (not ? "NOT " : "") + "IN $" + setParameter(parameters, rightAnyObjects) + + " } "; + } + + protected String getQuery( + final AnyTypeKind kind, + final MembershipCond cond, + final boolean not, + final Map parameters) { + + cond.setGroup(cond.getGroup().replace("%", ".*")); + List groupKeys = check(cond); + + String membershipTypeNode = kind == AnyTypeKind.ANY_OBJECT + ? Neo4jAMembership.NODE + : Neo4jUMembership.NODE; + String dynMembRelType = kind == AnyTypeKind.ANY_OBJECT + ? GroupRepoExt.DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + : GroupRepoExt.DYN_GROUP_USER_MEMBERSHIP_REL; + + String param = setParameter(parameters, groupKeys); + return "MATCH (n) " + + "WHERE " + (not ? "NOT " : "") + "EXISTS { " + + "MATCH (n)-[]-(:" + membershipTypeNode + ")-[]-" + + "(g:" + Neo4jGroup.NODE + ") WHERE g.id IN $" + param + " } " + + (not ? "AND NOT" : "OR") + " EXISTS { " + + "MATCH (n)-[:" + dynMembRelType + "]-" + + "(g:" + Neo4jGroup.NODE + ") WHERE g.id IN $" + param + " } "; + } + + protected String getQuery( + final AnyTypeKind kind, + final MemberCond cond, + final boolean not, + final Map parameters) { + + Set memberKeys = check(cond); + + String membershipTypeNode = kind == AnyTypeKind.ANY_OBJECT + ? Neo4jAMembership.NODE + : Neo4jUMembership.NODE; + String memberTypeNode = kind == AnyTypeKind.ANY_OBJECT + ? Neo4jAnyObject.NODE + : Neo4jUser.NODE; + String dynMembRelType = kind == AnyTypeKind.ANY_OBJECT + ? GroupRepoExt.DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + : GroupRepoExt.DYN_GROUP_USER_MEMBERSHIP_REL; + + String param = setParameter(parameters, memberKeys); + return "MATCH (n) " + + "WHERE " + (not ? "NOT " : "") + "EXISTS { " + + "MATCH (n)-[]-(:" + membershipTypeNode + ")-[]-" + + "(m:" + memberTypeNode + ") WHERE m.id IN $" + param + " } " + + (not ? "AND NOT" : "OR") + " EXISTS { " + + "MATCH (n)-[:" + dynMembRelType + "]-" + + "(m:" + memberTypeNode + ") WHERE m.id IN $" + param + " } "; + } + + protected String getQuery( + final RoleCond cond, + final boolean not, + final Map parameters) { + + String param = setParameter(parameters, cond.getRole()); + return "MATCH (n) " + + "WHERE " + (not ? "NOT " : "") + + "(n)-[:" + Neo4jUser.ROLE_MEMBERSHIP_REL + "]-" + + "(:" + Neo4jRole.NODE + " {id: $" + param + "}) " + + (not ? "AND NOT" : "OR") + " EXISTS { " + + "MATCH (n)-[:" + RoleRepoExt.DYN_ROLE_MEMBERSHIP_REL + "]-" + + "(:" + Neo4jRole.NODE + " {id: $" + param + "}) } "; + } + + protected String getQuery( + final PrivilegeCond cond, + final boolean not, + final Map parameters) { + + String param = setParameter(parameters, cond.getPrivilege()); + return "MATCH (n) " + + "WHERE " + (not ? "NOT " : "") + + "(n)-[:" + Neo4jUser.ROLE_MEMBERSHIP_REL + "]-" + + "(:" + Neo4jRole.NODE + ")-[]-" + + "(:" + Neo4jPrivilege.NODE + " {id: $" + param + "}) " + + (not ? "AND NOT" : "OR") + " EXISTS { " + + "MATCH (n)-[:" + RoleRepoExt.DYN_ROLE_MEMBERSHIP_REL + "]-" + + "(:" + Neo4jRole.NODE + ")-[]-" + + "(:" + Neo4jPrivilege.NODE + " {id: $" + param + "}) } "; + } + + protected String getQuery( + final DynRealmCond cond, + final boolean not, + final Map parameters) { + + return "MATCH (n) " + + "WHERE " + (not ? "NOT " : "") + "(n)-" + + "[:" + DynRealmRepoExt.DYN_REALM_MEMBERSHIP_REL + "]-" + + "(:" + Neo4jDynRealm.NODE + " {id: $" + setParameter(parameters, cond.getDynRealm()) + "}) "; + } + + protected String getQuery( + final AnyTypeKind kind, + final ResourceCond cond, + final boolean not, + final Map parameters) { + + String param = setParameter(parameters, cond.getResource()); + TextStringBuilder query = new TextStringBuilder("MATCH (n) "). + append("WHERE "). + append(not ? "NOT " : ""). + append("(n)-[]-(:").append(Neo4jExternalResource.NODE).append(" {id: $").append(param).append("}) "); + + if (kind == AnyTypeKind.USER || kind == AnyTypeKind.ANY_OBJECT) { + String membershipTypeNode = kind == AnyTypeKind.ANY_OBJECT + ? Neo4jAMembership.NODE + : Neo4jUMembership.NODE; + + if (not) { + query.append("AND NOT EXISTS { "); + } else { + query.append("OR EXISTS { "); + } + + query.append("MATCH (n)-[]-(:").append(membershipTypeNode).append(")-[]-"). + append("(g:").append(Neo4jGroup.NODE).append(") "). + append("WHERE "). + append("(g)-[]-(:").append(Neo4jExternalResource.NODE).append(" {id: $").append(param).append("})"). + append(" } "); + } + + return query.toString(); + } + + protected void fillAttrQuery( + final AnyUtils anyUtils, + final TextStringBuilder query, + final PlainAttrValue attrValue, + final PlainSchema schema, + final AttrCond cond, + final boolean not, + final Map parameters) { + + if (not && cond.getType() == AttrCond.Type.ISNULL) { + cond.setType(AttrCond.Type.ISNOTNULL); + fillAttrQuery(anyUtils, query, attrValue, schema, cond, true, parameters); + return; + } + if (not) { + if (schema.isUniqueConstraint()) { + fillAttrQuery(anyUtils, query, attrValue, schema, cond, false, parameters); + query.replaceFirst("WHERE", "WHERE NOT("); + query.append(')'); + } else { + fillAttrQuery(anyUtils, query, attrValue, schema, cond, false, parameters); + query.replaceAll("any(", schema.getKey() + " IS NULL OR none("); + } + return; + } + + String value = Optional.ofNullable(attrValue.getDateValue()). + map(DateTimeFormatter.ISO_OFFSET_DATE_TIME::format). + orElse(cond.getExpression()); + + boolean isStr = true; + boolean lower = false; + if (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum) { + lower = (cond.getType() == AttrCond.Type.IEQ || cond.getType() == AttrCond.Type.ILIKE); + } else if (schema.getType() != AttrSchemaType.Date) { + lower = false; + try { + switch (schema.getType()) { + case Long -> + Long.valueOf(value); + + case Double -> + Double.valueOf(value); + + case Boolean -> { + if (!("true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value))) { + throw new IllegalArgumentException(); + } + } + + default -> { + } + } + + isStr = false; + } catch (Exception nfe) { + // ignore + } + } + + query.append("WHERE "); + + switch (cond.getType()) { + case ISNULL -> { + } + + case ISNOTNULL -> + query.append(schema.getKey()).append(" IS NOT NULL"); + + case ILIKE, LIKE -> { + if (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum) { + appendPlainAttrCond( + query, + schema, + " =~ \"" + (lower ? "(?i)" : "") + + AnyRepoExt.escapeForLikeRegex(value).replace("%", ".*") + '"'); + } else { + query.append(ALWAYS_FALSE_ASSERTION); + LOG.error("LIKE is only compatible with string or enum schemas"); + } + } + + case IEQ, EQ -> { + if (StringUtils.containsAny(value, AnyRepoExt.REGEX_CHARS) || lower) { + appendPlainAttrCond( + query, + schema, + " =~ \"^" + (lower ? "(?i)" : "") + + AnyRepoExt.escapeForLikeRegex(value).replace("%", ".*") + "$\""); + } else { + appendPlainAttrCond( + query, + schema, + " = " + escapeIfString(value, isStr)); + } + } + + case GE -> + appendPlainAttrCond( + query, + schema, + " >= " + escapeIfString(value, isStr)); + + case GT -> + appendPlainAttrCond( + query, + schema, + " > " + escapeIfString(value, isStr)); + + case LE -> + appendPlainAttrCond( + query, + schema, + " <= " + escapeIfString(value, isStr)); + + case LT -> + appendPlainAttrCond( + query, + schema, + " < " + escapeIfString(value, isStr)); + + default -> { + } + } + // shouldn't occour: processed before + } + + protected void fillAttrQuery( + final TextStringBuilder query, + final PlainAttrValue attrValue, + final PlainSchema schema, + final AttrCond cond, + final boolean not, + final Map parameters) { + + if (not && cond.getType() == AttrCond.Type.ISNULL) { + cond.setType(AttrCond.Type.ISNOTNULL); + fillAttrQuery(query, attrValue, schema, cond, true, parameters); + return; + } + if (not) { + query.append("NOT ("); + fillAttrQuery(query, attrValue, schema, cond, false, parameters); + query.append(')'); + return; + } + if (not && cond.getType() == AttrCond.Type.ISNULL) { + cond.setType(AttrCond.Type.ISNOTNULL); + fillAttrQuery(query, attrValue, schema, cond, true, parameters); + return; + } + + boolean lower = (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum) + && (cond.getType() == AttrCond.Type.IEQ || cond.getType() == AttrCond.Type.ILIKE); + + String property = "n." + cond.getSchema(); + if ((schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum) && lower) { + property = "toLower (" + property + ')'; + } + + switch (cond.getType()) { + + case ISNULL -> + query.append(property).append(" IS NULL"); + + case ISNOTNULL -> + query.append(property).append(" IS NOT NULL"); + + case ILIKE, LIKE -> { + if (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum) { + query.append(property).append(" =~ "); + if (lower) { + query.append("toLower($"). + append(setParameter(parameters, cond.getExpression().replace("%", ".*"))). + append(')'); + } else { + query.append('$').append(setParameter(parameters, cond.getExpression().replace("%", ".*"))); + } + } else { + query.append(' ').append(ALWAYS_FALSE_ASSERTION); + LOG.error("LIKE is only compatible with string or enum schemas"); + } + } + + case IEQ, EQ -> { + query.append(property).append('='); + + if (lower + && (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum)) { + + query.append("toLower($").append(setParameter(parameters, attrValue.getValue())).append(')'); + } else { + query.append('$').append(setParameter(parameters, attrValue.getValue())); + } + } + + case GE -> { + query.append(property); + if (not) { + query.append('<'); + } else { + query.append(">="); + } + query.append('$').append(setParameter(parameters, attrValue.getValue())); + } + + case GT -> { + query.append(property); + if (not) { + query.append("<="); + } else { + query.append('>'); + } + query.append('$').append(setParameter(parameters, attrValue.getValue())); + } + + case LE -> { + query.append(property); + if (not) { + query.append('>'); + } else { + query.append("<="); + } + query.append('$').append(setParameter(parameters, attrValue.getValue())); + } + + case LT -> { + query.append(property); + if (not) { + query.append(">="); + } else { + query.append('<'); + } + query.append('$').append(setParameter(parameters, attrValue.getValue())); + } + + default -> { + } + } + } + + protected Pair getQuery( + final AnyTypeKind kind, + final AnyCond cond, + final boolean not, + final Map parameters) { + + if (JAXRSService.PARAM_REALM.equals(cond.getSchema())) { + if (!SyncopeConstants.UUID_PATTERN.matcher(cond.getExpression()).matches()) { + + Realm realm = realmDAO.findByFullPath(cond.getExpression()). + orElseThrow(() -> new IllegalArgumentException( + "Invalid Realm full path: " + cond.getExpression())); + cond.setExpression(realm.getKey()); + } + + return Pair.of( + "MATCH (n)-[]-" + + "(:" + Neo4jRealm.NODE + " {id: $" + setParameter(parameters, cond.getExpression()) + "}) ", + null); + } + + Triple checked = check(cond, kind); + + TextStringBuilder query = new TextStringBuilder("MATCH (n) WHERE "); + + plainSchemaDAO.findById(cond.getSchema()).ifPresentOrElse( + schema -> fillAttrQuery( + anyUtilsFactory.getInstance(kind), + query, checked.getMiddle(), checked.getLeft(), checked.getRight(), not, parameters), + () -> fillAttrQuery( + query, checked.getMiddle(), checked.getLeft(), checked.getRight(), not, parameters)); + + return Pair.of(query.toString(), checked.getRight().getSchema()); + } + + protected Pair getQuery( + final AnyTypeKind kind, + final AttrCond cond, + final boolean not, + final Map parameters) { + + Pair checked = check(cond, kind); + + TextStringBuilder query = new TextStringBuilder("MATCH (n) "); + switch (cond.getType()) { + case ISNOTNULL -> + query.append("WHERE n.`plainAttrs.").append(checked.getLeft().getKey()).append("` IS NOT NULL"); + + case ISNULL -> + query.append("WHERE n.`plainAttrs.").append(checked.getLeft().getKey()).append("` IS NULL"); + + default -> + fillAttrQuery( + anyUtilsFactory.getInstance(kind), + query, checked.getRight(), checked.getLeft(), cond, not, parameters); + } + + return Pair.of(query.toString(), checked.getLeft()); + } + + protected void getQueryForCustomConds( + final AnyTypeKind kind, + final SearchCond cond, + final Map parameters, + final boolean not, + final TextStringBuilder query) { + + // do nothing by default, leave it open for subclasses + } + + protected void queryOp( + final TextStringBuilder query, + final String op, + final QueryInfo leftInfo, + final QueryInfo rightInfo) { + + query.append("WHERE EXISTS { "). + append(StringUtils.prependIfMissing(leftInfo.query().toString(), "MATCH (n) ")). + append(" } "). + append(op). + append(" EXISTS { "). + append(rightInfo.query()). + append(" }"); + } + + protected QueryInfo getQuery(final AnyTypeKind kind, final SearchCond cond, final Map parameters) { + boolean not = cond.getType() == SearchCond.Type.NOT_LEAF; + + TextStringBuilder query = new TextStringBuilder(); + Set involvedFields = new HashSet<>(); + Set involvedPlainSchemas = new HashSet<>(); + + switch (cond.getType()) { + case LEAF, NOT_LEAF -> { + cond.getLeaf(AnyTypeCond.class). + filter(leaf -> AnyTypeKind.ANY_OBJECT == kind). + ifPresent(leaf -> query.append(getQuery(leaf, not, parameters))); + + cond.getLeaf(AuxClassCond.class). + ifPresent(leaf -> query.append(getQuery(leaf, not, parameters))); + + cond.getLeaf(RelationshipTypeCond.class). + filter(leaf -> AnyTypeKind.GROUP != kind). + ifPresent(leaf -> query.append(getQuery(kind, leaf, not, parameters))); + + cond.getLeaf(RelationshipCond.class). + filter(leaf -> AnyTypeKind.GROUP != kind). + ifPresent(leaf -> query.append(getQuery(kind, leaf, not, parameters))); + + cond.getLeaf(MembershipCond.class). + filter(leaf -> AnyTypeKind.GROUP != kind). + ifPresent(leaf -> query.append(getQuery(kind, leaf, not, parameters))); + + cond.getLeaf(MemberCond.class). + filter(leaf -> AnyTypeKind.GROUP == kind). + ifPresent(leaf -> query.append(getQuery(kind, leaf, not, parameters))); + + cond.getLeaf(RoleCond.class). + filter(leaf -> AnyTypeKind.USER == kind). + ifPresent(leaf -> query.append(getQuery(leaf, not, parameters))); + + cond.getLeaf(PrivilegeCond.class). + filter(leaf -> AnyTypeKind.USER == kind). + ifPresent(leaf -> query.append(getQuery(leaf, not, parameters))); + + cond.getLeaf(DynRealmCond.class). + ifPresent(leaf -> query.append(getQuery(leaf, not, parameters))); + + cond.getLeaf(ResourceCond.class). + ifPresent(leaf -> query.append(getQuery(kind, leaf, not, parameters))); + + cond.getLeaf(AnyCond.class).ifPresentOrElse( + anyCond -> { + Pair anyCondResult = getQuery(kind, anyCond, not, parameters); + query.append(anyCondResult.getLeft()); + Optional.ofNullable(anyCondResult.getRight()).ifPresent(involvedFields::add); + }, + () -> cond.getLeaf(AttrCond.class).ifPresent(leaf -> { + Pair attrCondResult = getQuery(kind, leaf, not, parameters); + query.append(attrCondResult.getLeft()); + involvedPlainSchemas.add(attrCondResult.getRight()); + })); + + // allow for additional search conditions + getQueryForCustomConds(kind, cond, parameters, not, query); + } + case AND -> { + QueryInfo leftAndInfo = getQuery(kind, cond.getLeft(), parameters); + involvedFields.addAll(leftAndInfo.fields()); + involvedPlainSchemas.addAll(leftAndInfo.plainSchemas()); + + QueryInfo rigthAndInfo = getQuery(kind, cond.getRight(), parameters); + involvedFields.addAll(rigthAndInfo.fields()); + involvedPlainSchemas.addAll(rigthAndInfo.plainSchemas()); + + queryOp(query, "AND", leftAndInfo, rigthAndInfo); + } + + case OR -> { + QueryInfo leftOrInfo = getQuery(kind, cond.getLeft(), parameters); + involvedFields.addAll(leftOrInfo.fields()); + involvedPlainSchemas.addAll(leftOrInfo.plainSchemas()); + + QueryInfo rigthOrInfo = getQuery(kind, cond.getRight(), parameters); + involvedFields.addAll(rigthOrInfo.fields()); + involvedPlainSchemas.addAll(rigthOrInfo.plainSchemas()); + + queryOp(query, "OR", leftOrInfo, rigthOrInfo); + } + + default -> { + } + } + + return new QueryInfo(query, involvedFields, involvedPlainSchemas); + } + + protected void wrapQuery( + final QueryInfo queryInfo, + final Streamable orderBy, + final AnyTypeKind kind, + final String adminRealmsFilter) { + + TextStringBuilder query = queryInfo.query(); + + TextStringBuilder match = new TextStringBuilder("MATCH (n:").append(AnyRepoExt.node(kind)).append(") "). + append("WITH n.id AS id"); + + // take fields into account + AnyUtils anyUtils = anyUtilsFactory.getInstance(kind); + queryInfo.fields().remove("id"); + Stream.concat( + queryInfo.fields().stream(), + orderBy.stream().filter(clause -> !"key".equals(clause.getProperty()) + && anyUtils.getField(clause.getProperty()).isPresent()).map(Order::getProperty)). + distinct().forEach(field -> match.append(", n.").append(field).append(" AS ").append(field)); + + // take plain schemas into account + Stream.concat( + queryInfo.plainSchemas().stream(), + orderBy.stream().map(clause -> plainSchemaDAO.findById(clause.getProperty())). + filter(Optional::isPresent).map(Optional::get)).distinct().forEach(schema -> { + + match.append(", apoc.convert.getJsonProperty(n, 'plainAttrs.").append(schema.getKey()); + if (schema.isUniqueConstraint()) { + match.append("', '$.uniqueValue')"); + } else { + match.append("', '$.values')"); + } + match.append(" AS ").append(schema.getKey()); + }); + + // take realms into account + if (query.startsWith("MATCH (n)")) { + query.replaceFirst("MATCH (n)", match + " WHERE EXISTS { MATCH (n)"); + query.append("} "); + } else { + query.insert(0, match.append(' ')); + } + query.append(" AND EXISTS { ").append(adminRealmsFilter).append(" } "); + } + + @Override + protected long doCount( + final Realm base, + final boolean recursive, + final Set adminRealms, + final SearchCond cond, + final AnyTypeKind kind) { + + Map parameters = new HashMap<>(); + + AdminRealmsFilter filter = getAdminRealmsFilter(base, recursive, adminRealms, parameters); + + // 1. get the query string from the search condition + QueryInfo queryInfo = getQuery( + kind, buildEffectiveCond(cond, filter.dynRealmKeys(), filter.groupOwners(), kind), parameters); + + // 2. wrap query + wrapQuery(queryInfo, Streamable.empty(), kind, filter.filter()); + TextStringBuilder query = queryInfo.query(); + + // 3. prepare the count query + query.append("RETURN COUNT(id)"); + + return neo4jTemplate.count(query.toString(), parameters); + } + + protected String parseOrderBy( + final AnyTypeKind kind, + final Stream orderBy) { + + AnyUtils anyUtils = anyUtilsFactory.getInstance(kind); + + List clauses = new ArrayList<>(); + + orderBy.forEach(clause -> { + if (anyUtils.getField(clause.getProperty()).isPresent()) { + // Manage difference among external key attribute and internal @Id + String fieldName = "key".equals(clause.getProperty()) ? "id" : clause.getProperty(); + + clauses.add(fieldName + " " + clause.getDirection().name()); + } else { + plainSchemaDAO.findById(clause.getProperty()). + ifPresent(schema -> clauses.add(schema.getKey() + " " + clause.getDirection().name())); + } + }); + + return clauses.stream().collect(Collectors.joining(", ")); + } + + @Override + protected > List doSearch( + final Realm base, + final boolean recursive, + final Set adminRealms, + final SearchCond cond, + final Pageable pageable, + final AnyTypeKind kind) { + + Map parameters = new HashMap<>(); + try { + AdminRealmsFilter filter = getAdminRealmsFilter(base, recursive, adminRealms, parameters); + + // 1. get the query string from the search condition + QueryInfo queryInfo = getQuery( + kind, buildEffectiveCond(cond, filter.dynRealmKeys(), filter.groupOwners(), kind), parameters); + + // 2. wrap query + wrapQuery(queryInfo, pageable.getSort(), kind, filter.filter()); + TextStringBuilder query = queryInfo.query(); + + // 3. prepare the search query + query.append("RETURN id "). + append("ORDER BY ").append(parseOrderBy(kind, pageable.getSort().get())); + + if (pageable.isPaged()) { + query.append(" SKIP ").append(pageable.getPageSize() * pageable.getPageNumber()). + append(" LIMIT ").append(pageable.getPageSize()); + } + + LOG.debug("Query with auth and order by statements: {}, parameters: {}", query, parameters); + + // 4. Prepare the result (avoiding duplicates) + return buildResult(neo4jClient.query(query.toString()).bindAll(parameters).fetch().all().stream(). + map(found -> found.get("id")).toList(), kind); + } catch (SyncopeClientException e) { + throw e; + } catch (Exception e) { + LOG.error("While searching for {}", kind, e); + } + + return List.of(); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAuditEntryDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAuditEntryDAO.java new file mode 100644 index 0000000000..4b76d5ed67 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAuditEntryDAO.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.persistence.neo4j.dao; + +import java.time.OffsetDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.audit.AuditEntry; +import org.apache.syncope.common.lib.types.AuditElements; +import org.apache.syncope.core.persistence.api.dao.AuditEntryDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAuditEntry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Pageable; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; + +public class Neo4jAuditEntryDAO implements AuditEntryDAO { + + protected static final Logger LOG = LoggerFactory.getLogger(AuditEntryDAO.class); + + protected static class MessageCriteriaBuilder { + + protected final StringBuilder query = new StringBuilder(); + + protected String andIfNeeded() { + return query.length() == 0 ? " " : " AND "; + } + + protected MessageCriteriaBuilder entityKey(final String entityKey) { + if (entityKey != null) { + query.append(andIfNeeded()).append("n.").append(MESSAGE_COLUMN). + append(" =~ '.*key.*").append(entityKey).append(".*'"); + } + return this; + } + + public MessageCriteriaBuilder type(final AuditElements.EventCategoryType type) { + if (type != null) { + query.append(andIfNeeded()).append("n.").append(MESSAGE_COLUMN). + append(" =~ '.*\"type\":\"").append(type.name()).append("\".*'"); + } + return this; + } + + public MessageCriteriaBuilder category(final String category) { + if (StringUtils.isNotBlank(category)) { + query.append(andIfNeeded()).append("n.").append(MESSAGE_COLUMN). + append(" =~ '.*\"category\":\"").append(category).append("\".*'"); + } + return this; + } + + public MessageCriteriaBuilder subcategory(final String subcategory) { + if (StringUtils.isNotBlank(subcategory)) { + query.append(andIfNeeded()).append("n.").append(MESSAGE_COLUMN). + append(" =~ '.*\"subcategory\":\"").append(subcategory).append("\".*'"); + } + return this; + } + + public MessageCriteriaBuilder events(final List events) { + if (!events.isEmpty()) { + query.append(andIfNeeded()).append("( "). + append(events.stream(). + map(event -> "n.*" + MESSAGE_COLUMN + + " =~ '.*\"event\":\"" + event + "\".*'"). + collect(Collectors.joining(" OR "))). + append(" )"); + } + return this; + } + + public MessageCriteriaBuilder result(final AuditElements.Result result) { + if (result != null) { + query.append(andIfNeeded()).append("n.").append(MESSAGE_COLUMN). + append(" =~ '.*\"result\":\"").append(result.name()).append("\".*' "); + } + return this; + } + + public MessageCriteriaBuilder before(final OffsetDateTime before, final Map parameters) { + if (before != null) { + query.append(andIfNeeded()).append("n.").append(EVENT_DATE_COLUMN). + append(" <= $before"); + parameters.put("before", before); + } + return this; + } + + public MessageCriteriaBuilder after(final OffsetDateTime after, final Map parameters) { + if (after != null) { + query.append(andIfNeeded()).append("n.").append(EVENT_DATE_COLUMN). + append(" >= $after"); + parameters.put("after", after); + } + return this; + } + + public String build() { + return query.toString(); + } + } + + protected final Neo4jTemplate neo4jTemplate; + + protected final Neo4jClient neo4jClient; + + public Neo4jAuditEntryDAO(final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient) { + this.neo4jTemplate = neo4jTemplate; + this.neo4jClient = neo4jClient; + } + + protected MessageCriteriaBuilder messageCriteriaBuilder(final String entityKey) { + return new MessageCriteriaBuilder().entityKey(entityKey); + } + + @Override + public long count( + final String entityKey, + final AuditElements.EventCategoryType type, + final String category, + final String subcategory, + final List events, + final AuditElements.Result result, + final OffsetDateTime before, + final OffsetDateTime after) { + + Map parameters = new HashMap<>(); + String query = "MATCH (n:" + Neo4jAuditEntry.NODE + ") " + + " WHERE " + messageCriteriaBuilder(entityKey). + type(type). + category(category). + subcategory(subcategory). + result(result). + events(events). + before(before, parameters). + after(after, parameters). + build() + + " RETURN COUNT(n)"; + return neo4jTemplate.count(query, parameters); + } + + @Transactional(readOnly = true) + @Override + public List search( + final String entityKey, + final AuditElements.EventCategoryType type, + final String category, + final String subcategory, + final List events, + final AuditElements.Result result, + final OffsetDateTime before, + final OffsetDateTime after, + final Pageable pageable) { + + Map parameters = new HashMap<>(); + + StringBuilder query = new StringBuilder("MATCH (n:" + Neo4jAuditEntry.NODE + ") " + + " WHERE " + messageCriteriaBuilder(entityKey). + type(type). + category(category). + subcategory(subcategory). + result(result). + events(events). + before(before, parameters). + after(after, parameters). + build() + + " RETURN n.id"); + + if (!pageable.getSort().isEmpty()) { + query.append(" ORDER BY ").append(pageable.getSort().stream(). + map(clause -> "n." + clause.getProperty() + ' ' + clause.getDirection().name()). + collect(Collectors.joining(","))); + } + + if (pageable.isPaged()) { + query.append(" SKIP ").append(pageable.getPageSize() * pageable.getPageNumber()). + append(" LIMIT ").append(pageable.getPageSize()); + } + + return neo4jClient.query(query.toString()). + bindAll(parameters).fetch().all().stream(). + map(found -> neo4jTemplate.findById(found.get("n.id"), Neo4jAuditEntry.class)). + filter(Optional::isPresent).map(Optional::get).map(AuditEntry.class::cast).toList(); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jBatchDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jBatchDAO.java new file mode 100644 index 0000000000..39f51bbcb1 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jBatchDAO.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.persistence.neo4j.dao; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.dao.BatchDAO; +import org.apache.syncope.core.persistence.api.entity.Batch; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAccessToken; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jBatch; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; + +@Transactional(rollbackFor = Throwable.class) +public class Neo4jBatchDAO implements BatchDAO { + + protected final Neo4jTemplate neo4jTemplate; + + protected final Neo4jClient neo4jClient; + + protected final NodeValidator nodeValidator; + + public Neo4jBatchDAO( + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + this.neo4jTemplate = neo4jTemplate; + this.neo4jClient = neo4jClient; + this.nodeValidator = nodeValidator; + } + + @Transactional(readOnly = true) + @Override + public boolean existsById(final String key) { + return neo4jTemplate.existsById(key, Neo4jBatch.class); + } + + @Transactional(readOnly = true) + @Override + public Optional findById(final String key) { + return neo4jTemplate.findById(key, Neo4jBatch.class).map(Batch.class::cast); + } + + @Transactional(readOnly = true) + @Override + public long count() { + return neo4jTemplate.count(Neo4jBatch.class); + } + + @Transactional(readOnly = true) + @Override + public List findAll() { + return neo4jTemplate.findAll(Neo4jBatch.class); + } + + @Override + public S save(final S batch) { + return neo4jTemplate.save(nodeValidator.validate(batch)); + } + + @Override + public void delete(final Batch batch) { + neo4jTemplate.deleteById(batch.getKey(), Neo4jBatch.class); + } + + @Override + public void deleteById(final String key) { + findById(key).ifPresent(this::delete); + } + + @Override + public long deleteExpired() { + Map result = neo4jClient.query( + "MATCH (n:" + Neo4jAccessToken.NODE + " WHERE n.expirationTime < $now) " + + "DETACH DELETE n " + + "RETURN count(*) AS deleted").bindAll(Map.of("now", OffsetDateTime.now())). + fetch().one().orElse(Map.of("deleted", 0L)); + + return (long) result.getOrDefault(result.get("deleted"), 0L); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jEntityCacheDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jEntityCacheDAO.java new file mode 100644 index 0000000000..68d04c10da --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jEntityCacheDAO.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.core.persistence.neo4j.dao; + +import java.util.Map; +import org.apache.syncope.core.persistence.api.dao.EntityCacheDAO; +import org.apache.syncope.core.persistence.api.entity.Entity; + +public class Neo4jEntityCacheDAO implements EntityCacheDAO { + + @Override + public Map getStatistics() { + return Map.of(); + } + + @Override + public void enableStatistics() { + // not supported + } + + @Override + public void disableStatistics() { + // not supported + } + + @Override + public void resetStatistics() { + // not supported + } + + @Override + public void evict(final Class entityClass, final String key) { + // not supported + } + + @Override + public void clearCache() { + // not supported + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jJobStatusDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jJobStatusDAO.java new file mode 100644 index 0000000000..67a088162a --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jJobStatusDAO.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.persistence.neo4j.dao; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.dao.JobStatusDAO; +import org.apache.syncope.core.persistence.api.entity.JobStatus; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jJobStatus; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; + +@Transactional(rollbackFor = Throwable.class) +public class Neo4jJobStatusDAO implements JobStatusDAO { + + protected final Neo4jTemplate neo4jTemplate; + + protected final NodeValidator nodeValidator; + + public Neo4jJobStatusDAO(final Neo4jTemplate neo4jTemplate, final NodeValidator nodeValidator) { + this.neo4jTemplate = neo4jTemplate; + this.nodeValidator = nodeValidator; + } + + @Override + public boolean existsById(final String key) { + return neo4jTemplate.existsById(key, Neo4jJobStatus.class); + } + + @Transactional(readOnly = true) + @Override + public Optional findById(final String key) { + return neo4jTemplate.findById(key, Neo4jJobStatus.class); + } + + @Transactional(readOnly = true) + @Override + public long count() { + return neo4jTemplate.count(Neo4jJobStatus.class); + } + + @Transactional(readOnly = true) + @Override + public List findAll() { + return neo4jTemplate.findAll(Neo4jJobStatus.class); + } + + @Override + public S save(final S jobStatus) { + return neo4jTemplate.save(nodeValidator.validate(jobStatus)); + } + + @Override + public void delete(final JobStatus jobStatus) { + neo4jTemplate.deleteById(jobStatus.getKey(), Neo4jJobStatus.class); + } + + @Override + public void deleteById(final String key) { + findById(key).ifPresent(this::delete); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jOIDCJWKSDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jOIDCJWKSDAO.java new file mode 100644 index 0000000000..99d98911e5 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jOIDCJWKSDAO.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.persistence.neo4j.dao; + +import java.util.Optional; +import org.apache.syncope.core.persistence.api.dao.OIDCJWKSDAO; +import org.apache.syncope.core.persistence.api.entity.am.OIDCJWKS; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jOIDCJWKS; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; + +public class Neo4jOIDCJWKSDAO implements OIDCJWKSDAO { + + protected final Neo4jTemplate neo4jTemplate; + + protected final NodeValidator nodeValidator; + + public Neo4jOIDCJWKSDAO(final Neo4jTemplate neo4jTemplate, final NodeValidator nodeValidator) { + this.neo4jTemplate = neo4jTemplate; + this.nodeValidator = nodeValidator; + } + + @Transactional(readOnly = true) + @Override + public Optional get() { + return neo4jTemplate.findAll(Neo4jOIDCJWKS.class).stream().findFirst().map(OIDCJWKS.class::cast); + } + + @Override + public OIDCJWKS save(final OIDCJWKS jwks) { + return neo4jTemplate.save(nodeValidator.validate(jwks)); + } + + @Override + public void delete() { + neo4jTemplate.deleteAll(Neo4jOIDCJWKS.class); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jPersistenceInfoDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jPersistenceInfoDAO.java new file mode 100644 index 0000000000..56bef4d7aa --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jPersistenceInfoDAO.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.core.persistence.neo4j.dao; + +import java.util.LinkedHashMap; +import java.util.Map; +import org.apache.syncope.core.persistence.api.dao.PersistenceInfoDAO; +import org.neo4j.driver.Driver; +import org.neo4j.driver.Session; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Neo4jPersistenceInfoDAO implements PersistenceInfoDAO { + + protected static final Logger LOG = LoggerFactory.getLogger(PersistenceInfoDAO.class); + + protected static final String CYPHER = + """ +CALL dbms.components() YIELD versions, name, edition WHERE name = 'Neo4j Kernel' RETURN edition, versions[0] as version +"""; + + protected final Driver driver; + + public Neo4jPersistenceInfoDAO(final Driver driver) { + this.driver = driver; + } + + @Override + public Map info() { + Map result = new LinkedHashMap<>(); + + try (Session session = driver.session()) { + result.putAll(session.run(CYPHER).next().asMap()); + } + + result.put("metrics", driver.metrics().connectionPoolMetrics()); + + return result; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jPolicyDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jPolicyDAO.java new file mode 100644 index 0000000000..419c1526b5 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jPolicyDAO.java @@ -0,0 +1,245 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.dao.CASSPClientAppDAO; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +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.RealmDAO; +import org.apache.syncope.core.persistence.api.dao.SAML2SPClientAppDAO; +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.policy.AccessPolicy; +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.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.PullPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementation; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccessPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccountPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAttrReleasePolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAuthPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPasswordPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPropagationPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPullCorrelationRuleEntity; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPullPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPushCorrelationRuleEntity; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPushPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jTicketExpirationPolicy; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class Neo4jPolicyDAO extends AbstractDAO implements PolicyDAO { + + protected static final Logger LOG = LoggerFactory.getLogger(PolicyDAO.class); + + protected static Class getNodeReference(final Class reference) { + return AccountPolicy.class.isAssignableFrom(reference) + ? Neo4jAccountPolicy.class + : PasswordPolicy.class.isAssignableFrom(reference) + ? Neo4jPasswordPolicy.class + : PropagationPolicy.class.isAssignableFrom(reference) + ? Neo4jPropagationPolicy.class + : PullPolicy.class.isAssignableFrom(reference) + ? Neo4jPullPolicy.class + : PushPolicy.class.isAssignableFrom(reference) + ? Neo4jPushPolicy.class + : AuthPolicy.class.isAssignableFrom(reference) + ? Neo4jAuthPolicy.class + : AccessPolicy.class.isAssignableFrom(reference) + ? Neo4jAccessPolicy.class + : AttrReleasePolicy.class.isAssignableFrom(reference) + ? Neo4jAttrReleasePolicy.class + : TicketExpirationPolicy.class.isAssignableFrom(reference) + ? Neo4jTicketExpirationPolicy.class + : null; + } + + protected final RealmDAO realmDAO; + + protected final ExternalResourceDAO resourceDAO; + + protected final CASSPClientAppDAO casSPClientAppDAO; + + protected final OIDCRPClientAppDAO oidcRPClientAppDAO; + + protected final SAML2SPClientAppDAO saml2SPClientAppDAO; + + protected final NodeValidator nodeValidator; + + public Neo4jPolicyDAO( + final RealmDAO realmDAO, + final ExternalResourceDAO resourceDAO, + final CASSPClientAppDAO casSPClientAppDAO, + final OIDCRPClientAppDAO oidcRPClientAppDAO, + final SAML2SPClientAppDAO saml2SPClientAppDAO, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super(neo4jTemplate, neo4jClient); + this.realmDAO = realmDAO; + this.resourceDAO = resourceDAO; + this.casSPClientAppDAO = casSPClientAppDAO; + this.oidcRPClientAppDAO = oidcRPClientAppDAO; + this.saml2SPClientAppDAO = saml2SPClientAppDAO; + this.nodeValidator = nodeValidator; + } + + @Override + public boolean existsById(final String key) { + return neo4jTemplate.count( + "MATCH (n:" + Neo4jPolicy.NODE + ") WHERE n.id = $key RETURN count(n)", Map.of("key", key)) > 0; + } + + @Override + public Optional findById(final String key) { + return neo4jClient.query( + "MATCH (n:" + Neo4jPolicy.NODE + ") WHERE n.id = $key RETURN n.id"). + bindAll(Map.of("key", key)).fetch().one(). + flatMap(toOptional("n.id", Neo4jPolicy.class)); + } + + @SuppressWarnings("unchecked") + @Override + public Optional findById(final String key, final Class reference) { + return neo4jTemplate.findById(key, getNodeReference(reference)).map(reference::cast); + } + + @Override + public long count() { + return neo4jTemplate.count("MATCH (n:" + Neo4jPolicy.NODE + ") RETURN count(n)"); + } + + @Override + public List findAll() { + return toList(neo4jClient.query( + "MATCH (n:" + Neo4jPolicy.NODE + ") RETURN n.id"). + fetch().all(), "n.id", Neo4jPolicy.class); + } + + @Override + public List findAll(final Class reference) { + return neo4jTemplate.findAll(getNodeReference(reference)).stream().map(reference::cast).toList(); + } + + @Override + public List findByAccountRule(final Implementation accountRule) { + return findByRelationship( + Neo4jAccountPolicy.NODE, Neo4jImplementation.NODE, accountRule.getKey(), Neo4jAccountPolicy.class); + } + + @Override + public List findByPasswordRule(final Implementation passwordRule) { + return findByRelationship( + Neo4jPasswordPolicy.NODE, Neo4jImplementation.NODE, passwordRule.getKey(), Neo4jPasswordPolicy.class); + } + + @Override + public List findByPullCorrelationRule(final Implementation correlationRule) { + return findByRelationship( + Neo4jPullPolicy.NODE, Neo4jImplementation.NODE, correlationRule.getKey(), Neo4jPullPolicy.class); + } + + @Override + public List findByPushCorrelationRule(final Implementation correlationRule) { + return findByRelationship( + Neo4jPushPolicy.NODE, Neo4jImplementation.NODE, correlationRule.getKey(), Neo4jPushPolicy.class); + } + + @Override + public List findByResource(final ExternalResource resource) { + return findByRelationship( + Neo4jAccountPolicy.NODE, Neo4jExternalResource.NODE, resource.getKey(), Neo4jAccountPolicy.class); + } + + @Override + public

P save(final P policy) { + return neo4jTemplate.save(nodeValidator.validate(policy)); + } + + @Override + public void delete(final Policy policy) { + if (policy instanceof AccountPolicy) { + realmDAO.findByPolicy(policy).forEach(realm -> realm.setAccountPolicy(null)); + resourceDAO.findByPolicy(policy).forEach(resource -> resource.setAccountPolicy(null)); + } else if (policy instanceof PasswordPolicy) { + realmDAO.findByPolicy(policy).forEach(realm -> realm.setPasswordPolicy(null)); + resourceDAO.findByPolicy(policy).forEach(resource -> resource.setPasswordPolicy(null)); + } else if (policy instanceof PropagationPolicy) { + resourceDAO.findByPolicy(policy).forEach(resource -> resource.setPropagationPolicy(null)); + } else if (policy instanceof PullPolicy) { + resourceDAO.findByPolicy(policy).forEach(resource -> resource.setPullPolicy(null)); + + cascadeDelete( + Neo4jPullCorrelationRuleEntity.NODE, + Neo4jPullPolicy.NODE, + policy.getKey(), Neo4jPullCorrelationRuleEntity.class); + } else if (policy instanceof PushPolicy) { + resourceDAO.findByPolicy(policy).forEach(resource -> resource.setPushPolicy(null)); + + cascadeDelete( + Neo4jPushCorrelationRuleEntity.NODE, + Neo4jPushPolicy.NODE, + policy.getKey(), Neo4jPushCorrelationRuleEntity.class); + } else if (policy instanceof AuthPolicy) { + realmDAO.findByPolicy(policy).forEach(realm -> realm.setAuthPolicy(null)); + casSPClientAppDAO.findAllByPolicy(policy).forEach(clientApp -> clientApp.setAuthPolicy(null)); + oidcRPClientAppDAO.findAllByPolicy(policy).forEach(clientApp -> clientApp.setAuthPolicy(null)); + saml2SPClientAppDAO.findAllByPolicy(policy).forEach(clientApp -> clientApp.setAuthPolicy(null)); + } else if (policy instanceof AccessPolicy) { + realmDAO.findByPolicy(policy).forEach(realm -> realm.setAccessPolicy(null)); + casSPClientAppDAO.findAllByPolicy(policy).forEach(clientApp -> clientApp.setAccessPolicy(null)); + oidcRPClientAppDAO.findAllByPolicy(policy).forEach(clientApp -> clientApp.setAccessPolicy(null)); + saml2SPClientAppDAO.findAllByPolicy(policy).forEach(clientApp -> clientApp.setAccessPolicy(null)); + } else if (policy instanceof AttrReleasePolicy) { + realmDAO.findByPolicy(policy).forEach(realm -> realm.setAttrReleasePolicy(null)); + casSPClientAppDAO.findAllByPolicy(policy).forEach(clientApp -> clientApp.setAttrReleasePolicy(null)); + oidcRPClientAppDAO.findAllByPolicy(policy).forEach(clientApp -> clientApp.setAttrReleasePolicy(null)); + saml2SPClientAppDAO.findAllByPolicy(policy).forEach(clientApp -> clientApp.setAttrReleasePolicy(null)); + } else if (policy instanceof TicketExpirationPolicy) { + realmDAO.findByPolicy(policy).forEach(realm -> realm.setTicketExpirationPolicy(null)); + casSPClientAppDAO.findAllByPolicy(policy).forEach(clientApp -> clientApp.setTicketExpirationPolicy(null)); + oidcRPClientAppDAO.findAllByPolicy(policy).forEach(clientApp -> clientApp.setTicketExpirationPolicy(null)); + saml2SPClientAppDAO.findAllByPolicy(policy).forEach(clientApp -> clientApp.setTicketExpirationPolicy(null)); + } + + neo4jClient.query("MATCH (n:" + Neo4jPolicy.NODE + " {id: $id}) DETACH DELETE n"). + bindAll(Map.of("id", policy.getKey())).run(); + } + + @Override + public void deleteById(final String key) { + findById(key).ifPresent(this::delete); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmDAO.java new file mode 100644 index 0000000000..028e987588 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmDAO.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.persistence.neo4j.dao; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.core.persistence.api.dao.MalformedPathException; +import org.apache.syncope.core.persistence.api.dao.RealmDAO; +import org.apache.syncope.core.persistence.api.dao.RoleDAO; +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.AccessPolicy; +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.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.ProvisioningPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; +import org.apache.syncope.core.persistence.api.search.SyncopePage; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyTemplateRealm; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementation; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccessPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccountPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAttrReleasePolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAuthPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPasswordPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jTicketExpirationPolicy; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.apache.syncope.core.provisioning.api.event.EntityLifecycleEvent; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.identityconnectors.framework.common.objects.SyncDeltaType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; + +public class Neo4jRealmDAO extends AbstractDAO implements RealmDAO { + + protected static final Logger LOG = LoggerFactory.getLogger(RealmDAO.class); + + protected static StringBuilder buildDescendantQuery( + final String base, + final String keyword, + final Map parameters) { + + StringBuilder queryString = new StringBuilder("MATCH (n:").append(Neo4jRealm.NODE).append(") "). + append("WHERE (n.fullPath = $base OR n.fullPath =~ $like)"); + parameters.put("base", base); + parameters.put("like", SyncopeConstants.ROOT_REALM.equals(base) ? "/.*" : base + "/.*"); + + if (keyword != null) { + queryString.append(" AND toLower(n.name) =~ $name"); + parameters.put("name", keyword.replaceAll("_", "\\\\_").toLowerCase() + ".*"); + } + + return queryString; + } + + protected final RoleDAO roleDAO; + + protected final ApplicationEventPublisher publisher; + + protected final NodeValidator nodeValidator; + + public Neo4jRealmDAO( + final RoleDAO roleDAO, + final ApplicationEventPublisher publisher, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super(neo4jTemplate, neo4jClient); + this.publisher = publisher; + this.roleDAO = roleDAO; + this.nodeValidator = nodeValidator; + } + + @Override + public Realm getRoot() { + return neo4jClient.query("MATCH (n:" + Neo4jRealm.NODE + ") WHERE n.fullPath = '/' RETURN n.id").fetch().one(). + flatMap(super.toOptional("n.id", Neo4jRealm.class)).orElseGet(() -> { + LOG.debug("Root realm not found"); + return null; + }); + } + + @Override + public boolean existsById(final String key) { + return neo4jTemplate.existsById(key, Neo4jRealm.class); + } + + @Transactional(readOnly = true) + @Override + public Optional findById(final String key) { + return neo4jTemplate.findById(key, Neo4jRealm.class); + } + + @Transactional(readOnly = true) + @Override + public Optional findByFullPath(final String fullPath) { + if (SyncopeConstants.ROOT_REALM.equals(fullPath)) { + return Optional.of(getRoot()); + } + + if (StringUtils.isBlank(fullPath) || !RealmDAO.PATH_PATTERN.matcher(fullPath).matches()) { + throw new MalformedPathException(fullPath); + } + + return neo4jClient.query( + "MATCH (n:" + Neo4jRealm.NODE + ") WHERE n.fullPath = $fullPath RETURN n.id"). + bindAll(Map.of("fullPath", fullPath)).fetch().one(). + flatMap(toOptional("n.id", Neo4jRealm.class)); + } + + @Override + public List findByName(final String name) { + return toList(neo4jClient.query( + "MATCH (n:" + Neo4jRealm.NODE + ") WHERE n.name = $name RETURN n.id"). + bindAll(Map.of("name", name)).fetch().all(), "n.id", Neo4jRealm.class); + } + + @Override + public long countDescendants(final String base, final String keyword) { + Map parameters = new HashMap<>(); + + StringBuilder queryString = buildDescendantQuery(base, keyword, parameters).append(" RETURN COUNT(n)"); + return neo4jTemplate.count(queryString.toString(), parameters); + } + + @Override + public List findDescendants(final String base, final String keyword, final Pageable pageable) { + Map parameters = new HashMap<>(); + + StringBuilder queryString = buildDescendantQuery(base, keyword, parameters). + append(" RETURN n.id ORDER BY n.fullPath"); + if (pageable.isPaged()) { + queryString.append(" SKIP ").append(pageable.getPageSize() * pageable.getPageNumber()). + append(" LIMIT ").append(pageable.getPageSize()); + } + + return toList(neo4jClient.query( + queryString.toString()).bindAll(parameters).fetch().all(), "n.id", Neo4jRealm.class); + } + + @Override + public List findDescendants(final String base, final String prefix) { + Map parameters = new HashMap<>(); + + StringBuilder queryString = buildDescendantQuery(base, null, parameters). + append(" AND (n.fullPath = $prefix OR n.fullPath =~ $likePrefix)"). + append(" RETURN n.id ORDER BY n.fullPath"); + parameters.put("prefix", prefix); + parameters.put("likePrefix", SyncopeConstants.ROOT_REALM.equals(prefix) ? "/.*" : prefix + "/.*"); + + return neo4jClient.query(queryString.toString()). + bindAll(parameters).fetch().all().stream(). + map(found -> (String) found.values().iterator().next()).toList(); + } + + protected List findSamePolicyChildren(final Realm realm, final T policy) { + List result = new ArrayList<>(); + + findChildren(realm).stream(). + filter(child -> (policy instanceof AccountPolicy + && child.getAccountPolicy() == null || policy.equals(child.getAccountPolicy())) + || (policy instanceof PasswordPolicy + && child.getPasswordPolicy() == null || policy.equals(child.getPasswordPolicy()))). + forEach(child -> { + result.add(child); + result.addAll(findSamePolicyChildren(child, policy)); + }); + + return result; + } + + @Override + public List findByPolicy(final T policy) { + if (policy instanceof PropagationPolicy || policy instanceof ProvisioningPolicy) { + return List.of(); + } + + String relationship = null; + String label = null; + if (policy instanceof AccountPolicy) { + relationship = Neo4jRealm.REALM_ACCOUNT_POLICY_REL; + label = Neo4jAccountPolicy.NODE + ":" + Neo4jPolicy.NODE; + } else if (policy instanceof PasswordPolicy) { + relationship = Neo4jRealm.REALM_PASSWORD_POLICY_REL; + label = Neo4jPasswordPolicy.NODE + ":" + Neo4jPolicy.NODE; + } else if (policy instanceof AuthPolicy) { + relationship = Neo4jRealm.REALM_AUTH_POLICY_REL; + label = Neo4jAuthPolicy.NODE + ":" + Neo4jPolicy.NODE; + } else if (policy instanceof AccessPolicy) { + relationship = Neo4jRealm.REALM_ACCESS_POLICY_REL; + label = Neo4jAccessPolicy.NODE + ":" + Neo4jPolicy.NODE; + } else if (policy instanceof AttrReleasePolicy) { + relationship = Neo4jRealm.REALM_ATTR_RELEASE_POLICY_REL; + label = Neo4jAttrReleasePolicy.NODE + ":" + Neo4jPolicy.NODE; + } else if (policy instanceof TicketExpirationPolicy) { + relationship = Neo4jRealm.REALM_TICKET_EXPIRATION_POLICY_REL; + label = Neo4jTicketExpirationPolicy.NODE + ":" + Neo4jPolicy.NODE; + } + + List found = findByRelationship( + Neo4jRealm.NODE, + label, + policy.getKey(), + relationship, + Neo4jRealm.class); + + List result = new ArrayList<>(); + found.forEach(realm -> { + result.add(realm); + result.addAll(findSamePolicyChildren(realm, policy)); + }); + + return result; + } + + protected void findAncestors(final List result, final Realm realm) { + if (realm.getParent() != null && !result.contains(realm.getParent())) { + result.add(realm.getParent()); + findAncestors(result, realm.getParent()); + } + } + + @Override + public List findAncestors(final Realm realm) { + List result = new ArrayList<>(); + result.add(realm); + findAncestors(result, realm); + return result; + } + + @Override + public List findChildren(final Realm realm) { + return toList(neo4jClient.query( + "MATCH (n:" + Neo4jRealm.NODE + " {id: $id})<-[r:" + Neo4jRealm.PARENT_REL + "]-(c) RETURN c.id"). + bindAll(Map.of("id", realm.getKey())).fetch().all(), "c.id", Neo4jRealm.class); + } + + @Override + public List findByActionsContaining(final Implementation logicActions) { + return findByRelationship(Neo4jRealm.NODE, Neo4jImplementation.NODE, logicActions.getKey(), Neo4jRealm.class); + } + + @Override + public List findByResources(final ExternalResource resource) { + return findByRelationship(Neo4jRealm.NODE, Neo4jExternalResource.NODE, resource.getKey(), Neo4jRealm.class); + } + + @Override + public long count() { + return neo4jTemplate.count(Neo4jRealm.class); + } + + @Override + public List findAll() { + throw new UnsupportedOperationException(); + } + + @Override + public Page findAll(final Pageable pageable) { + StringBuilder query = new StringBuilder("MATCH (n:" + Neo4jRealm.NODE + ") RETURN n.id ORDER BY n.fullPath"); + + if (pageable.isPaged()) { + query.append(" SKIP ").append(pageable.getPageSize() * pageable.getPageNumber()). + append(" LIMIT ").append(pageable.getPageSize()); + } + + List result = toList( + neo4jClient.query(query.toString()).fetch().all(), + "n.id", + Neo4jRealm.class); + return new SyncopePage<>(result, pageable, count()); + } + + @Override + public S save(final S realm) { + String fullPathBefore = realm.getFullPath(); + String fullPathAfter = realm.getParent() == null + ? SyncopeConstants.ROOT_REALM + : StringUtils.appendIfMissing(realm.getParent().getFullPath(), "/") + realm.getName(); + if (!fullPathAfter.equals(fullPathBefore)) { + ((Neo4jRealm) realm).setFullPath(fullPathAfter); + } + + S merged = neo4jTemplate.save(nodeValidator.validate(realm)); + + if (!fullPathAfter.equals(fullPathBefore)) { + findChildren(merged).forEach(this::save); + } + + publisher.publishEvent( + new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, merged, AuthContextUtils.getDomain())); + + return merged; + } + + @Override + public void deleteById(final String key) { + findById(key).ifPresent(this::delete); + } + + @Override + public void delete(final Realm realm) { + if (realm == null || realm.getParent() == null) { + return; + } + + findDescendants(realm.getFullPath(), null, Pageable.unpaged()).forEach(toBeDeleted -> { + roleDAO.findByRealms(toBeDeleted).forEach(role -> role.getRealms().remove(toBeDeleted)); + + cascadeDelete( + Neo4jAnyTemplateRealm.NODE, + Neo4jRealm.NODE, + toBeDeleted.getKey(), Neo4jAnyTemplateRealm.class); + + toBeDeleted.setParent(null); + + neo4jTemplate.deleteById(toBeDeleted.getKey(), Neo4jRealm.class); + + publisher.publishEvent( + new EntityLifecycleEvent<>(this, SyncDeltaType.DELETE, toBeDeleted, AuthContextUtils.getDomain())); + }); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskDAO.java new file mode 100644 index 0000000000..75e324b3dc --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskDAO.java @@ -0,0 +1,598 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao; + +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.common.lib.to.PropagationTaskTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.ExecStatus; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.common.lib.types.TaskType; +import org.apache.syncope.core.persistence.api.dao.RealmDAO; +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.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.Notification; +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.PropagationTask; +import org.apache.syncope.core.persistence.api.entity.task.PullTask; +import org.apache.syncope.core.persistence.api.entity.task.PushTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.api.entity.task.Task; +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.persistence.neo4j.entity.Neo4jExternalResource; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementation; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; +import org.apache.syncope.core.persistence.neo4j.entity.task.AbstractTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jAnyTemplatePullTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jMacroTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jNotificationTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jPropagationTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jPropagationTaskExec; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jPullTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jPushTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jSchedTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jSchedTaskExec; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.apache.syncope.core.spring.security.SecurityProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +public class Neo4jTaskDAO extends AbstractDAO implements TaskDAO { + + protected static final Logger LOG = LoggerFactory.getLogger(TaskDAO.class); + + protected final RealmDAO realmDAO; + + protected final RemediationDAO remediationDAO; + + protected final TaskUtilsFactory taskUtilsFactory; + + protected final SecurityProperties securityProperties; + + protected final NodeValidator nodeValidator; + + public Neo4jTaskDAO( + final RealmDAO realmDAO, + final RemediationDAO remediationDAO, + final TaskUtilsFactory taskUtilsFactory, + final SecurityProperties securityProperties, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super(neo4jTemplate, neo4jClient); + this.realmDAO = realmDAO; + this.remediationDAO = remediationDAO; + this.taskUtilsFactory = taskUtilsFactory; + this.securityProperties = securityProperties; + this.nodeValidator = nodeValidator; + } + + @Override + public boolean existsById(final String key) { + return findById(key).isPresent(); + } + + @Transactional(readOnly = true) + @Override + @SuppressWarnings("unchecked") + public > Optional findById(final TaskType type, final String key) { + return neo4jTemplate.findById(key, (Class) taskUtilsFactory.getInstance(type).getTaskEntity()); + } + + @Transactional(readOnly = true) + @Override + @SuppressWarnings("unchecked") + public Optional findByName(final TaskType type, final String name) { + TaskUtils taskUtils = taskUtilsFactory.getInstance(type); + return neo4jClient.query( + "MATCH (n:" + taskUtils.getTaskTable() + ") WHERE n.name = $name RETURN n.id"). + bindAll(Map.of("name", name)).fetch().one(). + flatMap(toOptional("n.id", (Class>) taskUtils.getTaskEntity())); + } + + @Override + public Optional> findById(final String key) { + Optional> task = findById(TaskType.SCHEDULED, key); + if (task.isEmpty()) { + task = findById(TaskType.PULL, key); + } + if (task.isEmpty()) { + task = findById(TaskType.PUSH, key); + } + if (task.isEmpty()) { + task = findById(TaskType.MACRO, key); + } + if (task.isEmpty()) { + task = findById(TaskType.PROPAGATION, key); + } + if (task.isEmpty()) { + task = findById(TaskType.NOTIFICATION, key); + } + + return task; + } + + @Override + public List findByDelegate(final Implementation delegate) { + return findByRelationship( + Neo4jSchedTask.NODE, Neo4jSchedTaskExec.NODE, delegate.getKey(), Neo4jSchedTask.class); + } + + @Override + public List findByReconFilterBuilder(final Implementation reconFilterBuilder) { + return findByRelationship( + Neo4jPullTask.NODE, Neo4jImplementation.NODE, reconFilterBuilder.getKey(), Neo4jPullTask.class); + } + + @Override + public List findByPullActions(final Implementation pullActions) { + return findByRelationship( + Neo4jPullTask.NODE, Neo4jImplementation.NODE, pullActions.getKey(), Neo4jPullTask.class); + } + + @Override + public List findByPushActions(final Implementation pushActions) { + return findByRelationship( + Neo4jPushTask.NODE, Neo4jImplementation.NODE, pushActions.getKey(), Neo4jPushTask.class); + } + + @Override + public List findByRealm(final Realm realm) { + return findByRelationship(Neo4jMacroTask.NODE, Neo4jRealm.NODE, realm.getKey(), Neo4jMacroTask.class); + } + + @Override + public List findByCommand(final Implementation command) { + return findByRelationship( + Neo4jMacroTask.NODE, Neo4jImplementation.NODE, command.getKey(), Neo4jMacroTask.class); + } + + protected String execRelationship(final TaskType type) { + String result = null; + + switch (type) { + case NOTIFICATION: + result = Neo4jNotificationTask.NOTIFICATION_TASK_EXEC_REL; + break; + + case PROPAGATION: + result = Neo4jPropagationTask.PROPAGATION_TASK_EXEC_REL; + break; + + case PUSH: + result = Neo4jPushTask.PUSH_TASK_EXEC_REL; + break; + + case PULL: + result = Neo4jPullTask.PULL_TASK_EXEC_REL; + break; + + case MACRO: + result = Neo4jMacroTask.MACRO_TASK_EXEC_REL; + break; + + case SCHEDULED: + result = Neo4jSchedTask.SCHED_TASK_EXEC_REL; + break; + + default: + } + + return result; + } + + @Override + @SuppressWarnings("unchecked") + public > List findToExec(final TaskType type) { + TaskUtils taskUtils = taskUtilsFactory.getInstance(type); + StringBuilder queryString = new StringBuilder("MATCH (n:" + taskUtils.getTaskTable() + ") WHERE "); + + if (type == TaskType.NOTIFICATION) { + queryString.append("n.executed = false "); + } else { + queryString.append("(n)-[:").append(execRelationship(type)).append("]-() "); + } + queryString.append("RETURN n.id ORDER BY n.id DESC"); + + return toList(neo4jClient.query(queryString.toString()).fetch().all(), + "n.id", + (Class>) taskUtils.getTaskEntity()); + } + + @Override + public List> findAll() { + throw new UnsupportedOperationException(); + } + + @Transactional(readOnly = true) + @Override + public > List findAll(final TaskType type) { + return findAll(type, null, null, null, null, Pageable.unpaged()); + } + + protected StringBuilder query( + final TaskType type, + final TaskUtils taskUtils, + final ExternalResource resource, + final Notification notification, + final AnyTypeKind anyTypeKind, + final String entityKey, + final boolean orderByTaskExecInfo, + final Map parameters) { + + if (resource != null + && type != TaskType.PROPAGATION && type != TaskType.PUSH && type != TaskType.PULL) { + + throw new IllegalArgumentException(type + " is not related to " + ExternalResource.class.getSimpleName()); + } + + if ((anyTypeKind != null || entityKey != null) + && type != TaskType.PROPAGATION && type != TaskType.NOTIFICATION) { + + throw new IllegalArgumentException(type + " is not related to users, groups or any objects"); + } + + if (notification != null && type != TaskType.NOTIFICATION) { + throw new IllegalArgumentException(type + " is not related to notifications"); + } + + List> relationships = new ArrayList<>(); + List properties = new ArrayList<>(); + + if (orderByTaskExecInfo) { + relationships.add(Pair.of("(p:" + taskUtils.getTaskExecTable() + ")", "EXISTS((n)-[]-(p))")); + } + + if (resource != null) { + relationships.add(Pair.of("(e:" + Neo4jExternalResource.NODE + " {id: $eid})", "EXISTS((n)-[]-(e))")); + parameters.put("eid", resource.getKey()); + } + if (notification != null) { + relationships.add(Pair.of("(s:" + Neo4jExternalResource.NODE + " {id: $sid})", "EXISTS((n)-[]-(s))")); + parameters.put("sid", notification.getKey()); + } + if (anyTypeKind != null) { + properties.add("anyTypeKind: $anyTypeKind"); + parameters.put("anyTypeKind", anyTypeKind); + } + if (entityKey != null) { + properties.add("entityKey: $entityKey"); + parameters.put("entityKey", entityKey); + } + if (type == TaskType.MACRO + && !AuthContextUtils.getUsername().equals(securityProperties.getAdminUser())) { + + Stream realmKeys = AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.TASK_LIST).stream(). + map(realmDAO::findByFullPath). + filter(Optional::isPresent). + flatMap(r -> realmDAO.findDescendants(r.get().getFullPath(), null, Pageable.unpaged()).stream()). + map(Realm::getKey). + distinct(); + + AtomicInteger index = new AtomicInteger(0); + String realmCond = realmKeys.map(realm -> { + int idx = index.incrementAndGet(); + parameters.put("realm" + idx, realm); + return "q.id: $realm," + idx; + }).collect(Collectors.joining(" OR ")); + + relationships.add(Pair.of("(q:" + Neo4jRealm.NODE + ")", "EXISTS((n)-[]-(q) AND (" + realmCond + ")")); + } + + StringBuilder queryString = new StringBuilder("MATCH (n:").append(taskUtils.getTaskTable()).append(")"); + if (!relationships.isEmpty()) { + queryString.append(", "). + append(relationships.stream().map(Pair::getLeft).collect(Collectors.joining(", "))); + + properties.addAll(relationships.stream().map(Pair::getRight).toList()); + } + + if (type == TaskType.SCHEDULED) { + properties.add("NOT n:" + Neo4jMacroTask.NODE); + properties.add("NOT n:" + Neo4jPullTask.NODE); + properties.add("NOT n:" + Neo4jPushTask.NODE); + } + + if (!properties.isEmpty()) { + queryString.append(" WHERE ").append(properties.stream().collect(Collectors.joining(" AND "))); + } + + return queryString; + } + + @Override + public long count() { + throw new UnsupportedOperationException(); + } + + @Override + public long count( + final TaskType type, + final ExternalResource resource, + final Notification notification, + final AnyTypeKind anyTypeKind, + final String entityKey) { + + Map parameters = new HashMap<>(); + + StringBuilder queryString = query( + type, + taskUtilsFactory.getInstance(type), + resource, + notification, + anyTypeKind, + entityKey, + false, + parameters). + append(" RETURN COUNT(n)"); + return neo4jTemplate.count(queryString.toString(), parameters); + } + + protected String toOrderByStatement( + final Class> beanClass, + final Stream orderByClauses) { + + StringBuilder statement = new StringBuilder(); + + statement.append(" ORDER BY "); + + StringBuilder subStatement = new StringBuilder(); + orderByClauses.forEach(clause -> { + String field = clause.getProperty().trim(); + switch (field) { + case "latestExecStatus": + field = "status"; + break; + + case "start": + field = "startDate"; + break; + + case "end": + field = "endDate"; + break; + + default: + } + + subStatement.append("n.").append(field).append(' ').append(clause.getDirection().name()).append(','); + }); + + if (subStatement.length() == 0) { + statement.append("n.id DESC"); + } else { + subStatement.deleteCharAt(subStatement.length() - 1); + statement.append(subStatement); + } + + return statement.toString(); + } + + @SuppressWarnings("unchecked") + @Override + public > List findAll( + final TaskType type, + final ExternalResource resource, + final Notification notification, + final AnyTypeKind anyTypeKind, + final String entityKey, + final Pageable pageable) { + + TaskUtils taskUtils = taskUtilsFactory.getInstance(type); + + Map parameters = new HashMap<>(); + + boolean orderByTaskExecInfo = pageable.getSort().stream(). + anyMatch(clause -> clause.getProperty().equals("start") + || clause.getProperty().equals("end") + || clause.getProperty().equals("latestExecStatus") + || clause.getProperty().equals("status")); + + StringBuilder query = query( + type, + taskUtils, + resource, + notification, + anyTypeKind, + entityKey, + orderByTaskExecInfo, + parameters); + + query.append(" RETURN n.id "); + + if (orderByTaskExecInfo) { + // UNION with tasks without executions... + query.append("UNION "). + append(query( + type, + taskUtils, + resource, + notification, + anyTypeKind, + entityKey, + false, + parameters)). + append(" RETURN n.id "); + } + + query.append(toOrderByStatement(taskUtils.getTaskEntity(), pageable.getSort().get())); + + if (pageable.isPaged()) { + query.append(" SKIP ").append(pageable.getPageSize() * pageable.getPageNumber()). + append(" LIMIT ").append(pageable.getPageSize()); + } + + return toList(neo4jClient.query( + query.toString()).bindAll(parameters).fetch().all(), + "n.id", + (Class>) taskUtils.getTaskEntity()); + } + + @Transactional(rollbackFor = { Throwable.class }) + @Override + public > T save(final T task) { + task.getExecs().forEach(exec -> neo4jTemplate.save(nodeValidator.validate(exec))); + + switch (task) { + case Neo4jNotificationTask notificationTask -> + notificationTask.list2json(); + case Neo4jPushTask pushTask -> + pushTask.map2json(); + case Neo4jMacroTask macroTask -> + macroTask.list2json(); + default -> { + } + } + + T saved = neo4jTemplate.save(nodeValidator.validate(task)); + + switch (saved) { + case Neo4jNotificationTask notificationTask -> + notificationTask.postSave(); + case Neo4jPushTask pushTask -> + pushTask.postSave(); + case Neo4jMacroTask macroTask -> + macroTask.postSave(); + default -> { + } + } + + return saved; + } + + @Override + public void delete(final TaskType type, final String key) { + findById(type, key).ifPresent(this::delete); + } + + @Override + public void deleteById(final String key) { + findById(key).ifPresent(this::delete); + } + + @Override + public void delete(final Task task) { + if (task instanceof PullTask pullTask) { + remediationDAO.findByPullTask(pullTask).forEach(remediation -> remediation.setPullTask(null)); + pullTask.getTemplates(). + forEach(template -> neo4jTemplate.deleteById(template.getKey(), Neo4jAnyTemplatePullTask.class)); + } + + TaskUtils taskUtils = taskUtilsFactory.getInstance(task); + + task.getExecs().forEach(exec -> neo4jTemplate.deleteById(exec.getKey(), taskUtils.getTaskExecEntity())); + + neo4jTemplate.deleteById(task.getKey(), taskUtils.getTaskEntity()); + } + + @Override + public void deleteAll(final ExternalResource resource, final TaskType type) { + findAll(type, resource, null, null, null, Pageable.unpaged()). + stream().map(Task::getKey).forEach(key -> delete(type, key)); + } + + @Override + public List purgePropagations( + final OffsetDateTime since, + final List statuses, + final List resources) { + + Map parameters = new HashMap<>(); + + StringBuilder queryString = new StringBuilder( + "MATCH (n:" + Neo4jPropagationTask.NODE + ")-[]-" + + "(p:" + Neo4jPropagationTaskExec.NODE + ")"); + if (!CollectionUtils.isEmpty(resources)) { + queryString.append("-[]-(r:" + Neo4jExternalResource.NODE + ")"); + } + queryString.append(" WHERE 1=1 "); + + if (since != null) { + parameters.put("since", since); + queryString.append("AND p.enddate <= $since "); + } + if (!CollectionUtils.isEmpty(statuses)) { + AtomicInteger index = new AtomicInteger(0); + queryString.append("AND ("). + append(statuses.stream().map(status -> { + int idx = index.incrementAndGet(); + parameters.put("status" + idx, status.name()); + return "p.status = $status" + idx; + }).collect(Collectors.joining(" OR "))). + append(")"); + } + if (!CollectionUtils.isEmpty(resources)) { + queryString.append("AND ("). + append(resources.stream().map(r -> { + AtomicInteger index = new AtomicInteger(0); + int idx = index.incrementAndGet(); + parameters.put("r.id" + idx, r); + return "r.id = $r.id" + idx; + }).collect(Collectors.joining(" OR "))). + append(")"); + } + + queryString.append("RETURN n.id"); + + Stream keys = neo4jClient.query(queryString.toString()).bindAll(parameters).fetch().all().stream(). + map(found -> (String) found.get("n.id")).distinct(); + + List purged = new ArrayList<>(); + keys.forEach(key -> findById(TaskType.PROPAGATION, key).map(PropagationTask.class::cast).ifPresent(task -> { + PropagationTaskTO taskTO = new PropagationTaskTO(); + + taskTO.setOperation(task.getOperation()); + taskTO.setConnObjectKey(task.getConnObjectKey()); + taskTO.setOldConnObjectKey(task.getOldConnObjectKey()); + taskTO.setPropagationData(task.getSerializedPropagationData()); + taskTO.setResource(task.getResource().getKey()); + taskTO.setObjectClassName(task.getObjectClassName()); + taskTO.setAnyTypeKind(task.getAnyTypeKind()); + taskTO.setAnyType(task.getAnyType()); + taskTO.setEntityKey(task.getEntityKey()); + + purged.add(taskTO); + + delete(task); + })); + + return purged; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskExecDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskExecDAO.java new file mode 100644 index 0000000000..e28a5b2559 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskExecDAO.java @@ -0,0 +1,268 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao; + +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; +import org.apache.syncope.common.lib.types.TaskType; +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.task.Task; +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.persistence.neo4j.entity.task.AbstractTaskExec; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ReflectionUtils; + +public class Neo4jTaskExecDAO extends AbstractDAO implements TaskExecDAO { + + protected final TaskDAO taskDAO; + + protected final TaskUtilsFactory taskUtilsFactory; + + protected final NodeValidator nodeValidator; + + public Neo4jTaskExecDAO( + final TaskDAO taskDAO, + final TaskUtilsFactory taskUtilsFactory, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super(neo4jTemplate, neo4jClient); + this.taskDAO = taskDAO; + this.taskUtilsFactory = taskUtilsFactory; + this.nodeValidator = nodeValidator; + } + + @Override + public boolean existsById(final String key) { + return findById(key).isPresent(); + } + + @Override + @SuppressWarnings("unchecked") + public > Optional> findById(final TaskType type, final String key) { + return neo4jTemplate.findById(key, (Class>) taskUtilsFactory.getInstance(type).getTaskExecEntity()); + } + + @Override + public Optional> findById(final String key) { + Optional> task = findById(TaskType.SCHEDULED, key); + if (task.isEmpty()) { + task = findById(TaskType.PULL, key); + } + if (task.isEmpty()) { + task = findById(TaskType.PUSH, key); + } + if (task.isEmpty()) { + task = findById(TaskType.MACRO, key); + } + if (task.isEmpty()) { + task = findById(TaskType.PROPAGATION, key); + } + if (task.isEmpty()) { + task = findById(TaskType.NOTIFICATION, key); + } + + return task; + } + + @SuppressWarnings("unchecked") + protected > List> findRecent(final TaskType type, final int max) { + TaskUtils taskUtils = taskUtilsFactory.getInstance(type); + return toList(neo4jClient.query( + "MATCH (n:" + taskUtils.getTaskExecTable() + ")-[]-(p:" + taskUtils.getTaskTable() + " {id: $id}) " + + "WHERE n.endDate IS NOT NULL " + + "ORDER BY n.endDate DESC LIMIT " + max + " RETURN n.id").fetch().all(), + "n.id", + (Class>) taskUtils.getTaskExecEntity()); + } + + @Override + public List> findRecent(final int max) { + List> recent = new ArrayList<>(); + + for (TaskType taskType : TaskType.values()) { + recent.addAll(findRecent(taskType, max)); + } + + return recent.stream(). + sorted(Comparator.comparing(TaskExec::getEnd).reversed()). + limit(max). + toList(); + } + + @SuppressWarnings("unchecked") + protected Optional> findLatest(final TaskType type, final Task task, final String field) { + TaskUtils taskUtils = taskUtilsFactory.getInstance(type); + return neo4jClient.query( + "MATCH (n:" + taskUtils.getTaskExecTable() + ")-[]-(p:" + taskUtils.getTaskTable() + " {id: $id}) " + + "RETURN n.id ORDER BY n." + field + " DESC LIMIT 1"). + bindAll(Map.of("id", task.getKey())).fetch().one(). + flatMap(toOptional("n.id", (Class>) taskUtils.getTaskExecEntity())); + } + + @Override + public Optional> findLatestStarted(final TaskType type, final Task task) { + return findLatest(type, task, "startDate"); + } + + @Override + public Optional> findLatestEnded(final TaskType type, final Task task) { + return findLatest(type, task, "endDate"); + } + + @Override + public long count() { + throw new UnsupportedOperationException(); + } + + protected StringBuilder query( + final Task task, + final OffsetDateTime before, + final OffsetDateTime after, + final Map parameters) { + + TaskUtils taskUtils = taskUtilsFactory.getInstance(task); + + parameters.put("id", task.getKey()); + + StringBuilder query = new StringBuilder( + "MATCH (n:").append(taskUtils.getTaskExecTable()).append(")-[]-"). + append("(p:").append(taskUtils.getTaskTable()).append(" {id: $id}) "). + append("WHERE 1=1 "); + + if (before != null) { + query.append("AND n.startDate <= $before "); + parameters.put("before", before); + } + if (after != null) { + query.append("AND n.startDate >= $after "); + parameters.put("after", after); + } + + return query; + } + + @Override + public long count( + final Task task, + final OffsetDateTime before, + final OffsetDateTime after) { + + Map parameters = new HashMap<>(); + + StringBuilder queryString = query(task, before, after, parameters).append(" RETURN COUNT(n)"); + return neo4jTemplate.count(queryString.toString(), parameters); + } + + protected String toOrderByStatement(final Stream orderByClauses) { + StringBuilder statement = new StringBuilder(); + + orderByClauses.forEach(clause -> { + String field = clause.getProperty().trim(); + if (ReflectionUtils.findField(AbstractTaskExec.class, field) != null) { + statement.append("n.").append(field).append(' ').append(clause.getDirection().name()); + } + }); + + if (statement.length() == 0) { + statement.append("ORDER BY n.id DESC"); + } else { + statement.insert(0, "ORDER BY "); + } + return statement.toString(); + } + + @Override + public List> findAll() { + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("unchecked") + @Override + public List> findAll( + final Task task, + final OffsetDateTime before, + final OffsetDateTime after, + final Pageable pageable) { + + Map parameters = new HashMap<>(); + + StringBuilder queryString = query(task, before, after, parameters). + append(" RETURN n.id ").append(toOrderByStatement(pageable.getSort().stream())); + if (pageable.isPaged()) { + queryString.append(" SKIP ").append(pageable.getPageSize() * pageable.getPageNumber()). + append(" LIMIT ").append(pageable.getPageSize()); + } + + return toList(neo4jClient.query( + queryString.toString()).bindAll(parameters).fetch().all(), + "n.id", + (Class>) taskUtilsFactory.getInstance(task).getTaskExecEntity()); + } + + @Transactional(rollbackFor = { Throwable.class }) + @Override + public > S save(final S execution) { + return neo4jTemplate.save(nodeValidator.validate(execution)); + } + + @Transactional(rollbackFor = { Throwable.class }) + @Override + public > void saveAndAdd( + final TaskType taskType, final String taskKey, final TaskExec execution) { + + Optional task = taskDAO.findById(taskType, taskKey); + if (task.isPresent()) { + task.get().add(execution); + taskDAO.save(task.get()); + } + } + + @Override + public > void delete(final TaskType taskType, final String key) { + findById(taskType, key).ifPresent(this::delete); + } + + @Override + public void delete(final TaskExec execution) { + Optional.ofNullable(execution.getTask()).ifPresent(task -> task.getExecs().remove(execution)); + + neo4jTemplate.deleteById(execution.getKey(), execution.getClass()); + } + + @Override + public void deleteById(final String key) { + findById(key).ifPresent(this::delete); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractAnyRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractAnyRepoExt.java new file mode 100644 index 0000000000..8e45e22ef3 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractAnyRepoExt.java @@ -0,0 +1,402 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import org.apache.commons.jexl3.parser.Parser; +import org.apache.commons.jexl3.parser.ParserConstants; +import org.apache.commons.jexl3.parser.Token; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.core.persistence.api.dao.AllowedSchemas; +import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; +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.Any; +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.DerSchema; +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.PlainAttrUniqueValue; +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.Schema; +import org.apache.syncope.core.persistence.api.entity.VirSchema; +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.neo4j.dao.AbstractDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDynRealm; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jPlainAttr; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +public abstract class AbstractAnyRepoExt> extends AbstractDAO implements AnyRepoExt { + + protected static final Logger LOG = LoggerFactory.getLogger(AnyRepoExt.class); + + /** + * Split an attribute value recurring on provided literals/tokens. + * + * @param attrValue value to be split + * @param literals literals/tokens + * @return split value + */ + protected static List split(final String attrValue, final List literals) { + final List attrValues = new ArrayList<>(); + + if (literals.isEmpty()) { + attrValues.add(attrValue); + } else { + for (String token : attrValue.split(Pattern.quote(literals.get(0)))) { + if (!token.isEmpty()) { + attrValues.addAll(split(token, literals.subList(1, literals.size()))); + } + } + } + + return attrValues; + } + + protected final PlainSchemaDAO plainSchemaDAO; + + protected final DerSchemaDAO derSchemaDAO; + + protected final DynRealmDAO dynRealmDAO; + + protected final AnyUtils anyUtils; + + protected AbstractAnyRepoExt( + final PlainSchemaDAO plainSchemaDAO, + final DerSchemaDAO derSchemaDAO, + final DynRealmDAO dynRealmDAO, + final AnyUtils anyUtils, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient) { + + super(neo4jTemplate, neo4jClient); + this.plainSchemaDAO = plainSchemaDAO; + this.derSchemaDAO = derSchemaDAO; + this.dynRealmDAO = dynRealmDAO; + this.anyUtils = anyUtils; + } + + @Override + public List findByKeys(final List keys) { + return toList(neo4jClient.query( + "MATCH (n:" + AnyRepoExt.node(anyUtils.anyTypeKind()) + ") WHERE n.id IN $keys RETURN n.id"). + bindAll(Map.of("keys", keys)).fetch().all(), + "n.id", + anyUtils.anyClass()); + } + + protected Optional findLastChange(final String key, final String node) { + return neo4jClient.query("MATCH (n:" + node + " {id: $id}) " + + "RETURN n.creationDate, n.lastChangeDate"). + bindAll(Map.of("id", key)).fetch().one().map(n -> { + OffsetDateTime creationDate = (OffsetDateTime) n.get("n.creationDate"); + OffsetDateTime lastChangeDate = (OffsetDateTime) n.get("n.lastChangeDate"); + return Optional.ofNullable(lastChangeDate).orElse(creationDate); + }); + } + + protected abstract void securityChecks(A any); + + protected Optional findById(final String key) { + return neo4jTemplate.findById(key, anyUtils.anyClass()); + } + + @Transactional(readOnly = true) + @Override + public A authFind(final String key) { + if (key == null) { + throw new NotFoundException("Null key"); + } + + A any = findById(key).orElseThrow(() -> new NotFoundException(anyUtils.anyTypeKind().name() + ' ' + key)); + + securityChecks(any); + + return any; + } + + @Override + @SuppressWarnings("unchecked") + public List findByPlainAttrValue( + final PlainSchema schema, + final PlainAttrValue attrValue, + final boolean ignoreCaseMatch) { + + if (schema == null) { + LOG.error("No PlainSchema"); + return List.of(); + } + + PlainAttr attr = anyUtils.newPlainAttr(); + attr.setSchema(schema); + if (attrValue instanceof PlainAttrUniqueValue plainAttrUniqueValue) { + attr.setUniqueValue(plainAttrUniqueValue); + } else { + ((Neo4jPlainAttr) attr).add(attrValue); + } + + String op; + Map parameters; + if (ignoreCaseMatch) { + op = "=~"; + parameters = Map.of("value", "(?i)" + AnyRepoExt.escapeForLikeRegex(POJOHelper.serialize(attr))); + } else { + op = "="; + parameters = Map.of("value", POJOHelper.serialize(attr)); + } + return toList( + neo4jClient.query( + "MATCH (n:" + AnyRepoExt.node(anyUtils.anyTypeKind()) + ") " + + "WHERE n.`plainAttrs." + schema.getKey() + "` " + op + " $value RETURN n.id"). + bindAll(parameters).fetch().all(), + "n.id", + anyUtils.anyClass()); + } + + @Override + public Optional findByPlainAttrUniqueValue( + final PlainSchema schema, + final PlainAttrUniqueValue attrUniqueValue, + final boolean ignoreCaseMatch) { + + if (schema == null) { + LOG.error("No PlainSchema"); + return Optional.empty(); + } + if (!schema.isUniqueConstraint()) { + LOG.error("This schema has not unique constraint: '{}'", schema.getKey()); + return Optional.empty(); + } + + List result = findByPlainAttrValue(schema, attrUniqueValue, ignoreCaseMatch); + return result.isEmpty() + ? Optional.empty() + : Optional.of(result.get(0)); + } + + @Override + public List findByDerAttrValue(final DerSchema derSchema, final String value, final boolean ignoreCaseMatch) { + if (derSchema == null) { + LOG.error("No DerSchema"); + return List.of(); + } + + Parser parser = new Parser(derSchema.getExpression()); + + // Schema keys + List identifiers = new ArrayList<>(); + + // Literals + List literals = new ArrayList<>(); + + // Get schema keys and literals + for (Token token = parser.getNextToken(); token != null && StringUtils.isNotBlank(token.toString()); + token = parser.getNextToken()) { + + if (token.kind == ParserConstants.STRING_LITERAL) { + literals.add(token.toString().substring(1, token.toString().length() - 1)); + } + + if (token.kind == ParserConstants.IDENTIFIER) { + identifiers.add(token.toString()); + } + } + + // Sort literals in order to process later literals included into others + literals.sort((l1, l2) -> { + if (l1 == null && l2 == null) { + return 0; + } else if (l1 != null && l2 == null) { + return -1; + } else if (l1 == null) { + return 1; + } else if (l1.length() == l2.length()) { + return 0; + } else if (l1.length() > l2.length()) { + return -1; + } else { + return 1; + } + }); + + // Split value on provided literals + List attrValues = split(value, literals); + + if (attrValues.size() != identifiers.size()) { + LOG.error("Ambiguous JEXL expression resolution: literals and values have different size"); + return List.of(); + } + + // Contains used identifiers in order to avoid replications + Set used = new HashSet<>(); + + // Create several clauses: one for eanch identifiers + List clauses = new ArrayList<>(); + Map parameters = new HashMap<>(); + for (int i = 0; i < identifiers.size(); i++) { + if (!used.contains(identifiers.get(i))) { + // verify schema existence and get schema type + PlainSchema schema = plainSchemaDAO.findById(identifiers.get(i)).orElse(null); + + if (schema == null) { + LOG.error("Invalid schema '{}', ignoring", identifiers.get(i)); + } else { + PlainAttr attr = anyUtils.newPlainAttr(); + attr.setSchema(schema); + if (schema.isUniqueConstraint()) { + PlainAttrUniqueValue attrValue = anyUtils.newPlainAttrUniqueValue(); + attrValue.setStringValue(attrValues.get(i)); + attr.setUniqueValue(attrValue); + } else { + PlainAttrValue attrValue = anyUtils.newPlainAttrValue(); + attrValue.setStringValue(attrValues.get(i)); + ((Neo4jPlainAttr) attr).add(attrValue); + } + + String op; + if (ignoreCaseMatch) { + op = "=~"; + parameters.put( + identifiers.get(i), + "(?i)" + AnyRepoExt.escapeForLikeRegex(POJOHelper.serialize(attr))); + } else { + op = "="; + parameters.put(identifiers.get(i), POJOHelper.serialize(attr)); + } + clauses.add("n.`plainAttrs." + schema.getKey() + "` " + op + " $" + identifiers.get(i)); + + used.add(identifiers.get(i)); + } + } + } + + LOG.debug("Generated where clauses {}", clauses); + + return toList( + neo4jClient.query( + "MATCH (n:" + AnyRepoExt.node(anyUtils.anyTypeKind()) + ") " + + "WHERE " + clauses.stream().collect(Collectors.joining(" AND ")) + + " RETURN n.id"). + bindAll(parameters).fetch().all(), + "n.id", + anyUtils.anyClass()); + } + + @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Override + @SuppressWarnings("unchecked") + public AllowedSchemas findAllowedSchemas(final A any, final Class reference) { + AllowedSchemas result = new AllowedSchemas<>(); + + // schemas given by type and aux classes + Set typeOwnClasses = new HashSet<>(); + typeOwnClasses.addAll(any.getType().getClasses()); + typeOwnClasses.addAll(any.getAuxClasses()); + + typeOwnClasses.forEach(typeClass -> { + if (reference.equals(PlainSchema.class)) { + result.getForSelf().addAll((Collection) typeClass.getPlainSchemas()); + } else if (reference.equals(DerSchema.class)) { + result.getForSelf().addAll((Collection) typeClass.getDerSchemas()); + } else if (reference.equals(VirSchema.class)) { + result.getForSelf().addAll((Collection) typeClass.getVirSchemas()); + } + }); + + // schemas given by type extensions + Map> typeExtensionClasses = new HashMap<>(); + switch (any) { + case User user -> + user.getMemberships().forEach(memb -> memb.getRightEnd().getTypeExtensions(). + forEach(typeExt -> typeExtensionClasses.put(memb.getRightEnd(), typeExt.getAuxClasses()))); + case AnyObject anyObject -> + anyObject.getMemberships().forEach(memb -> memb.getRightEnd().getTypeExtensions().stream(). + filter(typeExt -> any.getType().equals(typeExt.getAnyType())). + forEach(typeExt -> typeExtensionClasses.put(memb.getRightEnd(), typeExt.getAuxClasses()))); + default -> { + } + } + + typeExtensionClasses.entrySet().stream().map(entry -> { + result.getForMemberships().put(entry.getKey(), new HashSet<>()); + return entry; + }).forEach(entry -> entry.getValue().forEach(typeClass -> { + if (reference.equals(PlainSchema.class)) { + result.getForMemberships().get(entry.getKey()). + addAll((Collection) typeClass.getPlainSchemas()); + } else if (reference.equals(DerSchema.class)) { + result.getForMemberships().get(entry.getKey()). + addAll((Collection) typeClass.getDerSchemas()); + } else if (reference.equals(VirSchema.class)) { + result.getForMemberships().get(entry.getKey()). + addAll((Collection) typeClass.getVirSchemas()); + } + })); + + return result; + } + + @Transactional(readOnly = true) + @Override + public List findDynRealms(final String key) { + return neo4jClient.query( + "MATCH (n {id: $id})-" + + "[:" + DynRealmRepoExt.DYN_REALM_MEMBERSHIP_REL + "]-" + + "(p:" + Neo4jDynRealm.NODE + ") " + + "RETURN p.id"). + bindAll(Map.of("id", key)).fetch().all().stream(). + map(found -> found.get("p.id").toString()).distinct().toList(); + } + + @Override + public List findByResourcesContaining(final ExternalResource resource) { + return findByRelationship( + AnyRepoExt.node(anyUtils.anyTypeKind()), + Neo4jExternalResource.NODE, + resource.getKey(), + anyUtils.anyClass()); + } + + @Override + public void deleteById(final String key) { + findById(key).ifPresent(this::delete); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractClientRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractClientRepoExt.java new file mode 100644 index 0000000000..b7bdb6127e --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractClientRepoExt.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.persistence.neo4j.dao.repo; + +import java.util.List; +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.am.ClientApp; +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.Policy; +import org.apache.syncope.core.persistence.neo4j.dao.AbstractDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; +import org.apache.syncope.core.persistence.neo4j.entity.am.AbstractClientApp; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jCASSPClientApp; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jOIDCRPClientApp; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jSAML2SPClientApp; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPolicy; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public abstract class AbstractClientRepoExt + extends AbstractDAO + implements ClientAppRepoExt { + + protected static Class toDomainType(final Class clientAppClass) { + if (CASSPClientApp.class.isAssignableFrom(clientAppClass)) { + return Neo4jCASSPClientApp.class; + } + if (OIDCRPClientApp.class.isAssignableFrom(clientAppClass)) { + return Neo4jOIDCRPClientApp.class; + } + if (SAML2SPClientApp.class.isAssignableFrom(clientAppClass)) { + return Neo4jSAML2SPClientApp.class; + } + throw new IllegalArgumentException("Unexpected client app class: " + clientAppClass.getName()); + } + + protected AbstractClientRepoExt(final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient) { + super(neo4jTemplate, neo4jClient); + } + + protected List findAllByPolicy( + final Policy policy, + final String clientAppNode, + final Class clientAppClass) { + + return findByRelationship(clientAppNode, Neo4jPolicy.NODE, policy.getKey(), toDomainType(clientAppClass)); + } + + protected List findAllByRealm( + final Realm realm, + final String clientAppNode, + final Class clientAppClass) { + + return findByRelationship(clientAppNode, Neo4jRealm.NODE, realm.getKey(), toDomainType(clientAppClass)); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractSchemaRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractSchemaRepoExt.java new file mode 100644 index 0000000000..ab9c32a3a5 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractSchemaRepoExt.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.persistence.neo4j.dao.repo; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.Schema; +import org.apache.syncope.core.persistence.neo4j.dao.AbstractDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyTypeClass; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDerSchema; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jPlainSchema; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jSchema; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jVirSchema; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public abstract class AbstractSchemaRepoExt extends AbstractDAO { + + protected final NodeValidator nodeValidator; + + public AbstractSchemaRepoExt( + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super(neo4jTemplate, neo4jClient); + this.nodeValidator = nodeValidator; + } + + protected List findByAnyTypeClasses( + final Collection anyTypeClasses, + final Class domainType, + final Class reference) { + + if (anyTypeClasses.isEmpty()) { + return List.of(); + } + + String label = null; + String relationship = null; + if (domainType.equals(Neo4jPlainSchema.class)) { + label = Neo4jPlainSchema.NODE; + relationship = Neo4jAnyTypeClass.ANY_TYPE_CLASS_PLAIN_REL; + } else if (domainType.equals(Neo4jDerSchema.class)) { + label = Neo4jDerSchema.NODE; + relationship = Neo4jAnyTypeClass.ANY_TYPE_CLASS_DER_REL; + } else if (domainType.equals(Neo4jVirSchema.class)) { + label = Neo4jVirSchema.NODE; + relationship = Neo4jAnyTypeClass.ANY_TYPE_CLASS_VIR_REL; + } + + List clauses = new ArrayList<>(); + Map parameters = new HashMap<>(); + int clausesIdx = 0; + for (AnyTypeClass anyTypeClass : anyTypeClasses) { + clauses.add("a.id = $id" + (clausesIdx + 1)); + parameters.put("id" + (clausesIdx + 1), anyTypeClass.getKey()); + clausesIdx++; + } + + return toList(neo4jClient.query( + "MATCH (n:" + label + ")-[:" + relationship + "]-(a:" + Neo4jAnyTypeClass.NODE + ") " + + "WHERE (" + clauses.stream().collect(Collectors.joining(" OR ")) + ") " + + "RETURN n.id").bindAll(parameters).fetch().all(), + "n.id", + domainType); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AccessTokenRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AccessTokenRepo.java new file mode 100644 index 0000000000..09880a4948 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AccessTokenRepo.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.persistence.neo4j.dao.repo; + +import java.time.OffsetDateTime; +import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAccessToken; +import org.springframework.data.neo4j.repository.query.Query; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.repository.query.Param; + +public interface AccessTokenRepo + extends PagingAndSortingRepository, AccessTokenDAO { + + @Query("MATCH (n:" + Neo4jAccessToken.NODE + " WHERE n.expirationTime < $now) " + + "DETACH DELETE n " + + "RETURN count(*)") + @Override + int deleteExpired(@Param("now") OffsetDateTime now); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepo.java new file mode 100644 index 0000000000..ab6869549c --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepo.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.core.persistence.neo4j.dao.repo; + +import java.util.Optional; +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyType; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAnyObject; +import org.springframework.data.neo4j.repository.query.Query; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.repository.query.Param; + +public interface AnyObjectRepo + extends PagingAndSortingRepository, AnyObjectRepoExt, AnyObjectDAO { + + @Query("MATCH (n:" + Neo4jAnyObject.NODE + ")-[]-(a:" + Neo4jAnyType.NODE + " {id: $type}) " + + "WHERE n.name = $name " + + "RETURN n.id") + @Override + Optional findKey(@Param("type") String type, @Param("name") String name); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepoExt.java new file mode 100644 index 0000000000..3fcd34da69 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepoExt.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.persistence.neo4j.dao.repo; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import org.apache.commons.lang3.tuple.Pair; +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.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.Relationship; +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.Group; + +public interface AnyObjectRepoExt extends AnyRepoExt { + + Optional findByName(String type, String name); + + List findByName(String name); + + void securityChecks(Set authRealms, String key, String realm, Collection groups); + + Map countByType(); + + Map countByRealm(AnyType anyType); + + AMembership findMembership(String key); + + List findDynGroups(String key); + + List, AnyObject>> findAllRelationships(AnyObject anyObject); + + Collection findAllGroups(AnyObject anyObject); + + Collection findAllGroupKeys(AnyObject anyObject); + + Collection findAllResources(AnyObject anyObject); + + Pair, Set> saveAndGetDynGroupMembs(AnyObject anyObject); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepoExtImpl.java new file mode 100644 index 0000000000..856a606d37 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepoExtImpl.java @@ -0,0 +1,327 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.common.lib.types.AnyEntitlement; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; +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.Any; +import org.apache.syncope.core.persistence.api.entity.AnyType; +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.Relationship; +import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership; +import org.apache.syncope.core.persistence.api.entity.anyobject.ARelationship; +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.URelationship; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyType; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAMembership; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jARelationship; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAnyObject; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jURelationship; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.apache.syncope.core.spring.security.DelegatedAdministrationException; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +public class AnyObjectRepoExtImpl extends AbstractAnyRepoExt implements AnyObjectRepoExt { + + protected final UserDAO userDAO; + + protected final GroupDAO groupDAO; + + protected final NodeValidator nodeValidator; + + public AnyObjectRepoExtImpl( + final AnyUtilsFactory anyUtilsFactory, + final PlainSchemaDAO plainSchemaDAO, + final DerSchemaDAO derSchemaDAO, + final DynRealmDAO dynRealmDAO, + final UserDAO userDAO, + final GroupDAO groupDAO, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super( + plainSchemaDAO, + derSchemaDAO, + dynRealmDAO, + anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT), + neo4jTemplate, + neo4jClient); + this.userDAO = userDAO; + this.groupDAO = groupDAO; + this.nodeValidator = nodeValidator; + } + + @Override + public Optional findByName(final String type, final String name) { + return neo4jClient.query( + "MATCH (n:" + Neo4jAnyObject.NODE + ")-[]-(a:" + Neo4jAnyType.NODE + " {id: $type}) " + + "WHERE n.name = $name " + + "RETURN n.id"). + bindAll(Map.of("type", type, "name", name)).fetch().one(). + flatMap(toOptional("n.id", Neo4jAnyObject.class)); + } + + @Override + public List findByName(final String name) { + return toList(neo4jClient.query( + "MATCH (n:" + Neo4jAnyObject.NODE + ") WHERE n.name = $name RETURN n.id"). + bindAll(Map.of("name", name)).fetch().all(), + "n.id", + Neo4jAnyObject.class); + } + + @Transactional(readOnly = true) + @Override + public Optional findLastChange(final String key) { + return findLastChange(key, Neo4jAnyObject.NODE); + } + + @Transactional(readOnly = true) + @Override + public void securityChecks( + final Set authRealms, + final String key, + final String realm, + final Collection groups) { + + // 1. check if AuthContextUtils.getUsername() is owner of at least one group of which anyObject is member + boolean authorized = authRealms.stream(). + map(authRealm -> RealmUtils.parseGroupOwnerRealm(authRealm).orElse(null)). + filter(Objects::nonNull). + anyMatch(pair -> groups.contains(pair.getRight())); + + // 2. check if anyObject is in at least one DynRealm for which AuthContextUtils.getUsername() owns entitlement + if (!authorized) { + authorized = findDynRealms(key).stream().anyMatch(authRealms::contains); + } + + // 3. check if anyObject is in Realm (or descendants) for which AuthContextUtils.getUsername() owns entitlement + if (!authorized) { + authorized = authRealms.stream().anyMatch(realm::startsWith); + } + + if (!authorized) { + throw new DelegatedAdministrationException(realm, AnyTypeKind.ANY_OBJECT.name(), key); + } + } + + @Override + protected void securityChecks(final AnyObject anyObject) { + Set authRealms = AuthContextUtils.getAuthorizations(). + getOrDefault(AnyEntitlement.READ.getFor(anyObject.getType().getKey()), Set.of()); + + securityChecks(authRealms, anyObject.getKey(), anyObject.getRealm().getFullPath(), findAllGroupKeys(anyObject)); + } + + @Override + public Map countByType() { + Collection> result = neo4jClient.query( + "MATCH (n:" + Neo4jAnyObject.NODE + ")-[]-(a:" + Neo4jAnyType.NODE + ") " + + "RETURN a.id, COUNT(n) AS counted").fetch().all(); + + return result.stream().collect(Collectors.toMap( + r -> neo4jTemplate.findById(r.get("a.id"), Neo4jAnyType.class).orElseThrow(), + r -> (Long) r.get("counted"))); + } + + @Override + public Map countByRealm(final AnyType anyType) { + Collection> result = neo4jClient.query( + "MATCH (r:" + Neo4jRealm.NODE + ")-[]-" + + "(n:" + Neo4jAnyObject.NODE + ")-[]-" + + "(a:" + Neo4jAnyType.NODE + " {id: $aid}) " + + "RETURN r.fullPath AS realm, COUNT(n) AS counted"). + bindAll(Map.of("aid", anyType.getKey())).fetch().all(); + + return result.stream().collect(Collectors.toMap(r -> r.get("realm").toString(), r -> (Long) r.get("counted"))); + } + + @Override + public AMembership findMembership(final String key) { + return neo4jTemplate.findById(key, Neo4jAMembership.class).orElse(null); + } + + @Override + public List, AnyObject>> findAllRelationships(final AnyObject anyObject) { + List, AnyObject>> result = new ArrayList<>(); + + result.addAll(toList(neo4jClient.query( + "MATCH (n:" + Neo4jARelationship.NODE + ")-[]-(a:" + Neo4jAnyObject.NODE + " {id: $aid}) " + + "RETURN n.id").bindAll(Map.of("aid", anyObject.getKey())).fetch().all(), + "n.id", + Neo4jARelationship.class)); + + result.addAll(toList(neo4jClient.query( + "MATCH (n:" + Neo4jURelationship.NODE + ")-[]-(a:" + Neo4jAnyObject.NODE + " {id: $aid}) " + + "RETURN n.id").bindAll(Map.of("aid", anyObject.getKey())).fetch().all(), + "n.id", + Neo4jURelationship.class)); + + return result; + } + + protected Pair, Set>> doSave(final AnyObject anyObject) { + // delete any membership, relationship or linked account that was removed from anyObject + neo4jTemplate.findById(anyObject.getKey(), Neo4jAnyObject.class).ifPresent(before -> { + Set beforeMembs = before.getMemberships().stream().map(AMembership::getKey). + collect(Collectors.toSet()); + beforeMembs.removeAll(anyObject.getMemberships().stream().map(AMembership::getKey).toList()); + beforeMembs.forEach(m -> neo4jTemplate.deleteById(m, Neo4jAMembership.class)); + + Set beforeRels = before.getRelationships().stream().map(ARelationship::getKey). + collect(Collectors.toSet()); + beforeRels.removeAll(anyObject.getRelationships().stream().map(ARelationship::getKey).toList()); + beforeRels.forEach(r -> neo4jTemplate.deleteById(r, Neo4jARelationship.class)); + }); + + AnyObject merged = neo4jTemplate.save(nodeValidator.validate(anyObject)); + + Pair, Set> dynGroupMembs = groupDAO.refreshDynMemberships(merged); + dynRealmDAO.refreshDynMemberships(merged); + + return Pair.of(merged, dynGroupMembs); + } + + @Override + @SuppressWarnings("unchecked") + public S save(final S anyObject) { + return (S) doSave(anyObject).getLeft(); + } + + @Override + public Pair, Set> saveAndGetDynGroupMembs(final AnyObject anyObject) { + return doSave(anyObject).getRight(); + } + + protected List findARelationships(final AnyObject anyObject) { + return findByRelationship( + Neo4jARelationship.NODE, + Neo4jAnyObject.NODE, + anyObject.getKey(), + Neo4jARelationship.DEST_REL, + Neo4jARelationship.class); + } + + protected List findURelationships(final AnyObject anyObject) { + return findByRelationship( + Neo4jURelationship.NODE, + Neo4jAnyObject.NODE, + anyObject.getKey(), + Neo4jURelationship.DEST_REL, + Neo4jURelationship.class); + } + + @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Override + public List findDynGroups(final String key) { + return toList(neo4jClient.query( + "MATCH (n:" + Neo4jAnyObject.NODE + " {id: $id})-" + + "[:" + GroupRepoExt.DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]-" + + "(p:" + Neo4jGroup.NODE + ") " + + "RETURN p.id").bindAll(Map.of("id", key)).fetch().all(), + "p.id", + Neo4jGroup.class); + } + + @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Override + public Collection findAllGroups(final AnyObject anyObject) { + Set result = new HashSet<>(); + result.addAll(anyObject.getMemberships().stream(). + map(AMembership::getRightEnd).collect(Collectors.toSet())); + result.addAll(findDynGroups(anyObject.getKey())); + + return result; + } + + @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Override + public Collection findAllGroupKeys(final AnyObject anyObject) { + return findAllGroups(anyObject).stream().map(Group::getKey).toList(); + } + + @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Override + public Collection findAllResources(final AnyObject anyObject) { + Set result = new HashSet<>(); + result.addAll(anyObject.getResources()); + findAllGroups(anyObject).forEach(group -> result.addAll(group.getResources())); + + return result; + } + + @Transactional(readOnly = true) + @Override + public Collection findAllResourceKeys(final String key) { + return findAllResources(authFind(key)).stream().map(ExternalResource::getKey).toList(); + } + + @Override + public void delete(final AnyObject anyObject) { + groupDAO.removeDynMemberships(anyObject); + dynRealmDAO.removeDynMemberships(anyObject.getKey()); + + findARelationships(anyObject).forEach(relationship -> { + relationship.getLeftEnd().getRelationships().remove(relationship); + save(relationship.getLeftEnd()); + + neo4jTemplate.deleteById(relationship.getKey(), Neo4jARelationship.class); + }); + findURelationships(anyObject).forEach(relationship -> { + relationship.getLeftEnd().getRelationships().remove(relationship); + userDAO.save(relationship.getLeftEnd()); + + neo4jTemplate.deleteById(relationship.getKey(), Neo4jURelationship.class); + }); + + cascadeDelete( + Neo4jAMembership.NODE, + Neo4jAnyObject.NODE, + anyObject.getKey(), + Neo4jAMembership.class); + + neo4jTemplate.deleteById(anyObject.getKey(), Neo4jAnyObject.class); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyRepoExt.java new file mode 100644 index 0000000000..6de447549f --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyRepoExt.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.persistence.neo4j.dao.repo; + +import java.time.OffsetDateTime; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.dao.AllowedSchemas; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.DerSchema; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; +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.Schema; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAnyObject; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; + +public interface AnyRepoExt> { + + String REGEX_CHARS = "{}[]()?+*.\"'"; + + static String escapeForLikeRegex(final String input) { + String output = input; + for (char toEscape : REGEX_CHARS.toCharArray()) { + output = output.replace(String.valueOf(toEscape), "\\" + toEscape); + } + return output; + } + + static String node(final AnyTypeKind anyTypeKind) { + return switch (anyTypeKind) { + case USER -> + Neo4jUser.NODE; + case GROUP -> + Neo4jGroup.NODE; + case ANY_OBJECT -> + Neo4jAnyObject.NODE; + default -> + ""; + }; + } + + List findByKeys(List keys); + + Optional findLastChange(String key); + + A authFind(String key); + + List findByPlainAttrValue(PlainSchema schema, PlainAttrValue attrValue, boolean ignoreCaseMatch); + + Optional findByPlainAttrUniqueValue( + PlainSchema schema, PlainAttrUniqueValue attrUniqueValue, boolean ignoreCaseMatch); + + /** + * Find any objects by derived attribute value. This method could fail if one or more string literals contained + * into the derived attribute value provided derive from identifier (schema key) replacement. When you are going to + * specify a derived attribute expression you must be quite sure that string literals used to build the expression + * cannot be found into the attribute values used to replace attribute schema keys used as identifiers. + * + * @param schema derived schema + * @param value derived attribute value + * @param ignoreCaseMatch whether comparison for string values should take case into account or not + * @return list of any objects + */ + List findByDerAttrValue(DerSchema schema, String value, boolean ignoreCaseMatch); + + AllowedSchemas findAllowedSchemas(A any, Class reference); + + List findDynRealms(String key); + + Collection findAllResourceKeys(String key); + + List findByResourcesContaining(ExternalResource resource); + + S save(S any); + + void deleteById(String key); + + void delete(A any); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeClassRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeClassRepo.java new file mode 100644 index 0000000000..92e64c48ea --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeClassRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyTypeClass; +import org.springframework.data.repository.ListCrudRepository; + +public interface AnyTypeClassRepo + extends ListCrudRepository, AnyTypeClassRepoExt, AnyTypeClassDAO { + +} diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/DynRoleMembership.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeClassRepoExt.java similarity index 72% rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/DynRoleMembership.java rename to core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeClassRepoExt.java index 5b253d87d4..11ceda5900 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/DynRoleMembership.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeClassRepoExt.java @@ -16,14 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.api.entity.user; +package org.apache.syncope.core.persistence.neo4j.dao.repo; -import org.apache.syncope.core.persistence.api.entity.DynMembership; -import org.apache.syncope.core.persistence.api.entity.Role; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; -public interface DynRoleMembership extends DynMembership { +public interface AnyTypeClassRepoExt { - Role getRole(); + AnyTypeClass save(AnyTypeClass anyTypeClass); - void setRole(Role role); + void deleteById(String key); } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeClassRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeClassRepoExtImpl.java new file mode 100644 index 0000000000..bac2912ada --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeClassRepoExtImpl.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.persistence.neo4j.dao.repo; + +import java.util.List; +import org.apache.syncope.common.lib.to.Provision; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; +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.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO; +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.DerSchema; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.VirSchema; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyTypeClass; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class AnyTypeClassRepoExtImpl implements AnyTypeClassRepoExt { + + protected final AnyTypeDAO anyTypeDAO; + + protected final PlainSchemaDAO plainSchemaDAO; + + protected final DerSchemaDAO derSchemaDAO; + + protected final VirSchemaDAO virSchemaDAO; + + protected final GroupDAO groupDAO; + + protected final ExternalResourceDAO resourceDAO; + + protected final Neo4jTemplate neo4jTemplate; + + protected final NodeValidator nodeValidator; + + public AnyTypeClassRepoExtImpl( + final AnyTypeDAO anyTypeDAO, + final PlainSchemaDAO plainSchemaDAO, + final DerSchemaDAO derSchemaDAO, + final VirSchemaDAO virSchemaDAO, + final GroupDAO groupDAO, + final ExternalResourceDAO resourceDAO, + final Neo4jTemplate neo4jTemplate, + final NodeValidator nodeValidator) { + + this.anyTypeDAO = anyTypeDAO; + this.plainSchemaDAO = plainSchemaDAO; + this.derSchemaDAO = derSchemaDAO; + this.virSchemaDAO = virSchemaDAO; + this.groupDAO = groupDAO; + this.resourceDAO = resourceDAO; + this.neo4jTemplate = neo4jTemplate; + this.nodeValidator = nodeValidator; + } + + @Override + public AnyTypeClass save(final AnyTypeClass anyTypeClass) { + AnyTypeClass merge = neo4jTemplate.save(nodeValidator.validate(anyTypeClass)); + + for (PlainSchema schema : merge.getPlainSchemas()) { + schema.setAnyTypeClass(merge); + } + for (DerSchema schema : merge.getDerSchemas()) { + schema.setAnyTypeClass(merge); + } + for (VirSchema schema : merge.getVirSchemas()) { + schema.setAnyTypeClass(merge); + } + + return merge; + } + + @Override + public void deleteById(final String key) { + AnyTypeClass anyTypeClass = neo4jTemplate.findById(key, Neo4jAnyTypeClass.class).orElse(null); + if (anyTypeClass == null) { + return; + } + + for (PlainSchema schema : plainSchemaDAO.findByAnyTypeClasses(List.of(anyTypeClass))) { + schema.setAnyTypeClass(null); + } + for (DerSchema schema : derSchemaDAO.findByAnyTypeClasses(List.of(anyTypeClass))) { + schema.setAnyTypeClass(null); + } + for (VirSchema schema : virSchemaDAO.findByAnyTypeClasses(List.of(anyTypeClass))) { + schema.setAnyTypeClass(null); + } + + for (AnyType type : anyTypeDAO.findByClassesContaining(anyTypeClass)) { + type.getClasses().remove(anyTypeClass); + } + +// for (TypeExtension typeExt : groupDAO.findTypeExtensions(anyTypeClass)) { +// typeExt.getAuxClasses().remove(anyTypeClass); +// +// if (typeExt.getAuxClasses().isEmpty()) { +// typeExt.getGroup().getTypeExtensions().remove(typeExt); +// typeExt.setGroup(null); +// } +// } + for (Provision provision : resourceDAO.findProvisionsByAuxClass(anyTypeClass)) { + provision.getAuxClasses().remove(anyTypeClass.getKey()); + } + + neo4jTemplate.deleteById(key, Neo4jAnyTypeClass.class); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeRepo.java new file mode 100644 index 0000000000..938dc59ec8 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyType; +import org.springframework.data.repository.ListCrudRepository; + +public interface AnyTypeRepo + extends ListCrudRepository, AnyTypeRepoExt, AnyTypeDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeRepoExt.java new file mode 100644 index 0000000000..546e392259 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeRepoExt.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.core.persistence.neo4j.dao.repo; + +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; + +public interface AnyTypeRepoExt { + + List findByClassesContaining(AnyTypeClass anyTypeClass); + + AnyType getUser(); + + AnyType getGroup(); + + void deleteById(String key); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeRepoExtImpl.java new file mode 100644 index 0000000000..9868cffa9d --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyTypeRepoExtImpl.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.core.persistence.neo4j.dao.repo; + +import java.util.List; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.dao.RemediationDAO; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.persistence.neo4j.dao.AbstractDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyType; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyTypeClass; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; + +public class AnyTypeRepoExtImpl extends AbstractDAO implements AnyTypeRepoExt { + + protected final RemediationDAO remediationDAO; + + public AnyTypeRepoExtImpl( + final RemediationDAO remediationDAO, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient) { + + super(neo4jTemplate, neo4jClient); + this.remediationDAO = remediationDAO; + } + + @Transactional(readOnly = true) + @Override + public AnyType getUser() { + return neo4jTemplate.findById(AnyTypeKind.USER.name(), Neo4jAnyType.class).orElseThrow(); + } + + @Transactional(readOnly = true) + @Override + public AnyType getGroup() { + return neo4jTemplate.findById(AnyTypeKind.GROUP.name(), Neo4jAnyType.class).orElseThrow(); + } + + @Override + public List findByClassesContaining(final AnyTypeClass anyTypeClass) { + return findByRelationship(Neo4jAnyType.NODE, Neo4jAnyTypeClass.NODE, anyTypeClass.getKey(), Neo4jAnyType.class); + } + + @Override + public void deleteById(final String key) { + AnyType anyType = neo4jTemplate.findById(key, Neo4jAnyType.class).orElse(null); + if (anyType == null) { + return; + } + + if (anyType.equals(getUser()) || anyType.equals(getGroup())) { + throw new IllegalArgumentException(key + " cannot be deleted"); + } + + remediationDAO.findByAnyType(anyType).forEach(remediation -> { + remediation.setAnyType(null); + remediationDAO.delete(remediation); + }); + + neo4jTemplate.deleteById(key, Neo4jAnyType.class); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ApplicationRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ApplicationRepo.java new file mode 100644 index 0000000000..58a136eedf --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ApplicationRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.ApplicationDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jApplication; +import org.springframework.data.repository.ListCrudRepository; + +public interface ApplicationRepo + extends ListCrudRepository, ApplicationRepoExt, ApplicationDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ApplicationRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ApplicationRepoExt.java new file mode 100644 index 0000000000..e1f892136e --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ApplicationRepoExt.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import java.util.Optional; +import org.apache.syncope.core.persistence.api.entity.Application; +import org.apache.syncope.core.persistence.api.entity.Privilege; + +public interface ApplicationRepoExt { + + Optional findPrivilege(String key); + + Application save(Application application); + + void delete(Application application); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ApplicationRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ApplicationRepoExtImpl.java new file mode 100644 index 0000000000..7ffee16a88 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ApplicationRepoExtImpl.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.persistence.neo4j.dao.repo; + +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +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.Application; +import org.apache.syncope.core.persistence.api.entity.Privilege; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jApplication; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jPrivilege; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class ApplicationRepoExtImpl implements ApplicationRepoExt { + + protected final RoleDAO roleDAO; + + protected final UserDAO userDAO; + + protected final Neo4jTemplate neo4jTemplate; + + protected final NodeValidator nodeValidator; + + public ApplicationRepoExtImpl( + final RoleDAO roleDAO, + final UserDAO userDAO, + final Neo4jTemplate neo4jTemplate, + final NodeValidator nodeValidator) { + + this.roleDAO = roleDAO; + this.userDAO = userDAO; + this.neo4jTemplate = neo4jTemplate; + this.nodeValidator = nodeValidator; + } + + @Override + public Optional findPrivilege(final String key) { + return neo4jTemplate.findById(key, Neo4jPrivilege.class); + } + + @Override + public Application save(final Application application) { + // delete all privileges formerly associated to the application being saved + neo4jTemplate.findById(application.getKey(), Neo4jApplication.class).ifPresent(before -> { + Set bp = before.getPrivileges().stream().map(Privilege::getKey).collect(Collectors.toSet()); + Set ap = application.getPrivileges().stream().map(Privilege::getKey).collect(Collectors.toSet()); + bp.removeAll(ap); + bp.forEach(k -> neo4jTemplate.deleteById(k, Neo4jPrivilege.class)); + }); + return neo4jTemplate.save(nodeValidator.validate(application)); + } + + @Override + public void delete(final Application application) { + application.getPrivileges().forEach(privilege -> { + roleDAO.findByPrivileges(privilege). + forEach(role -> role.getPrivileges().remove(privilege)); + userDAO.findLinkedAccountsByPrivilege(privilege). + forEach(account -> account.getPrivileges().remove(privilege)); + + privilege.setApplication(null); + neo4jTemplate.deleteById(privilege.getKey(), Neo4jPrivilege.class); + }); + application.getPrivileges().clear(); + + neo4jTemplate.deleteById(application.getKey(), Neo4jApplication.class); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AttrRepoRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AttrRepoRepo.java new file mode 100644 index 0000000000..0f9155dc57 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AttrRepoRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.AttrRepoDAO; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jAttrRepo; +import org.springframework.data.repository.ListCrudRepository; + +public interface AttrRepoRepo + extends ListCrudRepository, AttrRepoRepoExt, AttrRepoDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AttrRepoRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AttrRepoRepoExt.java new file mode 100644 index 0000000000..eeabe703ce --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AttrRepoRepoExt.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.entity.am.AttrRepo; + +public interface AttrRepoRepoExt { + + AttrRepo save(AttrRepo attrRepo); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AttrRepoRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AttrRepoRepoExtImpl.java new file mode 100644 index 0000000000..acc9fa32fc --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AttrRepoRepoExtImpl.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.core.persistence.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.entity.am.AttrRepo; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jAttrRepo; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class AttrRepoRepoExtImpl implements AttrRepoRepoExt { + + protected final Neo4jTemplate neo4jTemplate; + + protected final NodeValidator nodeValidator; + + public AttrRepoRepoExtImpl(final Neo4jTemplate neo4jTemplate, final NodeValidator nodeValidator) { + this.neo4jTemplate = neo4jTemplate; + this.nodeValidator = nodeValidator; + } + + @Override + public AttrRepo save(final AttrRepo attrRepo) { + ((Neo4jAttrRepo) attrRepo).list2json(); + AttrRepo saved = neo4jTemplate.save(nodeValidator.validate(attrRepo)); + ((Neo4jAttrRepo) saved).postSave(); + return saved; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuditConfRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuditConfRepo.java new file mode 100644 index 0000000000..bdb4e57733 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuditConfRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.AuditConfDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAuditConf; +import org.springframework.data.repository.ListCrudRepository; + +public interface AuditConfRepo + extends ListCrudRepository, AuditConfDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthModuleRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthModuleRepo.java new file mode 100644 index 0000000000..4b6a70c4b0 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthModuleRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.AuthModuleDAO; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jAuthModule; +import org.springframework.data.repository.ListCrudRepository; + +public interface AuthModuleRepo + extends ListCrudRepository, AuthModuleRepoExt, AuthModuleDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthModuleRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthModuleRepoExt.java new file mode 100644 index 0000000000..e12966dc63 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthModuleRepoExt.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.entity.am.AuthModule; + +public interface AuthModuleRepoExt { + + AuthModule save(AuthModule authModule); + + void delete(AuthModule authModule); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthModuleRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthModuleRepoExtImpl.java new file mode 100644 index 0000000000..ab26c52852 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthModuleRepoExtImpl.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.persistence.neo4j.dao.repo; + +import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf; +import org.apache.syncope.core.persistence.api.dao.PolicyDAO; +import org.apache.syncope.core.persistence.api.entity.am.AuthModule; +import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jAuthModule; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class AuthModuleRepoExtImpl implements AuthModuleRepoExt { + + protected final PolicyDAO policyDAO; + + protected final Neo4jTemplate neo4jTemplate; + + protected final NodeValidator nodeValidator; + + public AuthModuleRepoExtImpl( + final PolicyDAO policyDAO, + final Neo4jTemplate neo4jTemplate, + final NodeValidator nodeValidator) { + + this.policyDAO = policyDAO; + this.neo4jTemplate = neo4jTemplate; + this.nodeValidator = nodeValidator; + } + + @Override + public AuthModule save(final AuthModule authModule) { + ((Neo4jAuthModule) authModule).list2json(); + AuthModule saved = neo4jTemplate.save(nodeValidator.validate(authModule)); + ((Neo4jAuthModule) saved).postSave(); + return saved; + } + + @Override + public void delete(final AuthModule authModule) { + policyDAO.findAll(AuthPolicy.class).stream(). + filter(policy -> policy.getConf() instanceof DefaultAuthPolicyConf). + forEach(policy -> { + DefaultAuthPolicyConf conf = (DefaultAuthPolicyConf) policy.getConf(); + if (conf.getAuthModules().remove(authModule.getKey())) { + policy.setConf(conf); + policyDAO.save(policy); + } + }); + + neo4jTemplate.deleteById(authModule.getKey(), Neo4jAuthModule.class); + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepo.java similarity index 73% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepo.java rename to core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepo.java index f5f4dcf5ff..86ccb54bed 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepo.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AuthProfileRepo.java @@ -16,13 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao.repo; +package org.apache.syncope.core.persistence.neo4j.dao.repo; -import org.apache.syncope.core.persistence.api.dao.RealmDAO; -import org.apache.syncope.core.persistence.jpa.entity.JPARealm; +import org.apache.syncope.core.persistence.api.dao.AuthProfileDAO; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jAuthProfile; import org.springframework.data.repository.PagingAndSortingRepository; -public interface RealmRepo - extends PagingAndSortingRepository, RealmRepoExt, RealmDAO { +public interface AuthProfileRepo + extends PagingAndSortingRepository, AuthProfileDAO { } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/CASSPClientAppRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/CASSPClientAppRepo.java new file mode 100644 index 0000000000..2039c7f15f --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/CASSPClientAppRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.CASSPClientAppDAO; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jCASSPClientApp; +import org.springframework.data.repository.ListCrudRepository; + +public interface CASSPClientAppRepo + extends ListCrudRepository, CASSPClientAppRepoExt, CASSPClientAppDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/CASSPClientAppRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/CASSPClientAppRepoExt.java new file mode 100644 index 0000000000..f816da4767 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/CASSPClientAppRepoExt.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.entity.am.CASSPClientApp; + +public interface CASSPClientAppRepoExt extends ClientAppRepoExt { +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/CASSPClientAppRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/CASSPClientAppRepoExtImpl.java new file mode 100644 index 0000000000..060b5c1ff1 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/CASSPClientAppRepoExtImpl.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.persistence.neo4j.dao.repo; + +import java.util.List; +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.Policy; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jCASSPClientApp; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class CASSPClientAppRepoExtImpl + extends AbstractClientRepoExt + implements CASSPClientAppRepoExt { + + public CASSPClientAppRepoExtImpl(final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient) { + super(neo4jTemplate, neo4jClient); + } + + @Override + public List findAllByPolicy(final Policy policy) { + return findAllByPolicy(policy, Neo4jCASSPClientApp.NODE, Neo4jCASSPClientApp.class); + } + + @Override + public List findAllByRealm(final Realm realm) { + return findAllByRealm(realm, Neo4jCASSPClientApp.NODE, Neo4jCASSPClientApp.class); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ClientAppRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ClientAppRepoExt.java new file mode 100644 index 0000000000..41ab8e8fbf --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ClientAppRepoExt.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.core.persistence.neo4j.dao.repo; + +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.am.ClientApp; +import org.apache.syncope.core.persistence.api.entity.policy.Policy; + +public interface ClientAppRepoExt { + + List findAllByPolicy(Policy policy); + + List findAllByRealm(Realm realm); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepo.java new file mode 100644 index 0000000000..6339b820ad --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jConnInstance; +import org.springframework.data.repository.ListCrudRepository; + +public interface ConnInstanceRepo + extends ListCrudRepository, ConnInstanceRepoExt, ConnInstanceDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepoExt.java new file mode 100644 index 0000000000..b0c091022c --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepoExt.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.core.persistence.neo4j.dao.repo; + +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.ConnInstance; + +public interface ConnInstanceRepoExt { + + ConnInstance authFind(String key); + + List findAll(); + + ConnInstance save(ConnInstance connector); + + void deleteById(String key); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepoExtImpl.java new file mode 100644 index 0000000000..023f040807 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConnInstanceRepoExtImpl.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.persistence.neo4j.dao.repo; + +import java.util.List; +import java.util.Set; +import org.apache.syncope.common.lib.types.IdMEntitlement; +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.persistence.neo4j.entity.Neo4jConnInstance; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.apache.syncope.core.spring.security.DelegatedAdministrationException; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; + +public class ConnInstanceRepoExtImpl implements ConnInstanceRepoExt { + + protected final ExternalResourceDAO resourceDAO; + + protected final Neo4jTemplate neo4jTemplate; + + protected final NodeValidator nodeValidator; + + public ConnInstanceRepoExtImpl( + final ExternalResourceDAO resourceDAO, + final Neo4jTemplate neo4jTemplate, + final NodeValidator nodeValidator) { + + this.resourceDAO = resourceDAO; + this.neo4jTemplate = neo4jTemplate; + this.nodeValidator = nodeValidator; + } + + @Transactional(readOnly = true) + @Override + public ConnInstance authFind(final String key) { + ConnInstance connInstance = neo4jTemplate.findById(key, Neo4jConnInstance.class).orElse(null); + if (connInstance == null) { + return null; + } + + Set authRealms = AuthContextUtils.getAuthorizations().get(IdMEntitlement.CONNECTOR_READ); + if (authRealms == null || authRealms.isEmpty() + || !authRealms.stream().anyMatch( + realm -> connInstance.getAdminRealm().getFullPath().startsWith(realm))) { + + throw new DelegatedAdministrationException( + connInstance.getAdminRealm().getFullPath(), + ConnInstance.class.getSimpleName(), + connInstance.getKey()); + } + + return connInstance; + } + + @Override + public List findAll() { + final Set authRealms = AuthContextUtils.getAuthorizations().get(IdMEntitlement.CONNECTOR_LIST); + if (authRealms == null || authRealms.isEmpty()) { + return List.of(); + } + + return neo4jTemplate.findAll(Neo4jConnInstance.class).stream().filter(connInstance -> authRealms.stream(). + anyMatch(realm -> connInstance.getAdminRealm().getFullPath().startsWith(realm))). + toList(); + } + + @Override + public ConnInstance save(final ConnInstance connector) { + ((Neo4jConnInstance) connector).list2json(); + ConnInstance saved = neo4jTemplate.save(nodeValidator.validate(connector)); + ((Neo4jConnInstance) saved).postSave(); + return saved; + } + + @Override + public void deleteById(final String key) { + neo4jTemplate.findById(key, Neo4jConnInstance.class).ifPresent(connInstance -> { + connInstance.getResources().stream(). + map(ExternalResource::getKey).toList().forEach(resourceDAO::deleteById); + + neo4jTemplate.deleteById(key, Neo4jConnInstance.class); + }); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DelegationRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DelegationRepo.java new file mode 100644 index 0000000000..0a88e92400 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DelegationRepo.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.core.persistence.neo4j.dao.repo; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.dao.DelegationDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDelegation; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; +import org.springframework.data.neo4j.repository.query.Query; +import org.springframework.data.repository.ListCrudRepository; +import org.springframework.data.repository.query.Param; + +public interface DelegationRepo + extends ListCrudRepository, DelegationRepoExt, DelegationDAO { + + @Query("MATCH (l:" + Neo4jUser.NODE + " {id: $delegating})-" + + "[:" + Neo4jDelegation.DELEGATING_REL + "]-" + + "(n:" + Neo4jDelegation.NODE + ")-" + + "[:" + Neo4jDelegation.DELEGATED_REL + "]-" + + "(r:" + Neo4jUser.NODE + " {id: $delegated})" + + "WHERE n.startDate <= $now AND (n.endDate IS NULL OR n.endDate >= $now) " + + "RETURN n.id") + @Override + Optional findValidFor( + @Param("delegating") String delegating, + @Param("delegated") String delegated, + @Param("now") OffsetDateTime now); + + @Query("MATCH (l:" + Neo4jUser.NODE + ")-" + + "[:" + Neo4jDelegation.DELEGATING_REL + "]-" + + "(n:" + Neo4jDelegation.NODE + ")-" + + "[:" + Neo4jDelegation.DELEGATED_REL + "]-" + + "(r:" + Neo4jUser.NODE + " {id: $delegated})" + + "WHERE n.startDate <= $now AND (n.endDate IS NULL OR n.endDate >= $now) " + + "RETURN l.username") + @Override + List findValidDelegating(@Param("delegated") String delegated, @Param("now") OffsetDateTime now); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DelegationRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DelegationRepoExt.java new file mode 100644 index 0000000000..08a414eb41 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DelegationRepoExt.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.core.persistence.neo4j.dao.repo; + +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.Delegation; +import org.apache.syncope.core.persistence.api.entity.Role; +import org.apache.syncope.core.persistence.api.entity.user.User; + +public interface DelegationRepoExt { + + List findByDelegating(User user); + + List findByDelegated(User user); + + List findByRoles(Role role); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DelegationRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DelegationRepoExtImpl.java new file mode 100644 index 0000000000..c62447c831 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DelegationRepoExtImpl.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.persistence.neo4j.dao.repo; + +import java.util.List; +import java.util.Map; +import org.apache.syncope.core.persistence.api.entity.Delegation; +import org.apache.syncope.core.persistence.api.entity.Role; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.neo4j.dao.AbstractDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDelegation; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRole; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class DelegationRepoExtImpl extends AbstractDAO implements DelegationRepoExt { + + public DelegationRepoExtImpl(final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient) { + super(neo4jTemplate, neo4jClient); + } + + @Override + public List findByDelegating(final User user) { + return toList(neo4jClient.query( + "MATCH (l:" + Neo4jUser.NODE + " {id: $id})-" + + "[:" + Neo4jDelegation.DELEGATING_REL + "]-" + + "(n:" + Neo4jDelegation.NODE + ") " + + "RETURN n.id").bindAll(Map.of("id", user.getKey())).fetch().all(), + "n.id", + Neo4jDelegation.class); + } + + @Override + public List findByDelegated(final User user) { + return toList(neo4jClient.query( + "MATCH (n:" + Neo4jDelegation.NODE + ")-" + + "[:" + Neo4jDelegation.DELEGATED_REL + "]-" + + "(u:" + Neo4jUser.NODE + " {id: $id}) " + + "RETURN n.id").bindAll(Map.of("id", user.getKey())).fetch().all(), + "n.id", + Neo4jDelegation.class); + } + + @Override + public List findByRoles(final Role role) { + return toList(neo4jClient.query( + "MATCH (n:" + Neo4jDelegation.NODE + ")-[]-(r:" + Neo4jRole.NODE + " {id: $id}) " + + "RETURN n.id").bindAll(Map.of("id", role.getKey())).fetch().all(), + "n.id", + Neo4jDelegation.class); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DerSchemaRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DerSchemaRepo.java new file mode 100644 index 0000000000..1309da90dd --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DerSchemaRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDerSchema; +import org.springframework.data.repository.ListCrudRepository; + +public interface DerSchemaRepo + extends ListCrudRepository, DerSchemaRepoExt, DerSchemaDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DerSchemaRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DerSchemaRepoExt.java new file mode 100644 index 0000000000..4769bd23d3 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DerSchemaRepoExt.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.core.persistence.neo4j.dao.repo; + +import java.util.Collection; +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.DerSchema; + +public interface DerSchemaRepoExt { + + List findByAnyTypeClasses(Collection anyTypeClasses); + + DerSchema save(DerSchema schema); + + void deleteById(String key); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DerSchemaRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DerSchemaRepoExtImpl.java new file mode 100644 index 0000000000..28df59e10c --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DerSchemaRepoExtImpl.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.persistence.neo4j.dao.repo; + +import java.util.Collection; +import java.util.List; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.DerSchema; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDerSchema; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jSchema; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class DerSchemaRepoExtImpl extends AbstractSchemaRepoExt implements DerSchemaRepoExt { + + protected final ExternalResourceDAO resourceDAO; + + public DerSchemaRepoExtImpl( + final ExternalResourceDAO resourceDAO, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super(neo4jTemplate, neo4jClient, nodeValidator); + this.resourceDAO = resourceDAO; + } + + @Override + public List findByAnyTypeClasses(final Collection anyTypeClasses) { + return findByAnyTypeClasses(anyTypeClasses, Neo4jDerSchema.class, DerSchema.class); + } + + @Override + public DerSchema save(final DerSchema schema) { + ((Neo4jSchema) schema).map2json(); + DerSchema saved = neo4jTemplate.save(nodeValidator.validate(schema)); + ((Neo4jSchema) saved).postSave(); + return saved; + } + + @Override + public void deleteById(final String key) { + neo4jTemplate.findById(key, Neo4jDerSchema.class).ifPresent(schema -> { + resourceDAO.deleteMapping(key); + + if (schema.getAnyTypeClass() != null) { + schema.getAnyTypeClass().getDerSchemas().remove(schema); + } + + neo4jTemplate.deleteById(key, Neo4jDerSchema.class); + }); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DynRealmRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DynRealmRepo.java new file mode 100644 index 0000000000..e0d3ef87fa --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DynRealmRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDynRealm; +import org.springframework.data.repository.ListCrudRepository; + +public interface DynRealmRepo + extends ListCrudRepository, DynRealmRepoExt, DynRealmDAO { + +} diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnPoolConf.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DynRealmRepoExt.java similarity index 63% rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnPoolConf.java rename to core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DynRealmRepoExt.java index 7148be3b31..d47340df5b 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnPoolConf.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DynRealmRepoExt.java @@ -16,27 +16,20 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.api.entity; +package org.apache.syncope.core.persistence.neo4j.dao.repo; -public interface ConnPoolConf { +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.DynRealm; - Integer getMaxObjects(); +public interface DynRealmRepoExt { - void setMaxObjects(Integer maxObjects); + String DYN_REALM_MEMBERSHIP_REL = "DYN_REALM_MEMBERSHIP"; - Integer getMinIdle(); + DynRealm saveAndRefreshDynMemberships(DynRealm dynRealm); - void setMinIdle(Integer minIdle); + void refreshDynMemberships(Any any); - Integer getMaxIdle(); + void removeDynMemberships(String anyKey); - void setMaxIdle(Integer maxIdle); - - Long getMaxWait(); - - void setMaxWait(Long maxWait); - - Long getMinEvictableIdleTimeMillis(); - - void setMinEvictableIdleTimeMillis(Long minEvictableIdleTimeMillis); + void deleteById(String key); } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DynRealmRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DynRealmRepoExtImpl.java new file mode 100644 index 0000000000..ecfb245473 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DynRealmRepoExtImpl.java @@ -0,0 +1,207 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO; +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.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.DynRealm; +import org.apache.syncope.core.persistence.api.search.SearchCondConverter; +import org.apache.syncope.core.persistence.api.search.SearchCondVisitor; +import org.apache.syncope.core.persistence.neo4j.dao.AbstractDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDynRealm; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDynRealmMembership; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAnyObject; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.apache.syncope.core.provisioning.api.event.EntityLifecycleEvent; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.identityconnectors.framework.common.objects.SyncDeltaType; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; + +public class DynRealmRepoExtImpl extends AbstractDAO implements DynRealmRepoExt { + + protected final ApplicationEventPublisher publisher; + + protected final UserDAO userDAO; + + protected final GroupDAO groupDAO; + + protected final AnyObjectDAO anyObjectDAO; + + protected final AnySearchDAO searchDAO; + + protected final AnyMatchDAO anyMatchDAO; + + protected final SearchCondVisitor searchCondVisitor; + + protected final NodeValidator nodeValidator; + + public DynRealmRepoExtImpl( + final ApplicationEventPublisher publisher, + final UserDAO userDAO, + final GroupDAO groupDAO, + final AnyObjectDAO anyObjectDAO, + final AnySearchDAO searchDAO, + final AnyMatchDAO anyMatchDAO, + final SearchCondVisitor searchCondVisitor, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super(neo4jTemplate, neo4jClient); + this.publisher = publisher; + this.userDAO = userDAO; + this.groupDAO = groupDAO; + this.anyObjectDAO = anyObjectDAO; + this.searchDAO = searchDAO; + this.anyMatchDAO = anyMatchDAO; + this.searchCondVisitor = searchCondVisitor; + this.nodeValidator = nodeValidator; + } + + protected List clearDynMembers(final DynRealm dynRealm) { + String query = "MATCH (n)-[r:" + DYN_REALM_MEMBERSHIP_REL + "]-(p:" + Neo4jDynRealm.NODE + " {id: $id}) "; + Map parameters = Map.of("id", dynRealm.getKey()); + + List cleared = neo4jClient.query( + query + "RETURN n.id"). + bindAll(parameters). + fetch().all().stream().map(found -> found.get("n.id").toString()).collect(Collectors.toList()); + + neo4jClient.query(query + "DETACH DELETE r").bindAll(parameters).run(); + + return cleared; + } + + protected void notifyDynMembershipRemoval(final List anyKeys) { + anyKeys.forEach(key -> { + Optional> any = userDAO.findById(key); + if (any.isEmpty()) { + any = groupDAO.findById(key); + } + if (any.isEmpty()) { + any = anyObjectDAO.findById(key); + } + any.ifPresent(entity -> publisher.publishEvent( + new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, entity, AuthContextUtils.getDomain()))); + }); + } + + protected String node(final Any any) { + return switch (any.getType().getKind()) { + case USER -> + Neo4jUser.NODE; + case GROUP -> + Neo4jGroup.NODE; + case ANY_OBJECT -> + Neo4jAnyObject.NODE; + default -> + ""; + }; + } + + @Override + public DynRealm saveAndRefreshDynMemberships(final DynRealm dynRealm) { + DynRealm merged = neo4jTemplate.save(nodeValidator.validate(dynRealm)); + + // refresh dynamic memberships + List cleared = clearDynMembers(merged); + + merged.getDynMemberships().stream().map(memb -> searchDAO.search( + SearchCondConverter.convert(searchCondVisitor, memb.getFIQLCond()), memb.getAnyType().getKind())). + forEach(matching -> matching.forEach(any -> { + + neo4jClient.query( + "MATCH (a:" + node(any) + " {id: $aid}), (b:" + Neo4jDynRealm.NODE + "{id: $rid}) " + + "CREATE (a)-[:" + DYN_REALM_MEMBERSHIP_REL + "]->(b)"). + bindAll(Map.of("aid", any.getKey(), "rid", merged.getKey())).run(); + + publisher.publishEvent( + new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, any, AuthContextUtils.getDomain())); + cleared.remove(any.getKey()); + })); + + notifyDynMembershipRemoval(cleared); + + return merged; + } + + @Override + public void deleteById(final String key) { + neo4jTemplate.findById(key, Neo4jDynRealm.class).ifPresent(dynRealm -> { + cascadeDelete( + Neo4jDynRealmMembership.NODE, + Neo4jDynRealm.NODE, + key, + Neo4jDynRealmMembership.class); + + notifyDynMembershipRemoval(clearDynMembers(dynRealm)); + + neo4jTemplate.deleteById(key, Neo4jDynRealm.class); + }); + } + + @Transactional + @Override + public void refreshDynMemberships(final Any any) { + neo4jTemplate.findAll(Neo4jDynRealm.class). + forEach(dynRealm -> dynRealm.getDynMembership(any.getType()).ifPresent(memb -> { + + boolean matches = anyMatchDAO.matches( + any, SearchCondConverter.convert(searchCondVisitor, memb.getFIQLCond())); + + boolean existing = neo4jTemplate.count( + "MATCH (n {id: $id})-[:" + DYN_REALM_MEMBERSHIP_REL + "]-(p:" + Neo4jDynRealm.NODE + ") " + + "RETURN COUNT(p)", + Map.of("id", any.getKey())) > 0; + + if (matches && !existing) { + neo4jClient.query( + "MATCH (a:" + node(any) + " {id: $aid}), (b:" + Neo4jDynRealm.NODE + "{id: $rid}) " + + "CREATE (a)-[:" + DYN_REALM_MEMBERSHIP_REL + "]->(b)"). + bindAll(Map.of("aid", any.getKey(), "rid", dynRealm.getKey())).run(); + } else if (!matches && existing) { + neo4jClient.query( + "MATCH (n {id: $aid})-" + + "[:" + DYN_REALM_MEMBERSHIP_REL + "]-" + + "(p:" + Neo4jDynRealm.NODE + " {id: $rid}) " + + "DETACH DELETE r").bindAll(Map.of("aid", any.getKey(), "rid", dynRealm.getKey())).run(); + } + })); + } + + @Override + public void removeDynMemberships(final String anyKey) { + neo4jClient.query( + "MATCH (n {id: $id})-[r:" + DYN_REALM_MEMBERSHIP_REL + "]-(p:" + Neo4jDynRealm.NODE + ") " + + "DETACH DELETE r").bindAll(Map.of("id", anyKey)).run(); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepo.java new file mode 100644 index 0000000000..ac303e391f --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; +import org.springframework.data.repository.ListCrudRepository; + +public interface ExternalResourceRepo + extends ListCrudRepository, ExternalResourceRepoExt, ExternalResourceDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepoExt.java new file mode 100644 index 0000000000..77ffb70c56 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepoExt.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.core.persistence.neo4j.dao.repo; + +import java.util.List; +import org.apache.syncope.common.lib.to.Provision; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +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.policy.Policy; + +public interface ExternalResourceRepoExt { + + ExternalResource authFind(String key); + + List findByConnInstance(String connInstance); + + List findByProvisionSorter(Implementation provisionSorter); + + List findByPropagationActionsContaining(Implementation propagationActions); + + List findProvisionsByAuxClass(AnyTypeClass anyTypeClass); + + boolean anyItemHaving(Implementation transformer); + + List findByPolicy(Policy policy); + + List findAll(); + + ExternalResource save(ExternalResource resource); + + void deleteById(String key); + + void deleteMapping(String schemaKey); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepoExtImpl.java new file mode 100644 index 0000000000..aeaef9475a --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepoExtImpl.java @@ -0,0 +1,272 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import org.apache.syncope.common.lib.to.Provision; +import org.apache.syncope.common.lib.types.IdMEntitlement; +import org.apache.syncope.common.lib.types.TaskType; +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.PolicyDAO; +import org.apache.syncope.core.persistence.api.dao.RealmDAO; +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.VirSchemaDAO; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +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.policy.AccountPolicy; +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.PullPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; +import org.apache.syncope.core.persistence.neo4j.dao.AbstractDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jConnInstance; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementation; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccountPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPasswordPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jLinkedAccount; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.apache.syncope.core.spring.security.DelegatedAdministrationException; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +public class ExternalResourceRepoExtImpl extends AbstractDAO implements ExternalResourceRepoExt { + + protected final TaskDAO taskDAO; + + protected final AnyObjectDAO anyObjectDAO; + + protected final UserDAO userDAO; + + protected final GroupDAO groupDAO; + + protected final PolicyDAO policyDAO; + + protected final VirSchemaDAO virSchemaDAO; + + protected final RealmDAO realmDAO; + + protected final NodeValidator nodeValidator; + + public ExternalResourceRepoExtImpl( + final TaskDAO taskDAO, + final AnyObjectDAO anyObjectDAO, + final UserDAO userDAO, + final GroupDAO groupDAO, + final PolicyDAO policyDAO, + final VirSchemaDAO virSchemaDAO, + final RealmDAO realmDAO, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super(neo4jTemplate, neo4jClient); + this.taskDAO = taskDAO; + this.anyObjectDAO = anyObjectDAO; + this.userDAO = userDAO; + this.groupDAO = groupDAO; + this.policyDAO = policyDAO; + this.virSchemaDAO = virSchemaDAO; + this.realmDAO = realmDAO; + this.nodeValidator = nodeValidator; + } + + @Override + public ExternalResource authFind(final String key) { + ExternalResource resource = neo4jTemplate.findById(key, Neo4jExternalResource.class).orElse(null); + if (resource == null) { + return null; + } + + Set authRealms = AuthContextUtils.getAuthorizations().get(IdMEntitlement.RESOURCE_READ); + if (authRealms == null || authRealms.isEmpty() + || !authRealms.stream().anyMatch(realm -> resource.getConnector() != null + && resource.getConnector().getAdminRealm().getFullPath().startsWith(realm))) { + + throw new DelegatedAdministrationException( + resource.getConnector().getAdminRealm().getFullPath(), + ExternalResource.class.getSimpleName(), + resource.getKey()); + } + + return resource; + } + + @Override + public List findByConnInstance(final String connInstance) { + return findByRelationship( + Neo4jExternalResource.NODE, Neo4jConnInstance.NODE, connInstance, Neo4jExternalResource.class); + } + + @Override + public List findByProvisionSorter(final Implementation provisionSorter) { + return findByRelationship( + Neo4jExternalResource.NODE, + Neo4jImplementation.NODE, + provisionSorter.getKey(), + Neo4jExternalResource.RESOURCE_PROVISION_SORTER_REL, + Neo4jExternalResource.class); + } + + @Override + public List findByPropagationActionsContaining(final Implementation propagationActions) { + return findByRelationship( + Neo4jExternalResource.NODE, + Neo4jImplementation.NODE, + propagationActions.getKey(), + Neo4jExternalResource.RESOURCE_PROPAGATION_ACTIONS_REL, + Neo4jExternalResource.class); + } + + @Override + public List findProvisionsByAuxClass(final AnyTypeClass anyTypeClass) { + return findAll().stream(). + flatMap(resource -> resource.getProvisions().stream()). + filter(provision -> provision.getAuxClasses().contains(anyTypeClass.getKey())). + toList(); + } + + @Override + public boolean anyItemHaving(final Implementation transformer) { + return findAll().stream(). + flatMap(resource -> resource.getProvisions().stream()). + flatMap(provision -> provision.getMapping().getItems().stream()). + filter(item -> item.getTransformers().contains(transformer.getKey())). + count() > 0; + } + + @Override + public List findByPolicy(final Policy policy) { + String relationship = null; + String label = null; + if (policy instanceof AccountPolicy) { + relationship = Neo4jExternalResource.RESOURCE_ACCOUNT_POLICY_REL; + label = Neo4jAccountPolicy.NODE + ":" + Neo4jPolicy.NODE; + } else if (policy instanceof PasswordPolicy) { + relationship = Neo4jExternalResource.RESOURCE_PASSWORD_POLICY_REL; + label = Neo4jPasswordPolicy.NODE + ":" + Neo4jPolicy.NODE; + } else if (policy instanceof PropagationPolicy) { + relationship = Neo4jExternalResource.RESOURCE_PASSWORD_POLICY_REL; + label = Neo4jPasswordPolicy.NODE + ":" + Neo4jPolicy.NODE; + } else if (policy instanceof PushPolicy) { + relationship = Neo4jExternalResource.RESOURCE_PASSWORD_POLICY_REL; + label = Neo4jPasswordPolicy.NODE + ":" + Neo4jPolicy.NODE; + } else if (policy instanceof PullPolicy) { + relationship = Neo4jExternalResource.RESOURCE_PASSWORD_POLICY_REL; + label = Neo4jPasswordPolicy.NODE + ":" + Neo4jPolicy.NODE; + } + + return findByRelationship( + Neo4jExternalResource.NODE, + label, + policy.getKey(), + relationship, + Neo4jExternalResource.class); + } + + @Transactional(readOnly = true) + @Override + public List findAll() { + Set authRealms = AuthContextUtils.getAuthorizations().get(IdMEntitlement.RESOURCE_LIST); + if (CollectionUtils.isEmpty(authRealms)) { + return List.of(); + } + + return neo4jTemplate.findAll(Neo4jExternalResource.class).stream().filter(resource -> authRealms.stream(). + anyMatch(realm -> resource.getConnector() != null + && resource.getConnector().getAdminRealm().getFullPath().startsWith(realm))). + toList(); + } + + @Transactional(rollbackFor = { Throwable.class }) + @Override + public ExternalResource save(final ExternalResource resource) { + ((Neo4jExternalResource) resource).list2json(); + ExternalResource saved = neo4jTemplate.save(nodeValidator.validate(resource)); + ((Neo4jExternalResource) saved).postSave(); + return saved; + } + + @Override + public void deleteMapping(final String schemaKey) { + findAll().forEach(resource -> { + AtomicBoolean removed = new AtomicBoolean(false); + + resource.getProvisions().forEach(provision -> removed.set( + removed.get() + || (provision.getMapping() != null + && provision.getMapping().getItems().removeIf(item -> schemaKey.equals(item.getIntAttrName()))))); + + if (removed.get()) { + ((Neo4jExternalResource) resource).list2json(); + ExternalResource saved = neo4jTemplate.save(resource); + ((Neo4jExternalResource) saved).postSave(); + } + }); + } + + @Override + public void deleteById(final String key) { + ExternalResource resource = neo4jTemplate.findById(key, Neo4jExternalResource.class).orElse(null); + if (resource == null) { + return; + } + + taskDAO.deleteAll(resource, TaskType.PROPAGATION); + taskDAO.deleteAll(resource, TaskType.PULL); + taskDAO.deleteAll(resource, TaskType.PUSH); + realmDAO.findByResources(resource). + forEach(realm -> realm.getResources().remove(resource)); + anyObjectDAO.findByResourcesContaining(resource). + forEach(anyObject -> anyObject.getResources().remove(resource)); + userDAO.findLinkedAccountsByResource(resource).forEach(account -> { + account.getOwner().getLinkedAccounts().remove(account); + account.setOwner(null); + neo4jTemplate.deleteById(account.getKey(), Neo4jLinkedAccount.class); + }); + userDAO.findByResourcesContaining(resource). + forEach(user -> user.getResources().remove(resource)); + groupDAO.findByResourcesContaining(resource). + forEach(group -> group.getResources().remove(resource)); + policyDAO.findByResource(resource). + forEach(policy -> policy.getResources().remove(resource)); + + virSchemaDAO.findByResource(resource).forEach(virSchemaDAO::delete); + + if (resource.getConnector() != null + && resource.getConnector().getResources() != null + && !resource.getConnector().getResources().isEmpty()) { + + resource.getConnector().getResources().remove(resource); + } + resource.setConnector(null); + + neo4jTemplate.deleteById(key, Neo4jExternalResource.class); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/FIQLQueryRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/FIQLQueryRepo.java new file mode 100644 index 0000000000..7ea1573ab2 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/FIQLQueryRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.FIQLQueryDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jFIQLQuery; +import org.springframework.data.repository.ListCrudRepository; + +public interface FIQLQueryRepo + extends ListCrudRepository, FIQLQueryRepoExt, FIQLQueryDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/FIQLQueryRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/FIQLQueryRepoExt.java new file mode 100644 index 0000000000..5178a0c0fe --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/FIQLQueryRepoExt.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.FIQLQuery; +import org.apache.syncope.core.persistence.api.entity.user.User; + +public interface FIQLQueryRepoExt { + + List findByOwner(User user, String target); + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/FIQLQueryRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/FIQLQueryRepoExtImpl.java new file mode 100644 index 0000000000..e26c8d8686 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/FIQLQueryRepoExtImpl.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.core.persistence.neo4j.dao.repo; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.core.persistence.api.entity.FIQLQuery; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.neo4j.dao.AbstractDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jFIQLQuery; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class FIQLQueryRepoExtImpl extends AbstractDAO implements FIQLQueryRepoExt { + + public FIQLQueryRepoExtImpl(final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient) { + super(neo4jTemplate, neo4jClient); + } + + @Override + public List findByOwner(final User user, final String target) { + Map parameters = new HashMap<>(); + parameters.put("id", user.getKey()); + StringBuilder queryString = new StringBuilder( + "MATCH (n:").append(Neo4jFIQLQuery.NODE).append(")-[]-(p:").append(Neo4jUser.NODE). + append(" {id: $id}) "); + if (StringUtils.isNotBlank(target)) { + queryString.append("WHERE n.target = $target "); + parameters.put("target", target); + } + queryString.append("RETURN n.id"); + + return toList( + neo4jClient.query(queryString.toString()).bindAll(parameters).fetch().all(), + "n.id", + Neo4jFIQLQuery.class); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepo.java new file mode 100644 index 0000000000..96c03efd8f --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepo.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.core.persistence.neo4j.dao.repo; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAMembership; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAnyObject; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUMembership; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; +import org.springframework.data.neo4j.repository.query.Query; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.repository.query.Param; + +public interface GroupRepo + extends PagingAndSortingRepository, GroupRepoExt, GroupDAO { + + @Query("MATCH (n:" + Neo4jGroup.NODE + ") WHERE n.name = $name RETURN n.id") + @Override + Optional findKey(@Param("name") String name); + + @Query("MATCH (n:" + Neo4jGroup.NODE + ") WHERE toLower(n.name) =~ $pattern RETURN n.id") + @Override + List findKeysByNamePattern(@Param("pattern") String pattern); + + @Query("MATCH (a:" + Neo4jAnyObject.NODE + " {id: $anyObjectKey})-[]-" + + "(n:" + Neo4jAMembership.NODE + ")-[]-" + + "(g:" + Neo4jGroup.NODE + " {id: $groupKey}) " + + "RETURN COUNT(n) > 0") + @Override + boolean existsAMembership(String anyObjectKey, String groupKey); + + @Query("MATCH (u:" + Neo4jUser.NODE + " {id: $userKey})-[]-" + + "(n:" + Neo4jUMembership.NODE + ")-[]-" + + "(g:" + Neo4jGroup.NODE + " {id: $groupKey}) " + + "RETURN COUNT(n) > 0") + @Override + boolean existsUMembership(String userKey, String groupKey); + + @Query("MATCH (a:" + Neo4jAnyObject.NODE + ")-[]-" + + "(n:" + Neo4jAMembership.NODE + ")-[]-" + + "(g:" + Neo4jGroup.NODE + " {id: $groupKey}) " + + "RETURN a.id") + @Override + List findAMembers(@Param("groupKey") String groupKey); + + @Query("MATCH (u:" + Neo4jUser.NODE + ")-[]-" + + "(n:" + Neo4jUMembership.NODE + ")-[]-" + + "(g:" + Neo4jGroup.NODE + " {id: $groupKey}) " + + "RETURN u.id") + @Override + List findUMembers(@Param("groupKey") String groupKey); + + @Query("MATCH (a:" + Neo4jAnyObject.NODE + ")-[]-" + + "(n:" + Neo4jAMembership.NODE + ")-[]-" + + "(g:" + Neo4jGroup.NODE + " {id: $groupKey}) " + + "RETURN COUNT(a)") + @Override + long countAMembers(@Param("groupKey") String groupKey); + + @Query("MATCH (u:" + Neo4jUser.NODE + ")-[]-" + + "(n:" + Neo4jUMembership.NODE + ")-[]-" + + "(g:" + Neo4jGroup.NODE + " {id: $groupKey}) " + + "RETURN COUNT(u)") + @Override + long countUMembers(@Param("groupKey") String groupKey); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExt.java new file mode 100644 index 0000000000..92e62c0dc8 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExt.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.persistence.neo4j.dao.repo; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +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.Group; +import org.apache.syncope.core.persistence.api.entity.group.TypeExtension; +import org.apache.syncope.core.persistence.api.entity.user.UMembership; +import org.apache.syncope.core.persistence.api.entity.user.User; + +public interface GroupRepoExt extends AnyRepoExt { + + String DYN_GROUP_USER_MEMBERSHIP_REL = "DYN_GROUP_USER_MEMBERSHIP"; + + String DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL = "DYN_GROUP_ANY_OBJECT_MEMBERSHIP"; + + void securityChecks(Set authRealms, String key, String realm); + + Map countByRealm(); + + List findOwnedByUser(String userKey); + + List findOwnedByGroup(String groupKey); + + List findAMemberships(Group group); + + List findUMemberships(Group group); + + Group saveAndRefreshDynMemberships(Group group); + + List findTypeExtensions(AnyTypeClass anyTypeClass); + + long countADynMembers(Group group); + + long countUDynMembers(Group group); + + List findADynMembers(Group group); + + List findUDynMembers(Group group); + + void clearADynMembers(Group group); + + void clearUDynMembers(Group group); + + Pair, Set> refreshDynMemberships(AnyObject anyObject); + + Set removeDynMemberships(AnyObject anyObject); + + Pair, Set> refreshDynMemberships(User user); + + Set removeDynMemberships(User user); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExtImpl.java new file mode 100644 index 0000000000..713fff3d61 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExtImpl.java @@ -0,0 +1,570 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import java.time.OffsetDateTime; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.persistence.api.dao.AnyDAO; +import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO; +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.DerSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; +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.dao.search.SearchCond; +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.AnyTypeClass; +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.anyobject.ADynGroupMembership; +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.Group; +import org.apache.syncope.core.persistence.api.entity.group.TypeExtension; +import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; +import org.apache.syncope.core.persistence.api.entity.user.UMembership; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.api.search.SearchCondConverter; +import org.apache.syncope.core.persistence.api.search.SearchCondVisitor; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyType; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyTypeClass; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jADynGroupMembership; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAMembership; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAnyObject; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jTypeExtension; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUDynGroupMembership; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUMembership; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.apache.syncope.core.provisioning.api.event.EntityLifecycleEvent; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.apache.syncope.core.spring.security.DelegatedAdministrationException; +import org.identityconnectors.framework.common.objects.SyncDeltaType; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; + +public class GroupRepoExtImpl extends AbstractAnyRepoExt implements GroupRepoExt { + + protected final ApplicationEventPublisher publisher; + + protected final AnyMatchDAO anyMatchDAO; + + protected final UserDAO userDAO; + + protected final AnyObjectDAO anyObjectDAO; + + protected final AnySearchDAO anySearchDAO; + + protected final SearchCondVisitor searchCondVisitor; + + protected final NodeValidator nodeValidator; + + public GroupRepoExtImpl( + final AnyUtilsFactory anyUtilsFactory, + final ApplicationEventPublisher publisher, + final PlainSchemaDAO plainSchemaDAO, + final DerSchemaDAO derSchemaDAO, + final DynRealmDAO dynRealmDAO, + final AnyMatchDAO anyMatchDAO, + final UserDAO userDAO, + final AnyObjectDAO anyObjectDAO, + final AnySearchDAO searchDAO, + final SearchCondVisitor searchCondVisitor, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super( + plainSchemaDAO, + derSchemaDAO, + dynRealmDAO, + anyUtilsFactory.getInstance(AnyTypeKind.GROUP), + neo4jTemplate, + neo4jClient); + this.publisher = publisher; + this.anyMatchDAO = anyMatchDAO; + this.userDAO = userDAO; + this.anyObjectDAO = anyObjectDAO; + this.anySearchDAO = searchDAO; + this.searchCondVisitor = searchCondVisitor; + this.nodeValidator = nodeValidator; + } + + @Transactional(readOnly = true) + @Override + public Optional findLastChange(final String key) { + return findLastChange(key, Neo4jGroup.NODE); + } + + @Transactional(readOnly = true) + @Override + public void securityChecks( + final Set authRealms, + final String key, + final String realm) { + + // 1. check if AuthContextUtils.getUsername() is owner of the group, or + // if group is in Realm (or descendants) for which AuthContextUtils.getUsername() owns entitlement + boolean authorized = authRealms.stream().anyMatch(authRealm -> realm.startsWith(authRealm) + || authRealm.equals(RealmUtils.getGroupOwnerRealm(realm, key))); + + // 2. check if groups is in at least one DynRealm for which AuthContextUtils.getUsername() owns entitlement + if (!authorized) { + authorized = findDynRealms(key).stream().anyMatch(authRealms::contains); + } + + if (authRealms.isEmpty() || !authorized) { + throw new DelegatedAdministrationException(realm, AnyTypeKind.GROUP.name(), key); + } + } + + @Override + protected void securityChecks(final Group group) { + Set authRealms = AuthContextUtils.getAuthorizations(). + getOrDefault(IdRepoEntitlement.GROUP_READ, Set.of()); + + securityChecks(authRealms, group.getKey(), group.getRealm().getFullPath()); + } + + @Override + public Map countByRealm() { + Collection> result = neo4jClient.query( + "MATCH (n:" + Neo4jGroup.NODE + ")-[]-(r:" + Neo4jRealm.NODE + ") " + + "RETURN r.fullPath AS realm, COUNT(n) AS counted").fetch().all(); + + return result.stream().collect(Collectors.toMap(r -> r.get("realm").toString(), r -> (Long) r.get("counted"))); + } + + @Transactional(readOnly = true) + @Override + public List findOwnedByUser(final String userKey) { + User owner = userDAO.findById(userKey).orElse(null); + if (owner == null) { + return List.of(); + } + + Set owned = neo4jClient.query( + "MATCH (n:" + Neo4jGroup.NODE + ")-[]-(u:" + Neo4jUser.NODE + " {id: $userKey}) " + + "RETURN n.id").bindAll(Map.of("userKey", userKey)).fetch().all(). + stream().map(r -> r.get("n.id").toString()).collect(Collectors.toSet()); + + Map parameters = new HashMap<>(); + StringBuilder query = new StringBuilder("MATCH (n:" + Neo4jGroup.NODE + ")-[]-(o:" + Neo4jGroup.NODE + ") "); + + Collection matching = userDAO.findAllGroupKeys(owner); + if (!matching.isEmpty()) { + AtomicInteger index = new AtomicInteger(0); + query.append("WHERE ("). + append(matching.stream().map(group -> { + int idx = index.incrementAndGet(); + parameters.put("group" + idx, group); + return "o.id = $group" + idx; + }).collect(Collectors.joining(" OR "))). + append(") "); + } + + query.append("RETURN n.id"); + + owned.addAll(neo4jClient.query(query.toString()).bindAll(parameters).fetch().all(). + stream().map(r -> r.get("n.id").toString()).collect(Collectors.toSet())); + + return owned.stream(). + map(id -> neo4jTemplate.findById(id, Neo4jGroup.class)). + filter(Optional::isPresent).map(Optional::get).map(Group.class::cast).toList(); + } + + @Override + public List findOwnedByGroup(final String groupKey) { + return findByRelationship( + Neo4jGroup.NODE, + Neo4jGroup.NODE, + groupKey, + Neo4jGroup.class); + } + + @Transactional(readOnly = true) + @Override + public Collection findAllResourceKeys(final String key) { + return findById(key).map(Any::getResources). + orElse(List.of()). + stream().map(ExternalResource::getKey).toList(); + } + + @Override + public List findAMemberships(final Group group) { + return toList( + neo4jClient.query( + "MATCH (n:" + Neo4jAMembership.NODE + ")-[]-(g:" + Neo4jGroup.NODE + " {id: $id}) " + + "RETURN n.id").bindAll(Map.of("id", group.getKey())).fetch().all(), + "n.id", + Neo4jAMembership.class); + } + + @Override + public List findUMemberships(final Group group) { + return toList( + neo4jClient.query( + "MATCH (n:" + Neo4jUMembership.NODE + ")-[]-(g:" + Neo4jGroup.NODE + " {id: $id}) " + + "RETURN n.id").bindAll(Map.of("id", group.getKey())).fetch().all(), + "n.id", + Neo4jUMembership.class); + } + + @Override + public S save(final S group) { + return neo4jTemplate.save(nodeValidator.validate(group)); + } + + @Override + public Group saveAndRefreshDynMemberships(final Group group) { + Group merged = save(group); + + // refresh dynamic memberships + clearUDynMembers(merged); + if (merged.getUDynMembership() != null) { + SearchCond cond = SearchCondConverter.convert(searchCondVisitor, merged.getUDynMembership().getFIQLCond()); + long count = anySearchDAO.count( + merged.getRealm(), true, Set.of(merged.getRealm().getFullPath()), cond, AnyTypeKind.USER); + for (int page = 0; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE); page++) { + List matching = anySearchDAO.search( + merged.getRealm(), + true, + Set.of(merged.getRealm().getFullPath()), + cond, + PageRequest.of(page, AnyDAO.DEFAULT_PAGE_SIZE), + AnyTypeKind.USER); + + matching.forEach(user -> { + neo4jClient.query( + "MATCH (a:" + Neo4jUser.NODE + " {id: $aid}), (b:" + Neo4jGroup.NODE + "{id: $gid}) " + + "CREATE (a)-[:" + DYN_GROUP_USER_MEMBERSHIP_REL + "]->(b)"). + bindAll(Map.of("aid", user.getKey(), "gid", merged.getKey())).run(); + + publisher.publishEvent( + new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, user, AuthContextUtils.getDomain())); + }); + } + } + clearADynMembers(merged); + merged.getADynMemberships().forEach(memb -> { + SearchCond cond = SearchCondConverter.convert(searchCondVisitor, memb.getFIQLCond()); + long count = anySearchDAO.count( + merged.getRealm(), true, Set.of(merged.getRealm().getFullPath()), cond, AnyTypeKind.ANY_OBJECT); + for (int page = 0; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE); page++) { + List matching = anySearchDAO.search( + merged.getRealm(), + true, + Set.of(merged.getRealm().getFullPath()), + cond, + PageRequest.of(page, AnyDAO.DEFAULT_PAGE_SIZE), + AnyTypeKind.ANY_OBJECT); + + matching.forEach(any -> { + neo4jClient.query( + "MATCH (a:" + Neo4jAnyObject.NODE + " {id: $aid}), (b:" + Neo4jGroup.NODE + "{id: $gid}) " + + "CREATE (a)-[:" + DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]->(b)"). + bindAll(Map.of("aid", any.getKey(), "gid", merged.getKey())).run(); + + publisher.publishEvent( + new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, any, AuthContextUtils.getDomain())); + }); + } + }); + + dynRealmDAO.refreshDynMemberships(merged); + + return merged; + } + + @Override + public void delete(final Group group) { + dynRealmDAO.removeDynMemberships(group.getKey()); + + findAMemberships(group).forEach(membership -> { + AnyObject leftEnd = membership.getLeftEnd(); + leftEnd.remove(membership); + membership.setRightEnd(null); + leftEnd.getPlainAttrs(membership).forEach(attr -> { + leftEnd.remove(attr); + attr.setOwner(null); + attr.setMembership(null); + + plainSchemaDAO.delete(attr); + }); + neo4jTemplate.deleteById(membership.getKey(), Neo4jAMembership.class); + + anyObjectDAO.save(leftEnd); + publisher.publishEvent( + new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, leftEnd, AuthContextUtils.getDomain())); + }); + + findUMemberships(group).forEach(membership -> { + User leftEnd = membership.getLeftEnd(); + leftEnd.remove(membership); + membership.setRightEnd(null); + leftEnd.getPlainAttrs(membership).forEach(attr -> { + leftEnd.remove(attr); + attr.setOwner(null); + attr.setMembership(null); + + plainSchemaDAO.delete(attr); + }); + neo4jTemplate.deleteById(membership.getKey(), Neo4jUMembership.class); + + userDAO.save(leftEnd); + publisher.publishEvent( + new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, leftEnd, AuthContextUtils.getDomain())); + }); + + clearUDynMembers(group); + clearADynMembers(group); + + Optional.ofNullable(group.getUDynMembership()). + ifPresent(r -> neo4jTemplate.deleteById(r.getKey(), Neo4jUDynGroupMembership.class)); + + cascadeDelete( + Neo4jADynGroupMembership.NODE, + Neo4jGroup.NODE, + group.getKey(), + Neo4jADynGroupMembership.class); + + cascadeDelete( + Neo4jTypeExtension.NODE, + Neo4jGroup.NODE, + group.getKey(), + Neo4jTypeExtension.class); + + neo4jTemplate.deleteById(group.getKey(), Neo4jGroup.class); + } + + @Override + public List findTypeExtensions(final AnyTypeClass anyTypeClass) { + return findByRelationship( + Neo4jTypeExtension.NODE, + Neo4jAnyTypeClass.NODE, + anyTypeClass.getKey(), + Neo4jTypeExtension.class); + } + + @Override + public long countADynMembers(final Group group) { + return neo4jTemplate.count( + "MATCH (n)-[:" + DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]-" + + "(p:" + Neo4jGroup.NODE + " {id: $id}) " + + "RETURN COUNT(n.id)", Map.of("id", group.getKey())); + } + + @Override + public long countUDynMembers(final Group group) { + return neo4jTemplate.count( + "MATCH (n)-[:" + DYN_GROUP_USER_MEMBERSHIP_REL + "]-" + + "(p:" + Neo4jGroup.NODE + " {id: $id}) " + + "RETURN COUNT(n.id)", Map.of("id", group.getKey())); + } + + @Override + public List findADynMembers(final Group group) { + return neo4jClient.query( + "MATCH (n)-[:" + DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]-" + + "(p:" + Neo4jGroup.NODE + " {id: $id}) " + + "RETURN n.id").bindAll(Map.of("id", group.getKey())).fetch().all().stream(). + map(found -> found.get("n.id").toString()).toList(); + } + + @Override + public List findUDynMembers(final Group group) { + return neo4jClient.query( + "MATCH (n)-[:" + DYN_GROUP_USER_MEMBERSHIP_REL + "]-" + + "(p:" + Neo4jGroup.NODE + " {id: $id}) " + + "RETURN n.id").bindAll(Map.of("id", group.getKey())).fetch().all().stream(). + map(found -> found.get("n.id").toString()).toList(); + } + + @Override + public void clearADynMembers(final Group group) { + neo4jClient.query( + "MATCH (n)-[r:" + DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]-(p:" + Neo4jGroup.NODE + " {id: $id})" + + "DETACH DELETE r"). + bindAll(Map.of("id", group.getKey())).run(); + } + + @Override + public void clearUDynMembers(final Group group) { + neo4jClient.query( + "MATCH (n)-[r:" + DYN_GROUP_USER_MEMBERSHIP_REL + "]-(p:" + Neo4jGroup.NODE + " {id: $id}) " + + "DETACH DELETE r"). + bindAll(Map.of("id", group.getKey())).run(); + } + + protected List findWithADynMemberships(final AnyType anyType) { + return findByRelationship( + Neo4jADynGroupMembership.NODE, + Neo4jAnyType.NODE, + anyType.getKey(), + Neo4jADynGroupMembership.class); + } + + @Transactional + @Override + public Pair, Set> refreshDynMemberships(final AnyObject anyObject) { + Set before = new HashSet<>(); + Set after = new HashSet<>(); + findWithADynMemberships(anyObject.getType()).forEach(memb -> { + boolean matches = anyMatchDAO.matches( + anyObject, SearchCondConverter.convert(searchCondVisitor, memb.getFIQLCond())); + if (matches) { + after.add(memb.getGroup().getKey()); + } + + boolean existing = neo4jTemplate.count( + "MATCH (n:" + Neo4jAnyObject.NODE + " {id: $aid})-" + + "[r:" + DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]-" + + "(p:" + Neo4jGroup.NODE + "{id: $gid}) " + + "RETURN COUNT(n)", + Map.of("aid", anyObject.getKey(), "gid", memb.getGroup().getKey())) > 0; + if (existing) { + before.add(memb.getGroup().getKey()); + } + + if (matches && !existing) { + neo4jClient.query( + "MATCH (a:" + Neo4jAnyObject.NODE + " {id: $aid}), (b:" + Neo4jGroup.NODE + "{id: $gid}) " + + "CREATE (a)-[:" + DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]->(b)"). + bindAll(Map.of("aid", anyObject.getKey(), "gid", memb.getGroup().getKey())).run(); + } else if (!matches && existing) { + neo4jClient.query( + "MATCH (n {id: $aid})-" + + "[r:" + DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]-" + + "(p:" + Neo4jGroup.NODE + " {id: $gid}) " + + "DETACH DELETE r"). + bindAll(Map.of("aid", anyObject.getKey(), "gid", memb.getGroup().getKey())).run(); + } + + publisher.publishEvent(new EntityLifecycleEvent<>( + this, SyncDeltaType.UPDATE, memb.getGroup(), AuthContextUtils.getDomain())); + }); + + return Pair.of(before, after); + } + + @Override + public Set removeDynMemberships(final AnyObject anyObject) { + List dynGroups = anyObjectDAO.findDynGroups(anyObject.getKey()); + + neo4jClient.query( + "MATCH (n {id: $id})-[r:" + DYN_GROUP_ANY_OBJECT_MEMBERSHIP_REL + "]-(p:" + Neo4jGroup.NODE + ") " + + "DETACH DELETE r").bindAll(Map.of("id", anyObject.getKey())).run(); + + Set before = new HashSet<>(); + dynGroups.forEach(group -> { + before.add(group.getKey()); + + publisher.publishEvent(new EntityLifecycleEvent<>( + this, SyncDeltaType.UPDATE, group, AuthContextUtils.getDomain())); + }); + + return before; + } + + protected List findWithUDynMemberships() { + return neo4jTemplate.findAll(Neo4jUDynGroupMembership.class).stream(). + map(UDynGroupMembership.class::cast).toList(); + } + + @Transactional + @Override + public Pair, Set> refreshDynMemberships(final User user) { + Set before = new HashSet<>(); + Set after = new HashSet<>(); + findWithUDynMemberships().forEach(memb -> { + boolean matches = anyMatchDAO.matches( + user, SearchCondConverter.convert(searchCondVisitor, memb.getFIQLCond())); + if (matches) { + after.add(memb.getGroup().getKey()); + } + + boolean existing = neo4jTemplate.count( + "MATCH (n:" + Neo4jUser.NODE + " {id: $aid})-" + + "[r:" + DYN_GROUP_USER_MEMBERSHIP_REL + "]-" + + "(p:" + Neo4jGroup.NODE + "{id: $gid}) " + + "RETURN COUNT(n)", + Map.of("aid", user.getKey(), "gid", memb.getGroup().getKey())) > 0; + if (existing) { + before.add(memb.getGroup().getKey()); + } + + if (matches && !existing) { + neo4jClient.query( + "MATCH (a:" + Neo4jUser.NODE + " {id: $aid}), (b:" + Neo4jGroup.NODE + "{id: $gid}) " + + "CREATE (a)-[:" + DYN_GROUP_USER_MEMBERSHIP_REL + "]->(b)"). + bindAll(Map.of("aid", user.getKey(), "gid", memb.getGroup().getKey())).run(); + } else if (!matches && existing) { + neo4jClient.query( + "MATCH (n {id: $aid})-" + + "[r:" + DYN_GROUP_USER_MEMBERSHIP_REL + "]-" + + "(p:" + Neo4jGroup.NODE + " {id: $gid}) " + + "DETACH DELETE r"). + bindAll(Map.of("aid", user.getKey(), "gid", memb.getGroup().getKey())).run(); + } + + publisher.publishEvent(new EntityLifecycleEvent<>( + this, SyncDeltaType.UPDATE, memb.getGroup(), AuthContextUtils.getDomain())); + }); + + return Pair.of(before, after); + } + + @Override + public Set removeDynMemberships(final User user) { + List dynGroups = userDAO.findDynGroups(user.getKey()); + + neo4jClient.query( + "MATCH (n {id: $id})-[r:" + DYN_GROUP_USER_MEMBERSHIP_REL + "]-(p:" + Neo4jGroup.NODE + ") " + + "DETACH DELETE r").bindAll(Map.of("id", user.getKey())).run(); + + Set before = new HashSet<>(); + dynGroups.forEach(group -> { + before.add(group.getKey()); + + publisher.publishEvent(new EntityLifecycleEvent<>( + this, SyncDeltaType.UPDATE, group, AuthContextUtils.getDomain())); + }); + + return before; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ImplementationRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ImplementationRepo.java new file mode 100644 index 0000000000..724650f210 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ImplementationRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.ImplementationDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementation; +import org.springframework.data.repository.ListCrudRepository; + +public interface ImplementationRepo + extends ListCrudRepository, ImplementationRepoExt, ImplementationDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ImplementationRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ImplementationRepoExt.java new file mode 100644 index 0000000000..c637e39fa1 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ImplementationRepoExt.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.core.persistence.neo4j.dao.repo; + +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.Implementation; + +public interface ImplementationRepoExt { + + List findByTypeAndKeyword(String type, String keyword); + + Implementation save(Implementation implementation); + + void deleteById(String key); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ImplementationRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ImplementationRepoExtImpl.java new file mode 100644 index 0000000000..3ab94e00eb --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ImplementationRepoExtImpl.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.persistence.neo4j.dao.repo; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementation; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.apache.syncope.core.spring.implementation.ImplementationManager; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class ImplementationRepoExtImpl implements ImplementationRepoExt { + + protected final Neo4jTemplate neo4jTemplate; + + protected final NodeValidator nodeValidator; + + public ImplementationRepoExtImpl(final Neo4jTemplate neo4jTemplate, final NodeValidator nodeValidator) { + this.neo4jTemplate = neo4jTemplate; + this.nodeValidator = nodeValidator; + } + + @Override + public List findByTypeAndKeyword(final String type, final String keyword) { + StringBuilder queryString = new StringBuilder("MATCH (n) WHERE n.type = $type "); + + Map parameters = new HashMap<>(); + parameters.put("type", type); + + if (StringUtils.isNotBlank(keyword)) { + queryString.append("AND n.keyword =~ $keyword "); + parameters.put("keyword", keyword); + } + + queryString.append("RETURN n"); + + return neo4jTemplate.findAll(queryString.toString(), parameters, Implementation.class); + } + + @Override + public Implementation save(final Implementation implementation) { + Implementation saved = neo4jTemplate.save(nodeValidator.validate(implementation)); + + ImplementationManager.purge(saved.getKey()); + + return saved; + } + + @Override + public void deleteById(final String key) { + neo4jTemplate.findById(key, Neo4jImplementation.class).ifPresent(implementation -> { + neo4jTemplate.deleteById(key, Neo4jImplementation.class); + ImplementationManager.purge(key); + }); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/MailTemplateRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/MailTemplateRepo.java new file mode 100644 index 0000000000..cbe9562121 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/MailTemplateRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.MailTemplateDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jMailTemplate; +import org.springframework.data.repository.ListCrudRepository; + +public interface MailTemplateRepo + extends ListCrudRepository, MailTemplateDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NotificationRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NotificationRepo.java new file mode 100644 index 0000000000..7e0b5599e4 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NotificationRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.NotificationDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jNotification; +import org.springframework.data.repository.ListCrudRepository; + +public interface NotificationRepo + extends ListCrudRepository, NotificationRepoExt, NotificationDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NotificationRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NotificationRepoExt.java new file mode 100644 index 0000000000..496b33bba8 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NotificationRepoExt.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.entity.Notification; + +public interface NotificationRepoExt { + + Notification save(Notification notification); + + void deleteById(String key); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NotificationRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NotificationRepoExtImpl.java new file mode 100644 index 0000000000..30b4f8301d --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NotificationRepoExtImpl.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.persistence.neo4j.dao.repo; + +import org.apache.syncope.common.lib.types.TaskType; +import org.apache.syncope.core.persistence.api.dao.TaskDAO; +import org.apache.syncope.core.persistence.api.entity.Notification; +import org.apache.syncope.core.persistence.api.entity.task.Task; +import org.apache.syncope.core.persistence.neo4j.dao.AbstractDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyAbout; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jNotification; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.springframework.data.domain.Pageable; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class NotificationRepoExtImpl extends AbstractDAO implements NotificationRepoExt { + + protected final TaskDAO taskDAO; + + protected final NodeValidator nodeValidator; + + public NotificationRepoExtImpl( + final TaskDAO taskDAO, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super(neo4jTemplate, neo4jClient); + this.taskDAO = taskDAO; + this.nodeValidator = nodeValidator; + } + + @Override + public Notification save(final Notification notification) { + ((Neo4jNotification) notification).list2json(); + Notification saved = neo4jTemplate.save(nodeValidator.validate(notification)); + ((Neo4jNotification) saved).postSave(); + return saved; + } + + @Override + public void deleteById(final String key) { + neo4jTemplate.findById(key, Neo4jNotification.class).ifPresent(notification -> { + taskDAO.findAll( + TaskType.NOTIFICATION, null, notification, null, null, Pageable.unpaged()). + stream().map(Task::getKey).forEach(taskDAO::deleteById); + + cascadeDelete( + Neo4jAnyAbout.NODE, + Neo4jNotification.NODE, + key, Neo4jAnyAbout.class); + + neo4jTemplate.deleteById(notification.getKey(), Neo4jNotification.class); + }); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/OIDCRPClientAppRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/OIDCRPClientAppRepo.java new file mode 100644 index 0000000000..d693da1d0b --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/OIDCRPClientAppRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.OIDCRPClientAppDAO; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jOIDCRPClientApp; +import org.springframework.data.repository.ListCrudRepository; + +public interface OIDCRPClientAppRepo + extends ListCrudRepository, OIDCRPClientAppRepoExt, OIDCRPClientAppDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/OIDCRPClientAppRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/OIDCRPClientAppRepoExt.java new file mode 100644 index 0000000000..ba98788b47 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/OIDCRPClientAppRepoExt.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp; + +public interface OIDCRPClientAppRepoExt extends ClientAppRepoExt { + + OIDCRPClientApp save(OIDCRPClientApp clientApp); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/OIDCRPClientAppRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/OIDCRPClientAppRepoExtImpl.java new file mode 100644 index 0000000000..e5f1bc1564 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/OIDCRPClientAppRepoExtImpl.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.persistence.neo4j.dao.repo; + +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp; +import org.apache.syncope.core.persistence.api.entity.policy.Policy; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jOIDCRPClientApp; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class OIDCRPClientAppRepoExtImpl + extends AbstractClientRepoExt + implements OIDCRPClientAppRepoExt { + + protected final NodeValidator nodeValidator; + + public OIDCRPClientAppRepoExtImpl( + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super(neo4jTemplate, neo4jClient); + this.nodeValidator = nodeValidator; + } + + @Override + public List findAllByPolicy(final Policy policy) { + return findAllByPolicy(policy, Neo4jOIDCRPClientApp.NODE, Neo4jOIDCRPClientApp.class); + } + + @Override + public List findAllByRealm(final Realm realm) { + return findAllByRealm(realm, Neo4jOIDCRPClientApp.NODE, Neo4jOIDCRPClientApp.class); + } + + @Override + public OIDCRPClientApp save(final OIDCRPClientApp clientApp) { + ((Neo4jOIDCRPClientApp) clientApp).list2json(); + OIDCRPClientApp saved = neo4jTemplate.save(nodeValidator.validate(clientApp)); + ((Neo4jOIDCRPClientApp) saved).postSave(); + return saved; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/PlainSchemaRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/PlainSchemaRepo.java new file mode 100644 index 0000000000..22db63d147 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/PlainSchemaRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jPlainSchema; +import org.springframework.data.repository.ListCrudRepository; + +public interface PlainSchemaRepo + extends ListCrudRepository, PlainSchemaRepoExt, PlainSchemaDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/PlainSchemaRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/PlainSchemaRepoExt.java new file mode 100644 index 0000000000..87f09c415c --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/PlainSchemaRepoExt.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.persistence.neo4j.dao.repo; + +import java.util.Collection; +import java.util.List; +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.PlainSchema; + +public interface PlainSchemaRepoExt { + + List findByAnyTypeClasses(Collection anyTypeClasses); + + > boolean hasAttrs(PlainSchema schema, Class reference); + + PlainSchema save(PlainSchema schema); + + void deleteById(String key); + + > void delete(T plainAttr); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/PlainSchemaRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/PlainSchemaRepoExtImpl.java new file mode 100644 index 0000000000..fbac03e53c --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/PlainSchemaRepoExtImpl.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.persistence.neo4j.dao.repo; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; +import org.apache.syncope.core.persistence.api.entity.Attributable; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jPlainSchema; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jSchema; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAPlainAttr; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAnyObject; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGPlainAttr; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jLinkedAccount; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class PlainSchemaRepoExtImpl extends AbstractSchemaRepoExt implements PlainSchemaRepoExt { + + protected final AnyUtilsFactory anyUtilsFactory; + + protected final ExternalResourceDAO resourceDAO; + + public PlainSchemaRepoExtImpl( + final AnyUtilsFactory anyUtilsFactory, + final ExternalResourceDAO resourceDAO, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super(neo4jTemplate, neo4jClient, nodeValidator); + this.anyUtilsFactory = anyUtilsFactory; + this.resourceDAO = resourceDAO; + } + + @Override + public List findByAnyTypeClasses(final Collection anyTypeClasses) { + return findByAnyTypeClasses(anyTypeClasses, Neo4jPlainSchema.class, PlainSchema.class); + } + + @Override + public > boolean hasAttrs(final PlainSchema schema, final Class reference) { + String label; + if (reference.isAssignableFrom(Neo4jGPlainAttr.class)) { + label = Neo4jGroup.NODE; + } else if (reference.isAssignableFrom(Neo4jAPlainAttr.class)) { + label = Neo4jAnyObject.NODE; + } else if (reference.isAssignableFrom(Neo4jAPlainAttr.class)) { + label = Neo4jLinkedAccount.NODE; + } else { + label = Neo4jUser.NODE; + } + + return neo4jTemplate.count( + "MATCH (n:" + label + ") " + + "WHERE n.`plainAttrs." + schema.getKey() + "` " + + "IS NOT NULL RETURN COUNT(n)") > 0; + } + + @Override + public PlainSchema save(final PlainSchema schema) { + ((Neo4jSchema) schema).map2json(); + PlainSchema saved = neo4jTemplate.save(nodeValidator.validate(schema)); + ((Neo4jSchema) saved).postSave(); + return saved; + } + + @Override + public void deleteById(final String key) { + neo4jTemplate.findById(key, Neo4jPlainSchema.class).ifPresent(schema -> { + resourceDAO.deleteMapping(key); + Optional.ofNullable(schema.getAnyTypeClass()).ifPresent(c -> c.getPlainSchemas().remove(schema)); + + neo4jTemplate.deleteById(key, Neo4jPlainSchema.class); + }); + } + + @Override + @SuppressWarnings("unchecked") + public > void delete(final T plainAttr) { + if (plainAttr.getOwner() != null) { + ((Attributable) plainAttr.getOwner()).remove(plainAttr); + } + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RelationshipTypeRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RelationshipTypeRepo.java new file mode 100644 index 0000000000..22890af0cd --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RelationshipTypeRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRelationshipType; +import org.springframework.data.repository.ListCrudRepository; + +public interface RelationshipTypeRepo + extends ListCrudRepository, RelationshipTypeRepoExt, RelationshipTypeDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RelationshipTypeRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RelationshipTypeRepoExt.java new file mode 100644 index 0000000000..d0540bcf3b --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RelationshipTypeRepoExt.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +public interface RelationshipTypeRepoExt { + + void deleteById(String key); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RelationshipTypeRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RelationshipTypeRepoExtImpl.java new file mode 100644 index 0000000000..88f89f9d8c --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RelationshipTypeRepoExtImpl.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.persistence.neo4j.dao.repo; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.Relationship; +import org.apache.syncope.core.persistence.api.entity.RelationshipType; +import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership; +import org.apache.syncope.core.persistence.api.entity.anyobject.ARelationship; +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.neo4j.dao.AbstractDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRelationshipType; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jARelationship; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jURelationship; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class RelationshipTypeRepoExtImpl extends AbstractDAO implements RelationshipTypeRepoExt { + + public RelationshipTypeRepoExtImpl(final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient) { + super(neo4jTemplate, neo4jClient); + } + + protected Collection> findRelationshipsByType(final RelationshipType type) { + List> result = new ArrayList<>(); + + result.addAll(findByRelationship( + Neo4jURelationship.NODE, Neo4jRelationshipType.NODE, type.getKey(), Neo4jURelationship.class)); + result.addAll(findByRelationship( + Neo4jARelationship.NODE, Neo4jRelationshipType.NODE, type.getKey(), Neo4jARelationship.class)); + + return result; + } + + @Override + public void deleteById(final String key) { + neo4jTemplate.findById(key, Neo4jRelationshipType.class).ifPresent(type -> { + findRelationshipsByType(type).stream().map(relationship -> { + switch (relationship) { + case URelationship uRelationship -> + uRelationship.getLeftEnd().getRelationships().remove(uRelationship); + case UMembership uMembership -> + uMembership.getLeftEnd().remove(uMembership); + case ARelationship aRelationship -> + aRelationship.getLeftEnd().getRelationships().remove(aRelationship); + case AMembership aMembership -> + aMembership.getLeftEnd().remove(aMembership); + default -> { + } + } + relationship.setLeftEnd(null); + return relationship; + }).forEach(r -> neo4jTemplate.deleteById(r.getKey(), r.getClass())); + + neo4jTemplate.deleteById(key, Neo4jRelationshipType.class); + }); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RemediationRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RemediationRepo.java new file mode 100644 index 0000000000..b560482513 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RemediationRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.RemediationDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRemediation; +import org.springframework.data.repository.ListCrudRepository; + +public interface RemediationRepo + extends ListCrudRepository, RemediationRepoExt, RemediationDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RemediationRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RemediationRepoExt.java new file mode 100644 index 0000000000..815e6d81d9 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RemediationRepoExt.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import java.time.OffsetDateTime; +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.Remediation; +import org.apache.syncope.core.persistence.api.entity.task.PullTask; +import org.springframework.data.domain.Pageable; + +public interface RemediationRepoExt { + + List findByAnyType(AnyType anyType); + + List findByPullTask(PullTask pullTask); + + long count(OffsetDateTime before, OffsetDateTime after); + + List findAll( + OffsetDateTime before, + OffsetDateTime after, + Pageable pageable); + + void deleteById(String key); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RemediationRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RemediationRepoExtImpl.java new file mode 100644 index 0000000000..ef554d332e --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RemediationRepoExtImpl.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.persistence.neo4j.dao.repo; + +import java.lang.reflect.Field; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.apache.syncope.core.persistence.api.entity.AnyType; +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.neo4j.dao.AbstractDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyType; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRemediation; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jPullTask; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ReflectionUtils; + +public class RemediationRepoExtImpl extends AbstractDAO implements RemediationRepoExt { + + protected static final Logger LOG = LoggerFactory.getLogger(RemediationRepoExt.class); + + protected final NodeValidator nodeValidator; + + public RemediationRepoExtImpl( + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super(neo4jTemplate, neo4jClient); + this.nodeValidator = nodeValidator; + } + + @Override + public List findByAnyType(final AnyType anyType) { + return findByRelationship(Neo4jRemediation.NODE, Neo4jAnyType.NODE, anyType.getKey(), Neo4jRemediation.class); + } + + @Override + public List findByPullTask(final PullTask pullTask) { + return findByRelationship(Neo4jRemediation.NODE, Neo4jPullTask.NODE, pullTask.getKey(), Neo4jRemediation.class); + } + + protected StringBuilder query( + final OffsetDateTime before, + final OffsetDateTime after, + final Map parameters) { + + StringBuilder query = new StringBuilder("MATCH (n:").append(Neo4jRemediation.NODE). + append(")-[r]-(e:").append(Neo4jExternalResource.NODE).append(")"); + + List conditions = new ArrayList<>(); + if (before != null) { + conditions.add("e.instant <= $before"); + parameters.put("before", before); + } + if (after != null) { + conditions.add("AND e.instant >= $after "); + parameters.put("after", after); + } + + if (!conditions.isEmpty()) { + query.append("WHERE ").append(conditions.stream().collect(Collectors.joining(" AND "))); + } + + return query; + } + + @Override + public long count(final OffsetDateTime before, final OffsetDateTime after) { + Map parameters = new HashMap<>(); + + StringBuilder queryString = query(before, after, parameters).append(" RETURN COUNT(n)"); + return neo4jTemplate.count(queryString.toString(), parameters); + } + + @Override + public List findAll( + final OffsetDateTime before, + final OffsetDateTime after, + final Pageable pageable) { + + Map parameters = new HashMap<>(); + + StringBuilder queryString = query(before, after, parameters).append(" RETURN n.id "); + + if (!pageable.getSort().isEmpty()) { + queryString.append(" ORDER BY "); + pageable.getSort().forEach(clause -> { + String field = clause.getProperty().trim(); + boolean ack = true; + if ("resource".equals(field)) { + queryString.append("e.id"); + } else { + Field beanField = ReflectionUtils.findField(Neo4jRemediation.class, field); + if (beanField == null) { + ack = false; + LOG.warn("Remediation sort request by {}: unsupported, ignoring", field); + } else { + queryString.append("n.").append(field); + } + } + if (ack) { + if (clause.getDirection() == Sort.Direction.ASC) { + queryString.append(" ASC"); + } else { + queryString.append(" DESC"); + } + queryString.append(','); + } + }); + + queryString.deleteCharAt(queryString.length() - 1); + } + + if (pageable.isPaged()) { + queryString.append(" SKIP ").append(pageable.getPageSize() * pageable.getPageNumber()). + append(" LIMIT ").append(pageable.getPageSize()); + } + + return toList(neo4jClient.query( + queryString.toString()).bindAll(parameters).fetch().all(), "n.id", Neo4jRemediation.class); + } + + @Transactional + @Override + public void deleteById(final String key) { + neo4jTemplate.deleteById(key, Neo4jRemediation.class); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportExecRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportExecRepo.java new file mode 100644 index 0000000000..3aa7a68409 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportExecRepo.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.ReportExecDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jReportExec; +import org.springframework.data.repository.ListCrudRepository; + +public interface ReportExecRepo + extends ListCrudRepository, ReportExecRepoExt, ReportExecDAO { +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportExecRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportExecRepoExt.java new file mode 100644 index 0000000000..0f934991e3 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportExecRepoExt.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.persistence.neo4j.dao.repo; + +import java.time.OffsetDateTime; +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.Report; +import org.apache.syncope.core.persistence.api.entity.ReportExec; +import org.springframework.data.domain.Pageable; + +public interface ReportExecRepoExt { + + List findRecent(int max); + + ReportExec findLatestStarted(Report report); + + ReportExec findLatestEnded(Report report); + + long count(Report report, OffsetDateTime before, OffsetDateTime after); + + List findAll( + Report report, + OffsetDateTime before, + OffsetDateTime after, + Pageable pageable); + + void deleteById(String key); + + void delete(ReportExec execution); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportExecRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportExecRepoExtImpl.java new file mode 100644 index 0000000000..70afe3dec9 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportExecRepoExtImpl.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.persistence.neo4j.dao.repo; + +import java.time.OffsetDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; +import org.apache.syncope.core.persistence.api.entity.Report; +import org.apache.syncope.core.persistence.api.entity.ReportExec; +import org.apache.syncope.core.persistence.neo4j.dao.AbstractDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jReport; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jReportExec; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.util.ReflectionUtils; + +public class ReportExecRepoExtImpl extends AbstractDAO implements ReportExecRepoExt { + + protected final NodeValidator nodeValidator; + + public ReportExecRepoExtImpl( + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super(neo4jTemplate, neo4jClient); + this.nodeValidator = nodeValidator; + } + + @Override + public List findRecent(final int max) { + return toList(neo4jClient.query( + "MATCH (n:" + Neo4jReportExec.NODE + ")-" + + "[:" + Neo4jReport.REPORT_EXEC_REL + "]-" + + "(p:" + Neo4jReport.NODE + " {id: $id}) " + + "WHERE n.endDate IS NOT NULL " + + "ORDER BY n.end DateDESC LIMIT " + max + " RETURN n.id").fetch().all(), + "n.id", + Neo4jReportExec.class); + } + + protected ReportExec findLatest(final Report report, final String field) { + return neo4jClient.query( + "MATCH (n:" + Neo4jReportExec.NODE + ")-" + + "[:" + Neo4jReport.REPORT_EXEC_REL + "]-" + + "(p:" + Neo4jReport.NODE + " {id: $id}) " + + "ORDER BY n." + field + " DESC LIMIT 1 RETURN n.id"). + bindAll(Map.of("id", report.getKey())).fetch().one(). + flatMap(super.toOptional("n.id", Neo4jReportExec.class)). + orElse(null); + } + + @Override + public ReportExec findLatestStarted(final Report report) { + return findLatest(report, "startDate"); + } + + @Override + public ReportExec findLatestEnded(final Report report) { + return findLatest(report, "endDate"); + } + + protected StringBuilder query( + final Report report, + final OffsetDateTime before, + final OffsetDateTime after, + final Map parameters) { + + parameters.put("id", report.getKey()); + + StringBuilder query = new StringBuilder( + "MATCH (n:").append(Neo4jReportExec.NODE).append(")-"). + append("[:").append(Neo4jReport.REPORT_EXEC_REL).append("]-"). + append("(p:").append(Neo4jReport.NODE).append(" {id: $id}) WHERE 1=1 "); + + if (before != null) { + query.append("AND e.startDate <= $before "); + parameters.put("before", before); + } + if (after != null) { + query.append("AND e.startDate >= $after "); + parameters.put("after", after); + } + + return query; + } + + @Override + public long count( + final Report report, + final OffsetDateTime before, + final OffsetDateTime after) { + + Map parameters = new HashMap<>(); + + StringBuilder queryString = query(report, before, after, parameters).append(" RETURN COUNT(n)"); + return neo4jTemplate.count(queryString.toString(), parameters); + } + + protected String toOrderByStatement(final Stream orderByClauses) { + StringBuilder statement = new StringBuilder(); + + orderByClauses.forEach(clause -> { + String field = clause.getProperty().trim(); + if (ReflectionUtils.findField(Neo4jReportExec.class, field) != null) { + statement.append("e.").append(field).append(' ').append(clause.getDirection().name()); + } + }); + + if (statement.length() == 0) { + statement.append("ORDER BY n.id DESC"); + } else { + statement.insert(0, "ORDER BY "); + } + return statement.toString(); + } + + @Override + public List findAll( + final Report report, + final OffsetDateTime before, + final OffsetDateTime after, + final Pageable pageable) { + + Map parameters = new HashMap<>(); + + StringBuilder queryString = query(report, before, after, parameters). + append(" RETURN n.id ").append(toOrderByStatement(pageable.getSort().stream())); + if (pageable.isPaged()) { + queryString.append(" SKIP ").append(pageable.getPageSize() * pageable.getPageNumber()). + append(" LIMIT ").append(pageable.getPageSize()); + } + + return toList(neo4jClient.query( + queryString.toString()).bindAll(parameters).fetch().all(), "n.id", Neo4jReportExec.class); + } + + @Override + public void deleteById(final String key) { + neo4jTemplate.findById(key, Neo4jReportExec.class).ifPresent(this::delete); + } + + @Override + public void delete(final ReportExec execution) { + Optional.ofNullable(execution.getReport()).ifPresent(report -> report.getExecs().remove(execution)); + neo4jTemplate.deleteById(execution.getKey(), Neo4jReportExec.class); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportRepo.java new file mode 100644 index 0000000000..94b2b38fcd --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.ReportDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jReport; +import org.springframework.data.repository.ListCrudRepository; + +public interface ReportRepo + extends ListCrudRepository, ReportRepoExt, ReportDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportRepoExt.java new file mode 100644 index 0000000000..1f990c79bb --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportRepoExt.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.entity.Report; + +public interface ReportRepoExt { + + void deleteById(String key); + + void delete(Report report); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportRepoExtImpl.java new file mode 100644 index 0000000000..62fd3ec739 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ReportRepoExtImpl.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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.entity.Report; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jReport; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jReportExec; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class ReportRepoExtImpl implements ReportRepoExt { + + protected final Neo4jTemplate neo4jTemplate; + + public ReportRepoExtImpl(final Neo4jTemplate neo4jTemplate) { + this.neo4jTemplate = neo4jTemplate; + } + + @Override + public void deleteById(final String key) { + neo4jTemplate.findById(key, Neo4jReport.class).ifPresent(this::delete); + } + + @Override + public void delete(final Report report) { + report.getExecs().forEach(exec -> neo4jTemplate.deleteById(exec.getKey(), Neo4jReportExec.class)); + + neo4jTemplate.deleteById(report.getKey(), Neo4jReport.class); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RoleRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RoleRepo.java new file mode 100644 index 0000000000..62cf246642 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RoleRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.RoleDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRole; +import org.springframework.data.repository.ListCrudRepository; + +public interface RoleRepo + extends ListCrudRepository, RoleRepoExt, RoleDAO { + +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RoleRepoExt.java similarity index 54% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepoExt.java rename to core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RoleRepoExt.java index 4b820d077c..d4fb9f39e5 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepoExt.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RoleRepoExt.java @@ -16,35 +16,33 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao.repo; +package org.apache.syncope.core.persistence.neo4j.dao.repo; import java.util.List; -import java.util.Optional; +import org.apache.syncope.core.persistence.api.entity.Privilege; import org.apache.syncope.core.persistence.api.entity.Realm; -import org.apache.syncope.core.persistence.api.entity.policy.Policy; -import org.springframework.data.domain.Pageable; +import org.apache.syncope.core.persistence.api.entity.Role; +import org.apache.syncope.core.persistence.api.entity.user.User; -public interface RealmRepoExt { +public interface RoleRepoExt { - Realm getRoot(); + String DYN_ROLE_MEMBERSHIP_REL = "DYN_ROLE_MEMBERSHIP"; - Optional findByFullPath(String fullPath); + List findByRealms(Realm realm); - List findByName(String name); + List findByPrivileges(Privilege privilege); - long countDescendants(String base, String keyword); + Role save(Role role); - List findDescendants(String base, String keyword, Pageable pageable); + void delete(Role role); - List findDescendants(String base, String prefix); + Role saveAndRefreshDynMemberships(Role role); - List findByPolicy(T policy); + List findDynMembers(Role role); - List findAncestors(Realm realm); + void clearDynMembers(Role role); - List findChildren(Realm realm); + void refreshDynMemberships(User user); - Realm save(Realm realm); - - void delete(Realm realm); + void removeDynMemberships(String key); } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RoleRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RoleRepoExtImpl.java new file mode 100644 index 0000000000..4a2c595fc6 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RoleRepoExtImpl.java @@ -0,0 +1,201 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import java.util.List; +import java.util.Map; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO; +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.entity.Privilege; +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.persistence.api.search.SearchCondConverter; +import org.apache.syncope.core.persistence.api.search.SearchCondVisitor; +import org.apache.syncope.core.persistence.neo4j.dao.AbstractDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jPrivilege; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRole; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.apache.syncope.core.provisioning.api.event.EntityLifecycleEvent; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.identityconnectors.framework.common.objects.SyncDeltaType; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; + +public class RoleRepoExtImpl extends AbstractDAO implements RoleRepoExt { + + protected final ApplicationEventPublisher publisher; + + protected final AnyMatchDAO anyMatchDAO; + + protected final AnySearchDAO anySearchDAO; + + protected final DelegationDAO delegationDAO; + + protected final SearchCondVisitor searchCondVisitor; + + protected final NodeValidator nodeValidator; + + public RoleRepoExtImpl( + final ApplicationEventPublisher publisher, + final AnyMatchDAO anyMatchDAO, + final AnySearchDAO anySearchDAO, + final DelegationDAO delegationDAO, + final SearchCondVisitor searchCondVisitor, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super(neo4jTemplate, neo4jClient); + this.publisher = publisher; + this.anyMatchDAO = anyMatchDAO; + this.anySearchDAO = anySearchDAO; + this.delegationDAO = delegationDAO; + this.searchCondVisitor = searchCondVisitor; + this.nodeValidator = nodeValidator; + } + + @Override + public List findByPrivileges(final Privilege privilege) { + return findByRelationship(Neo4jRole.NODE, Neo4jPrivilege.NODE, privilege.getKey(), Neo4jRole.class); + } + + @Override + public List findByRealms(final Realm realm) { + return findByRelationship(Neo4jRole.NODE, Neo4jRealm.NODE, realm.getKey(), Neo4jRole.class); + } + + @Override + public Role save(final Role role) { + ((Neo4jRole) role).list2json(); + Role saved = neo4jTemplate.save(nodeValidator.validate(role)); + ((Neo4jRole) saved).postSave(); + return saved; + } + + @Override + public Role saveAndRefreshDynMemberships(final Role role) { + Role merged = save(role); + + // refresh dynamic memberships + clearDynMembers(merged); + + if (merged.getDynMembershipCond() != null) { + List matching = anySearchDAO.search( + SearchCondConverter.convert(searchCondVisitor, merged.getDynMembershipCond()), + AnyTypeKind.USER); + + matching.forEach(user -> { + neo4jClient.query( + "MATCH (a:" + Neo4jUser.NODE + " {id: $aid}), (b:" + Neo4jRole.NODE + "{id: $rid}) " + + "CREATE (a)-[:" + DYN_ROLE_MEMBERSHIP_REL + "]->(b)"). + bindAll(Map.of("aid", user.getKey(), "rid", merged.getKey())).run(); + + publisher.publishEvent( + new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, user, AuthContextUtils.getDomain())); + }); + } + + return merged; + } + + @Override + public void delete(final Role role) { + List users = findByRelationship( + Neo4jUser.NODE, Neo4jRole.NODE, role.getKey(), Neo4jUser.ROLE_MEMBERSHIP_REL, Neo4jUser.class); + + users.forEach(user -> { + user.getRoles().remove(role); + publisher.publishEvent( + new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, user, AuthContextUtils.getDomain())); + }); + + clearDynMembers(role); + + delegationDAO.findByRoles(role).forEach(delegation -> delegation.getRoles().remove(role)); + neo4jTemplate.deleteById(role.getKey(), Neo4jRole.class); + } + + @Override + public List findDynMembers(final Role role) { + if (role.getDynMembershipCond() == null) { + return List.of(); + } + + return neo4jClient.query( + "MATCH (n)-[:" + DYN_ROLE_MEMBERSHIP_REL + "]-(p:" + Neo4jRole.NODE + " {id: $id}) " + + "RETURN n.id"). + bindAll(Map.of("id", role.getKey())). + fetch().all().stream().map(found -> found.get("n.id").toString()).toList(); + } + + @Override + public void clearDynMembers(final Role role) { + neo4jClient.query( + "MATCH (n)-[r:" + DYN_ROLE_MEMBERSHIP_REL + "]-(p:" + Neo4jRole.NODE + " {id: $id}) " + + "DETACH DELETE r"). + bindAll(Map.of("id", role.getKey())).run(); + } + + @Transactional + @Override + public void refreshDynMemberships(final User user) { + List roles = toList(neo4jClient.query("MATCH (n:" + Neo4jRole.NODE + ") " + + "WHERE n.dynMembershipCond IS NOT NULL " + + "RETURN n.id").fetch().all(), + "n.id", + Neo4jRole.class); + roles.forEach(role -> { + boolean matches = anyMatchDAO.matches( + user, + SearchCondConverter.convert(searchCondVisitor, role.getDynMembershipCond())); + + boolean existing = neo4jTemplate.count( + "MATCH (n)-[:" + DYN_ROLE_MEMBERSHIP_REL + "]-(p:" + Neo4jRole.NODE + "{id: $id}) " + + "RETURN COUNT(n)", + Map.of("id", role.getKey())) > 0; + + if (matches && !existing) { + neo4jClient.query( + "MATCH (a:" + Neo4jUser.NODE + " {id: $aid}), (b:" + Neo4jRole.NODE + "{id: $rid}) " + + "CREATE (a)-[:" + DYN_ROLE_MEMBERSHIP_REL + "]->(b)"). + bindAll(Map.of("aid", user.getKey(), "rid", role.getKey())).run(); + } else if (!matches && existing) { + neo4jClient.query( + "MATCH (n {id: $aid})-" + + "[:" + DYN_ROLE_MEMBERSHIP_REL + "]-" + + "(p:" + Neo4jRole.NODE + " {id: $rid}) " + + "DETACH DELETE r").bindAll(Map.of("aid", user.getKey(), "rid", role.getKey())).run(); + } + }); + } + + @Override + public void removeDynMemberships(final String key) { + neo4jClient.query( + "MATCH (n {id: $id})-[r:" + DYN_ROLE_MEMBERSHIP_REL + "]-(p:" + Neo4jRole.NODE + ") " + + "DETACH DELETE r").bindAll(Map.of("id", key)).run(); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2IdPEntityRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2IdPEntityRepo.java new file mode 100644 index 0000000000..7d54e59ad3 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2IdPEntityRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.SAML2IdPEntityDAO; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jSAML2IdPEntity; +import org.springframework.data.repository.ListCrudRepository; + +public interface SAML2IdPEntityRepo + extends ListCrudRepository, SAML2IdPEntityDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2SPClientAppRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2SPClientAppRepo.java new file mode 100644 index 0000000000..0f1a24dd83 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2SPClientAppRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.SAML2SPClientAppDAO; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jSAML2SPClientApp; +import org.springframework.data.repository.ListCrudRepository; + +public interface SAML2SPClientAppRepo + extends ListCrudRepository, SAML2SPClientAppRepoExt, SAML2SPClientAppDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2SPClientAppRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2SPClientAppRepoExt.java new file mode 100644 index 0000000000..e2ba56b0d6 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2SPClientAppRepoExt.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp; + +public interface SAML2SPClientAppRepoExt extends ClientAppRepoExt { + + SAML2SPClientApp save(SAML2SPClientApp clientApp); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2SPClientAppRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2SPClientAppRepoExtImpl.java new file mode 100644 index 0000000000..a06168731d --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2SPClientAppRepoExtImpl.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.persistence.neo4j.dao.repo; + +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp; +import org.apache.syncope.core.persistence.api.entity.policy.Policy; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jSAML2SPClientApp; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class SAML2SPClientAppRepoExtImpl + extends AbstractClientRepoExt + implements SAML2SPClientAppRepoExt { + + protected final NodeValidator nodeValidator; + + public SAML2SPClientAppRepoExtImpl( + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super(neo4jTemplate, neo4jClient); + this.nodeValidator = nodeValidator; + } + + @Override + public List findAllByPolicy(final Policy policy) { + return findAllByPolicy(policy, Neo4jSAML2SPClientApp.NODE, Neo4jSAML2SPClientApp.class); + } + + @Override + public List findAllByRealm(final Realm realm) { + return findAllByRealm(realm, Neo4jSAML2SPClientApp.NODE, Neo4jSAML2SPClientApp.class); + } + + @Override + public SAML2SPClientApp save(final SAML2SPClientApp clientApp) { + ((Neo4jSAML2SPClientApp) clientApp).list2json(); + SAML2SPClientApp saved = neo4jTemplate.save(nodeValidator.validate(clientApp)); + ((Neo4jSAML2SPClientApp) saved).postSave(); + return saved; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2SPEntityRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2SPEntityRepo.java new file mode 100644 index 0000000000..b76b7daa3d --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SAML2SPEntityRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.SAML2SPEntityDAO; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jSAML2SPEntity; +import org.springframework.data.repository.ListCrudRepository; + +public interface SAML2SPEntityRepo + extends ListCrudRepository, SAML2SPEntityDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SRARouteRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SRARouteRepo.java new file mode 100644 index 0000000000..dcb20442a5 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SRARouteRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.SRARouteDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jSRARoute; +import org.springframework.data.repository.ListCrudRepository; + +public interface SRARouteRepo + extends ListCrudRepository, SRARouteDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SecurityQuestionRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SecurityQuestionRepo.java new file mode 100644 index 0000000000..46f5d225f1 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SecurityQuestionRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.SecurityQuestionDAO; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jSecurityQuestion; +import org.springframework.data.repository.ListCrudRepository; + +public interface SecurityQuestionRepo + extends ListCrudRepository, SecurityQuestionRepoExt, SecurityQuestionDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SecurityQuestionRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SecurityQuestionRepoExt.java new file mode 100644 index 0000000000..7959811c76 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SecurityQuestionRepoExt.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +public interface SecurityQuestionRepoExt { + + void deleteById(String key); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SecurityQuestionRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SecurityQuestionRepoExtImpl.java new file mode 100644 index 0000000000..4f112901d9 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/SecurityQuestionRepoExtImpl.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.core.persistence.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jSecurityQuestion; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class SecurityQuestionRepoExtImpl implements SecurityQuestionRepoExt { + + protected final UserDAO userDAO; + + protected final Neo4jTemplate neo4jTemplate; + + public SecurityQuestionRepoExtImpl(final UserDAO userDAO, final Neo4jTemplate neo4jTemplate) { + this.userDAO = userDAO; + this.neo4jTemplate = neo4jTemplate; + } + + @Override + public void deleteById(final String key) { + neo4jTemplate.findById(key, Neo4jSecurityQuestion.class).ifPresent(securityQuestion -> { + + userDAO.findBySecurityQuestion(securityQuestion).forEach(user -> { + user.setSecurityQuestion(null); + user.setSecurityAnswer(null); + userDAO.save(user); + }); + + neo4jTemplate.deleteById(key, Neo4jSecurityQuestion.class); + }); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepo.java new file mode 100644 index 0000000000..9bea10fc75 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepo.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.persistence.neo4j.dao.repo; + +import java.util.Optional; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; +import org.springframework.data.neo4j.repository.query.Query; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.repository.query.Param; + +public interface UserRepo + extends PagingAndSortingRepository, UserRepoExt, UserDAO { + + @Query("MATCH (n:" + Neo4jUser.NODE + ") WHERE n.username = $username RETURN n.id") + @Override + Optional findKey(@Param("username") String username); + + @Query("MATCH (n:" + Neo4jUser.NODE + ") WHERE n.id = $key RETURN n.username") + @Override + Optional findUsername(@Param("key") String key); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExt.java new file mode 100644 index 0000000000..d56dd9d732 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExt.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.persistence.neo4j.dao.repo; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.Privilege; +import org.apache.syncope.core.persistence.api.entity.Role; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount; +import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion; +import org.apache.syncope.core.persistence.api.entity.user.UMembership; +import org.apache.syncope.core.persistence.api.entity.user.User; + +public interface UserRepoExt extends AnyRepoExt { + + Optional findByToken(String token); + + List findBySecurityQuestion(SecurityQuestion securityQuestion); + + void securityChecks(Set authRealms, String key, String realm, Collection groups); + + Map countByRealm(); + + Map countByStatus(); + + UMembership findMembership(String key); + + List findDynRoles(String key); + + Collection findAllRoles(User user); + + List findDynGroups(String key); + + Collection findAllGroups(User user); + + Collection findAllGroupKeys(User user); + + Collection findAllGroupNames(User user); + + Collection findAllResources(User user); + + Pair, Set> saveAndGetDynGroupMembs(User user); + + boolean linkedAccountExists(String userKey, String connObjectKeyValue); + + Optional findLinkedAccount(ExternalResource resource, String connObjectKeyValue); + + List findLinkedAccounts(String userKey); + + List findLinkedAccountsByPrivilege(Privilege privilege); + + List findLinkedAccountsByResource(ExternalResource resource); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExtImpl.java new file mode 100644 index 0000000000..431d456ddc --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExtImpl.java @@ -0,0 +1,402 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import java.time.OffsetDateTime; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; +import org.apache.syncope.core.persistence.api.dao.DelegationDAO; +import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; +import org.apache.syncope.core.persistence.api.dao.FIQLQueryDAO; +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.RoleDAO; +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.Privilege; +import org.apache.syncope.core.persistence.api.entity.Role; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount; +import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion; +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.apache.syncope.core.persistence.api.utils.RealmUtils; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jPrivilege; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRole; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jLinkedAccount; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jSecurityQuestion; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUMembership; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jURelationship; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.apache.syncope.core.spring.security.DelegatedAdministrationException; +import org.apache.syncope.core.spring.security.SecurityProperties; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +public class UserRepoExtImpl extends AbstractAnyRepoExt implements UserRepoExt { + + protected final RoleDAO roleDAO; + + protected final AccessTokenDAO accessTokenDAO; + + protected final GroupDAO groupDAO; + + protected final DelegationDAO delegationDAO; + + protected final FIQLQueryDAO fiqlQueryDAO; + + protected final SecurityProperties securityProperties; + + protected final NodeValidator nodeValidator; + + public UserRepoExtImpl( + final AnyUtilsFactory anyUtilsFactory, + final PlainSchemaDAO plainSchemaDAO, + final DerSchemaDAO derSchemaDAO, + final DynRealmDAO dynRealmDAO, + final RoleDAO roleDAO, + final AccessTokenDAO accessTokenDAO, + final GroupDAO groupDAO, + final DelegationDAO delegationDAO, + final FIQLQueryDAO fiqlQueryDAO, + final SecurityProperties securityProperties, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super( + plainSchemaDAO, + derSchemaDAO, + dynRealmDAO, + anyUtilsFactory.getInstance(AnyTypeKind.USER), + neo4jTemplate, + neo4jClient); + this.roleDAO = roleDAO; + this.accessTokenDAO = accessTokenDAO; + this.groupDAO = groupDAO; + this.delegationDAO = delegationDAO; + this.fiqlQueryDAO = fiqlQueryDAO; + this.securityProperties = securityProperties; + this.nodeValidator = nodeValidator; + } + + @Override + public Optional findByToken(final String token) { + return neo4jClient.query( + "MATCH (n:" + Neo4jUser.NODE + ") WHERE n.token = $token RETURN n.id"). + bindAll(Map.of("token", token)).fetch().one(). + flatMap(toOptional("n.id", Neo4jUser.class)); + } + + @Override + public List findBySecurityQuestion(final SecurityQuestion securityQuestion) { + return findByRelationship( + Neo4jUser.NODE, + Neo4jSecurityQuestion.NODE, + securityQuestion.getKey(), + Neo4jUser.class); + } + + @Transactional(readOnly = true) + @Override + public Optional findLastChange(final String key) { + return findLastChange(key, Neo4jUser.NODE); + } + + @Transactional(readOnly = true) + @Override + public void securityChecks( + final Set authRealms, + final String key, + final String realm, + final Collection groups) { + + // 1. check if AuthContextUtils.getUsername() is owner of at least one group of which user is member + boolean authorized = authRealms.stream(). + map(authRealm -> RealmUtils.parseGroupOwnerRealm(authRealm).orElse(null)). + filter(Objects::nonNull). + anyMatch(pair -> groups.contains(pair.getRight())); + + // 2. check if user is in at least one DynRealm for which AuthContextUtils.getUsername() owns entitlement + if (!authorized) { + authorized = findDynRealms(key).stream().anyMatch(authRealms::contains); + } + + // 3. check if user is in Realm (or descendants) for which AuthContextUtils.getUsername() owns entitlement + if (!authorized) { + authorized = authRealms.stream().anyMatch(realm::startsWith); + } + + if (!authorized) { + throw new DelegatedAdministrationException(realm, AnyTypeKind.USER.name(), key); + } + } + + @Override + protected void securityChecks(final User user) { + // Allows anonymous (during self-registration) and self (during self-update) to read own user, + // otherwise goes through security checks to see if required entitlements are owned + if (!AuthContextUtils.getUsername().equals(securityProperties.getAnonymousUser()) + && !AuthContextUtils.getUsername().equals(user.getUsername())) { + + Set authRealms = AuthContextUtils.getAuthorizations(). + getOrDefault(IdRepoEntitlement.USER_READ, Set.of()); + + securityChecks(authRealms, user.getKey(), user.getRealm().getFullPath(), findAllGroupKeys(user)); + } + } + + @Override + public Map countByRealm() { + Collection> result = neo4jClient.query( + "MATCH (n:" + Neo4jUser.NODE + ")-[]-(r:" + Neo4jRealm.NODE + ") " + + "RETURN r.fullPath AS realm, COUNT(n) AS counted").fetch().all(); + + return result.stream().collect(Collectors.toMap(r -> r.get("realm").toString(), r -> (Long) r.get("counted"))); + } + + @Override + public Map countByStatus() { + Collection> result = neo4jClient.query( + "MATCH (n:" + Neo4jUser.NODE + ") " + + "RETURN n.status AS status, COUNT(n) AS counted").fetch().all(); + + return result.stream().collect(Collectors.toMap(r -> r.get("status").toString(), r -> (Long) r.get("counted"))); + } + + @Override + public UMembership findMembership(final String key) { + return neo4jTemplate.findById(key, Neo4jUMembership.class).orElse(null); + } + + protected Pair, Set>> doSave(final User user) { + // delete any membership, relationship or linked account that was removed from user + neo4jTemplate.findById(user.getKey(), Neo4jUser.class).ifPresent(before -> { + Set beforeMembs = before.getMemberships().stream().map(UMembership::getKey). + collect(Collectors.toSet()); + beforeMembs.removeAll(user.getMemberships().stream().map(UMembership::getKey).toList()); + beforeMembs.forEach(m -> neo4jTemplate.deleteById(m, Neo4jUMembership.class)); + + Set beforeRels = before.getRelationships().stream().map(URelationship::getKey). + collect(Collectors.toSet()); + beforeRels.removeAll(user.getRelationships().stream().map(URelationship::getKey).toList()); + beforeRels.forEach(r -> neo4jTemplate.deleteById(r, Neo4jURelationship.class)); + + Set beforeLAs = before.getLinkedAccounts().stream().map(LinkedAccount::getKey). + collect(Collectors.toSet()); + beforeLAs.removeAll(user.getLinkedAccounts().stream().map(LinkedAccount::getKey).toList()); + beforeLAs.forEach(la -> neo4jTemplate.deleteById(la, Neo4jLinkedAccount.class)); + }); + + User merged = neo4jTemplate.save(nodeValidator.validate(user)); + roleDAO.refreshDynMemberships(merged); + Pair, Set> dynGroupMembs = groupDAO.refreshDynMemberships(merged); + dynRealmDAO.refreshDynMemberships(merged); + + return Pair.of(merged, dynGroupMembs); + } + + @Override + @SuppressWarnings("unchecked") + public S save(final S user) { + return (S) doSave(user).getLeft(); + } + + @Override + public Pair, Set> saveAndGetDynGroupMembs(final User user) { + return doSave(user).getRight(); + } + + @Override + public void delete(final User user) { + roleDAO.removeDynMemberships(user.getKey()); + groupDAO.removeDynMemberships(user); + dynRealmDAO.removeDynMemberships(user.getKey()); + + delegationDAO.findByDelegating(user).forEach(delegationDAO::delete); + delegationDAO.findByDelegated(user).forEach(delegationDAO::delete); + + fiqlQueryDAO.findByOwner(user, null).forEach(fiqlQueryDAO::delete); + + accessTokenDAO.findByOwner(user.getUsername()).ifPresent(accessTokenDAO::delete); + + cascadeDelete( + Neo4jURelationship.NODE, + Neo4jUser.NODE, + user.getKey(), + Neo4jURelationship.class); + + cascadeDelete( + Neo4jUMembership.NODE, + Neo4jUser.NODE, + user.getKey(), + Neo4jUMembership.class); + + cascadeDelete( + Neo4jLinkedAccount.NODE, + Neo4jUser.NODE, + user.getKey(), + Neo4jLinkedAccount.class); + + neo4jTemplate.deleteById(user.getKey(), Neo4jUser.class); + } + + @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Override + public Collection findAllRoles(final User user) { + Set result = new HashSet<>(); + result.addAll(user.getRoles()); + result.addAll(findDynRoles(user.getKey())); + + return result; + } + + @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Override + public List findDynRoles(final String key) { + return toList(neo4jClient.query( + "MATCH (n:" + Neo4jUser.NODE + " {id: $id})-" + + "[:" + RoleRepoExt.DYN_ROLE_MEMBERSHIP_REL + "]-" + + "(p:" + Neo4jRole.NODE + ") " + + "RETURN p.id").bindAll(Map.of("id", key)).fetch().all(), + "p.id", + Neo4jRole.class); + } + + @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Override + public List findDynGroups(final String key) { + return toList(neo4jClient.query( + "MATCH (n:" + Neo4jUser.NODE + " {id: $id})-" + + "[:" + GroupRepoExt.DYN_GROUP_USER_MEMBERSHIP_REL + "]-" + + "(p:" + Neo4jGroup.NODE + ") " + + "RETURN p.id").bindAll(Map.of("id", key)).fetch().all(), + "p.id", + Neo4jGroup.class); + } + + @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Override + public Collection findAllGroups(final User user) { + Set result = new HashSet<>(); + result.addAll(user.getMemberships().stream(). + map(UMembership::getRightEnd).collect(Collectors.toSet())); + result.addAll(findDynGroups(user.getKey())); + + return result; + } + + @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Override + public Collection findAllGroupKeys(final User user) { + return findAllGroups(user).stream().map(Group::getKey).toList(); + } + + @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Override + public Collection findAllGroupNames(final User user) { + return findAllGroups(user).stream().map(Group::getName).toList(); + } + + @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) + @Override + public Collection findAllResources(final User user) { + Set result = new HashSet<>(); + result.addAll(user.getResources()); + findAllGroups(user).forEach(group -> result.addAll(group.getResources())); + + return result; + } + + @Transactional(readOnly = true) + @Override + public Collection findAllResourceKeys(final String key) { + return findAllResources(authFind(key)).stream().map(ExternalResource::getKey).toList(); + } + + @Transactional(readOnly = true) + @Override + public boolean linkedAccountExists(final String userKey, final String connObjectKeyValue) { + return neo4jTemplate.count( + "MATCH (n:" + Neo4jUser.NODE + " {id: $id})-[]-(p:" + Neo4jLinkedAccount.NODE + ") " + + "WHERE p.connObjectKeyValue = $connObjectKeyValue " + + "RETURN COUNT(p)", + Map.of("id", userKey, "connObjectKeyValue", connObjectKeyValue)) > 0; + } + + @Override + public Optional findLinkedAccount( + final ExternalResource resource, + final String connObjectKeyValue) { + + return neo4jClient.query( + "MATCH (n:" + Neo4jLinkedAccount.NODE + ")-[]-" + + "(e:" + Neo4jExternalResource.NODE + " {id: $resource}) " + + "WHERE n.connObjectKeyValue = $connObjectKeyValue " + + "RETURN n.id"). + bindAll(Map.of("resource", resource.getKey(), "connObjectKeyValue", connObjectKeyValue)).fetch().one(). + flatMap(toOptional("n.id", Neo4jLinkedAccount.class)); + } + + @Override + public List findLinkedAccounts(final String userKey) { + return findByRelationship( + Neo4jLinkedAccount.NODE, + Neo4jUser.NODE, + userKey, + Neo4jLinkedAccount.class); + } + + @Override + public List findLinkedAccountsByPrivilege(final Privilege privilege) { + return findByRelationship( + Neo4jLinkedAccount.NODE, + Neo4jPrivilege.NODE, + privilege.getKey(), + Neo4jLinkedAccount.class); + } + + @Override + public List findLinkedAccountsByResource(final ExternalResource resource) { + return findByRelationship( + Neo4jLinkedAccount.NODE, + Neo4jExternalResource.NODE, + resource.getKey(), + Neo4jLinkedAccount.class); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/VirSchemaRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/VirSchemaRepo.java new file mode 100644 index 0000000000..bb68492819 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/VirSchemaRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jVirSchema; +import org.springframework.data.repository.ListCrudRepository; + +public interface VirSchemaRepo + extends ListCrudRepository, VirSchemaRepoExt, VirSchemaDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/VirSchemaRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/VirSchemaRepoExt.java new file mode 100644 index 0000000000..323841b38a --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/VirSchemaRepoExt.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import java.util.Collection; +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.VirSchema; + +public interface VirSchemaRepoExt { + + List findByAnyTypeClasses(Collection anyTypeClasses); + + List findByResource(ExternalResource resource); + + List findByResourceAndAnyType(String resource, String anyType); + + VirSchema save(VirSchema schema); + + void deleteById(String key); + + void delete(VirSchema schema); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/VirSchemaRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/VirSchemaRepoExtImpl.java new file mode 100644 index 0000000000..84f96a9850 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/VirSchemaRepoExtImpl.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.core.persistence.neo4j.dao.repo; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.VirSchema; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyType; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jSchema; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jVirSchema; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; + +public class VirSchemaRepoExtImpl extends AbstractSchemaRepoExt implements VirSchemaRepoExt { + + protected final ExternalResourceDAO resourceDAO; + + public VirSchemaRepoExtImpl( + final ExternalResourceDAO resourceDAO, + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient, + final NodeValidator nodeValidator) { + + super(neo4jTemplate, neo4jClient, nodeValidator); + this.resourceDAO = resourceDAO; + } + + @Override + public List findByAnyTypeClasses(final Collection anyTypeClasses) { + return findByAnyTypeClasses(anyTypeClasses, Neo4jVirSchema.class, VirSchema.class); + } + + @Override + public List findByResource(final ExternalResource resource) { + return findByRelationship( + Neo4jVirSchema.NODE, + Neo4jExternalResource.NODE, + resource.getKey(), + Neo4jVirSchema.VIRSCHEMA_RESOURCE_REL, + Neo4jVirSchema.class); + } + + @Override + public List findByResourceAndAnyType(final String resource, final String anyType) { + return toList(neo4jClient.query( + "MATCH (a:" + Neo4jAnyType.NODE + " {id: $anyTypeId})-" + + "[:" + Neo4jVirSchema.VIRSCHEMA_ANYTYPE_REL + "]-" + + "(n:" + Neo4jVirSchema.NODE + ")-" + + "[:" + Neo4jVirSchema.VIRSCHEMA_RESOURCE_REL + "]-" + + "(r:" + Neo4jExternalResource.NODE + " {id: $resourceId}) " + + "RETURN n.id").bindAll(Map.of("resourceId", resource, "anyTypeId", anyType)).fetch().all(), + "n.id", + Neo4jVirSchema.class); + } + + @Override + public VirSchema save(final VirSchema schema) { + ((Neo4jSchema) schema).map2json(); + VirSchema saved = neo4jTemplate.save(nodeValidator.validate(schema)); + ((Neo4jSchema) saved).postSave(); + return saved; + } + + @Override + public void deleteById(final String key) { + neo4jTemplate.findById(key, Neo4jVirSchema.class).ifPresent(this::delete); + } + + @Override + public void delete(final VirSchema schema) { + resourceDAO.deleteMapping(schema.getKey()); + + Optional.ofNullable(schema.getAnyTypeClass()). + ifPresent(anyTypeClass -> anyTypeClass.getVirSchemas().remove(schema)); + + neo4jTemplate.deleteById(schema.getKey(), Neo4jVirSchema.class); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/WAConfigRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/WAConfigRepo.java new file mode 100644 index 0000000000..fc95f7bdf2 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/WAConfigRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.WAConfigDAO; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jWAConfigEntry; +import org.springframework.data.repository.ListCrudRepository; + +public interface WAConfigRepo + extends ListCrudRepository, WAConfigDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractAny.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractAny.java new file mode 100644 index 0000000000..f009edce3b --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractAny.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.core.persistence.neo4j.entity; + +import jakarta.validation.constraints.NotNull; +import java.time.OffsetDateTime; +import java.util.Map; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.common.validation.AnyCheck; +import org.springframework.data.neo4j.core.schema.PostLoad; +import org.springframework.data.neo4j.core.schema.Relationship; + +@AnyCheck +public abstract class AbstractAny

> extends AbstractGeneratedKeyNode implements Any

{ + + private static final long serialVersionUID = -2666540708092702810L; + + /** + * Username of the user that has created the related instance. + */ + private String creator; + + /** + * Creation date. + */ + private OffsetDateTime creationDate; + + /** + * Context information about create. + */ + private String creationContext; + + /** + * Username of the user that has performed the last modification to the related instance. + */ + private String lastModifier; + + private OffsetDateTime lastChangeDate; + + /** + * Context information about last update. + */ + private String lastChangeContext; + + @NotNull + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jRealm realm; + + private String status; + + @Override + public String getCreator() { + return creator; + } + + @Override + public void setCreator(final String creator) { + this.creator = creator; + } + + @Override + public OffsetDateTime getCreationDate() { + return creationDate; + } + + @Override + public void setCreationDate(final OffsetDateTime creationDate) { + this.creationDate = creationDate; + } + + @Override + public String getCreationContext() { + return creationContext; + } + + @Override + public void setCreationContext(final String creationContext) { + this.creationContext = creationContext; + } + + @Override + public String getLastModifier() { + return lastModifier; + } + + @Override + public void setLastModifier(final String lastModifier) { + this.lastModifier = lastModifier; + } + + @Override + public OffsetDateTime getLastChangeDate() { + if (lastChangeDate != null) { + return lastChangeDate; + } else if (creationDate != null) { + return creationDate; + } + + return null; + } + + @Override + public void setLastChangeDate(final OffsetDateTime lastChangeDate) { + this.lastChangeDate = lastChangeDate; + } + + @Override + public String getLastChangeContext() { + return lastChangeContext; + } + + @Override + public void setLastChangeContext(final String lastChangeContext) { + this.lastChangeContext = lastChangeContext; + } + + @Override + public Realm getRealm() { + return realm; + } + + @Override + public void setRealm(final Realm realm) { + checkType(realm, Neo4jRealm.class); + this.realm = (Neo4jRealm) realm; + } + + @Override + public String getStatus() { + return status; + } + + @Override + public void setStatus(final String status) { + this.status = status; + } + + protected abstract Map>> plainAttrs(); + + @PostLoad + @SuppressWarnings("unchecked") + public void completePlainAttrs() { + for (var itor = plainAttrs().entrySet().iterator(); itor.hasNext();) { + var entry = itor.next(); + String schema = entry.getKey(); + Neo4jPlainAttr> attr = entry.getValue(); + + attr.setSchemaKey(schema); + if (attr.getSchema() == null) { + itor.remove(); + } else { + ((Neo4jPlainAttr) attr).setOwner(this); + attr.getValues().forEach(value -> value.setAttr(attr)); + Optional.ofNullable(attr.getUniqueValue()).ifPresent(value -> value.setAttr(attr)); + } + } + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractAnyTemplate.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractAnyTemplate.java new file mode 100644 index 0000000000..e14665a95d --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractAnyTemplate.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.persistence.neo4j.entity; + +import java.util.Optional; +import org.apache.syncope.common.lib.to.AnyTO; +import org.apache.syncope.core.persistence.api.entity.AnyTemplate; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.spring.ApplicationContextProvider; +import org.springframework.data.neo4j.core.schema.Relationship; + +public abstract class AbstractAnyTemplate extends AbstractGeneratedKeyNode implements AnyTemplate { + + private static final long serialVersionUID = -5280310945358790780L; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jAnyType anyType; + + private String template; + + @Override + public AnyType getAnyType() { + return anyType; + } + + @Override + public void setAnyType(final AnyType anyType) { + checkType(anyType, Neo4jAnyType.class); + this.anyType = (Neo4jAnyType) anyType; + } + + @Override + public AnyTO get() { + return template == null + ? anyType == null + ? null + : ApplicationContextProvider.getApplicationContext().getBean(AnyUtilsFactory.class). + getInstance(anyType.getKind()).newAnyTO() + : anyType == null + ? null + : POJOHelper.deserialize(template, anyType.getKind().getTOClass()); + } + + @Override + public void set(final AnyTO template) { + this.template = Optional.ofNullable(template).map(POJOHelper::serialize).orElse(null); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractDynMembership.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractDynMembership.java new file mode 100644 index 0000000000..d85998b2fa --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractDynMembership.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.DynMembership; + +public abstract class AbstractDynMembership> + extends AbstractGeneratedKeyNode implements DynMembership { + + private static final long serialVersionUID = 921821654690948787L; + + @NotNull + private String fiql; + + @Override + public String getFIQLCond() { + return fiql; + } + + @Override + public void setFIQLCond(final String fiql) { + this.fiql = fiql; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractExec.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractExec.java new file mode 100644 index 0000000000..f1832b1eb6 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractExec.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity; + +import jakarta.validation.constraints.NotNull; +import java.time.OffsetDateTime; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.entity.Exec; +import org.springframework.data.neo4j.core.schema.Property; + +public abstract class AbstractExec extends AbstractGeneratedKeyNode implements Exec { + + private static final long serialVersionUID = -812344822970166317L; + + @NotNull + protected String status; + + @NotNull + protected String executor; + + /** + * Any information to be accompanied to this execution's result. + */ + protected String message; + + /** + * Start instant of this execution. + */ + @NotNull + @Property("startDate") + protected OffsetDateTime start; + + /** + * End instant of this execution. + */ + @Property("endDate") + protected OffsetDateTime end; + + @Override + public String getStatus() { + return status; + } + + @Override + public void setStatus(final String status) { + this.status = status; + } + + @Override + public String getExecutor() { + return this.executor; + } + + @Override + public void setExecutor(final String executor) { + this.executor = executor; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public void setMessage(final String message) { + this.message = Optional.ofNullable(message).map(s -> s.replace('\0', '\n')).orElse(null); + } + + @Override + public OffsetDateTime getStart() { + return start; + } + + @Override + public void setStart(final OffsetDateTime start) { + this.start = start; + } + + @Override + public OffsetDateTime getEnd() { + return end; + } + + @Override + public void setEnd(final OffsetDateTime end) { + this.end = end; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractGeneratedKeyNode.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractGeneratedKeyNode.java new file mode 100644 index 0000000000..60928bb0c5 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractGeneratedKeyNode.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.persistence.neo4j.entity; + +import org.springframework.data.neo4j.core.schema.Id; + +public abstract class AbstractGeneratedKeyNode extends AbstractNode { + + private static final long serialVersionUID = 4705587655441599524L; + + @Id + private String id; + + @Override + public String getKey() { + return id; + } + + public void setKey(final String key) { + this.id = key; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractGroupableRelatable.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractGroupableRelatable.java new file mode 100644 index 0000000000..29f1100772 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractGroupableRelatable.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity; + +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.GroupablePlainAttr; +import org.apache.syncope.core.persistence.api.entity.GroupableRelatable; +import org.apache.syncope.core.persistence.api.entity.Membership; +import org.apache.syncope.core.persistence.api.entity.Relationship; +import org.apache.syncope.core.persistence.api.entity.RelationshipType; + +public abstract class AbstractGroupableRelatable< + L extends Any

, + M extends Membership, + P extends GroupablePlainAttr, + R extends Any, + REL extends Relationship> + extends AbstractAny

implements GroupableRelatable { + + private static final long serialVersionUID = -2269285197388729673L; + + protected abstract Map internalGetPlainAttrs(); + + protected abstract List> internalGetMemberships(); + + @Override + public boolean remove(final P attr) { + return internalGetPlainAttrs().remove(attr.getSchema().getKey()) != null; + } + + @Override + public Optional getPlainAttr(final String plainSchema) { + return Optional.ofNullable(internalGetPlainAttrs().get(plainSchema)); + } + + @Override + public Optional getPlainAttr(final String plainSchema, final Membership membership) { + return internalGetMemberships().stream(). + filter(m -> m.getRightEnd().getKey().equals(membership.getKey())).findFirst(). + flatMap(m -> m.getPlainAttr(plainSchema)); + } + + @Override + public List getPlainAttrs() { + return internalGetPlainAttrs().entrySet().stream(). + sorted(Comparator.comparing(Map.Entry::getKey)). + map(Map.Entry::getValue).toList(); + } + + @Override + public Collection getPlainAttrs(final String plainSchema) { + return Stream.concat( + getPlainAttr(plainSchema).map(Stream::of).orElse(Stream.empty()), + internalGetMemberships().stream().map(m -> m.getPlainAttr(plainSchema)). + filter(Optional::isPresent).map(Optional::get)). + toList(); + } + + @Override + public Collection getPlainAttrs(final Membership membership) { + return internalGetMemberships().stream(). + filter(m -> m.getRightEnd().getKey().equals(membership.getKey())). + flatMap(m -> m.getPlainAttrs().stream()).toList(); + } + + @SuppressWarnings("unchecked") + @Override + public List getMemberships() { + return internalGetMemberships().stream().map(m -> (M) m).toList(); + } + + @Override + public Optional getMembership(final String groupKey) { + return getMemberships().stream(). + filter(membership -> groupKey != null && groupKey.equals(membership.getRightEnd().getKey())). + findFirst(); + } + + @Override + public Collection getRelationships(final RelationshipType relationshipType) { + return getRelationships().stream(). + filter(relationship -> relationshipType != null && relationshipType.equals(relationship.getType())). + toList(); + } + + @Override + public Collection getRelationships(final String otherEndKey) { + return getRelationships().stream(). + filter(relationship -> otherEndKey != null && otherEndKey.equals(relationship.getRightEnd().getKey())). + toList(); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractMembership.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractMembership.java new file mode 100644 index 0000000000..69f89f9947 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractMembership.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.persistence.neo4j.entity; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.Membership; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; + +public abstract class AbstractMembership, P extends PlainAttr> + extends AbstractGeneratedKeyNode + implements Membership { + + private static final long serialVersionUID = -6360036936818368868L; + + public abstract List getPlainAttrs(); + + public abstract Optional getPlainAttr(String plainSchema); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractNode.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractNode.java new file mode 100644 index 0000000000..9ee54e1e89 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractNode.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.core.persistence.neo4j.entity; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import java.util.Objects; +import org.apache.syncope.core.persistence.api.entity.Entity; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "key") +public abstract class AbstractNode implements Entity { + + private static final long serialVersionUID = -9017214159540857901L; + + protected static final Logger LOG = LoggerFactory.getLogger(AbstractNode.class); + + protected void checkType(final Object object, final Class clazz) { + if (object != null && !clazz.isInstance(object)) { + throw new ClassCastException("Expected " + clazz.getName() + ", got " + object.getClass().getName()); + } + } + + protected void checkImplementationType(final Implementation object, final String expected) { + if (object != null && !object.getType().equals(expected)) { + throw new ClassCastException("Expected " + expected + ", got " + object.getType()); + } + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof AbstractNode)) { + return false; + } + AbstractNode entity = (AbstractNode) obj; + return Objects.equals(getKey(), entity.getKey()); + } + + @Override + public int hashCode() { + return Objects.hash(getKey()); + } + + @Override + public String toString() { + return new StringBuilder().append(getClass().getSimpleName()). + append('[').append(getKey()).append(']').toString(); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractPlainAttr.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractPlainAttr.java new file mode 100644 index 0000000000..68d804c661 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractPlainAttr.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import jakarta.validation.constraints.NotNull; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.entity.Any; +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.PlainAttrUniqueValue; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.common.validation.PlainAttrCheck; +import org.apache.syncope.core.spring.ApplicationContextProvider; + +@JsonIgnoreProperties("valuesAsStrings") +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@PlainAttrCheck +public abstract class AbstractPlainAttr> extends AbstractProvidedKeyNode implements PlainAttr { + + private static final long serialVersionUID = -9115431608821806124L; + + @JsonIgnore + @NotNull + protected String schemaKey; + + public String getSchemaKey() { + return schemaKey; + } + + public void setSchemaKey(final String schemaKey) { + this.schemaKey = schemaKey; + } + + @JsonIgnore + @Override + public Neo4jPlainSchema getSchema() { + return Optional.ofNullable(schemaKey). + flatMap(s -> ApplicationContextProvider.getBeanFactory().getBean(PlainSchemaDAO.class).findById(s)). + map(Neo4jPlainSchema.class::cast). + orElse(null); + } + + @JsonIgnore + @Override + public void setSchema(final PlainSchema schema) { + checkType(schema, Neo4jPlainSchema.class); + if (schema != null) { + this.schemaKey = schema.getKey(); + } + } + + protected abstract boolean addForMultiValue(PlainAttrValue attrValue); + + private void checkNonNullSchema() { + if (getSchema() == null) { + throw new IllegalStateException("First set owner then schema and finally add values"); + } + } + + @Override + public void add(final PlainAttrValidationManager validator, final String value, final PlainAttrValue attrValue) { + checkNonNullSchema(); + + attrValue.setAttr(this); + validator.validate(getSchema(), value, attrValue); + + if (getSchema().isUniqueConstraint()) { + setUniqueValue((PlainAttrUniqueValue) attrValue); + } else { + if (!getSchema().isMultivalue()) { + getValues().clear(); + } + addForMultiValue(attrValue); + } + } + + @Override + public void add(final PlainAttrValidationManager validator, final String value, final AnyUtils anyUtils) { + checkNonNullSchema(); + + PlainAttrValue attrValue; + if (getSchema().isUniqueConstraint()) { + attrValue = anyUtils.newPlainAttrUniqueValue(); + ((PlainAttrUniqueValue) attrValue).setSchema(getSchema()); + } else { + attrValue = anyUtils.newPlainAttrValue(); + } + + add(validator, value, attrValue); + } + + @Override + public List getValuesAsStrings() { + List result; + if (getUniqueValue() == null) { + result = getValues().stream().map(PlainAttrValue::getValueAsString).toList(); + } else { + result = List.of(getUniqueValue().getValueAsString()); + } + + return Collections.unmodifiableList(result); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractPlainAttrValue.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractPlainAttrValue.java new file mode 100644 index 0000000000..8ef6861767 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractPlainAttrValue.java @@ -0,0 +1,353 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import java.time.OffsetDateTime; +import java.util.Base64; +import java.util.Optional; +import java.util.regex.Pattern; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.core.persistence.api.attrvalue.ParsingValidationException; +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.utils.FormatUtils; +import org.apache.syncope.core.persistence.common.validation.PlainAttrValueCheck; +import org.apache.syncope.core.spring.ApplicationContextProvider; +import org.apache.syncope.core.spring.security.Encryptor; + +@JsonIgnoreProperties({ "valueAsString", "value" }) +@JsonInclude(JsonInclude.Include.NON_NULL) +@PlainAttrValueCheck +public abstract class AbstractPlainAttrValue extends AbstractProvidedKeyNode implements PlainAttrValue { + + private static final long serialVersionUID = -9141923816611244785L; + + private static final Pattern SPRING_ENV_PROPERTY = Pattern.compile("^\\$\\{.*\\}$"); + + private String stringValue; + + private OffsetDateTime dateValue; + + private Boolean booleanValue; + + private Long longValue; + + private Double doubleValue; + + private byte[] binaryValue; + + @Override + public Boolean getBooleanValue() { + return booleanValue; + } + + @Override + public void setBooleanValue(final Boolean booleanValue) { + this.booleanValue = booleanValue; + } + + @Override + public OffsetDateTime getDateValue() { + return dateValue; + } + + @Override + public void setDateValue(final OffsetDateTime dateValue) { + this.dateValue = dateValue; + } + + @Override + public Double getDoubleValue() { + return doubleValue; + } + + @Override + public void setDoubleValue(final Double doubleValue) { + this.doubleValue = doubleValue; + } + + @Override + public Long getLongValue() { + return longValue; + } + + @Override + public void setLongValue(final Long longValue) { + this.longValue = longValue; + } + + @Override + public String getStringValue() { + // workaround for Oracle DB considering empty string values as NULL (SYNCOPE-664) + return dateValue == null + && booleanValue == null + && longValue == null + && doubleValue == null + && binaryValue == null + && stringValue == null + ? StringUtils.EMPTY + : stringValue; + } + + @Override + public void setStringValue(final String stringValue) { + this.stringValue = stringValue; + } + + @Override + public byte[] getBinaryValue() { + return binaryValue; + } + + @Override + public void setBinaryValue(final byte[] binaryValue) { + this.binaryValue = ArrayUtils.clone(binaryValue); + } + + protected String getSecretKey(final PlainSchema schema) { + return SPRING_ENV_PROPERTY.matcher(schema.getSecretKey()).matches() + ? ApplicationContextProvider.getApplicationContext().getEnvironment(). + getProperty(StringUtils.substringBetween(schema.getSecretKey(), "${", "}")) + : schema.getSecretKey(); + } + + @Override + public void parseValue(final PlainSchema schema, final String value) { + Exception exception = null; + + switch (schema.getType()) { + + case Boolean: + this.setBooleanValue(Boolean.valueOf(value)); + break; + + case Long: + try { + this.setLongValue(schema.getConversionPattern() == null + ? Long.valueOf(value) + : FormatUtils.parseNumber(value, schema.getConversionPattern()).longValue()); + } catch (Exception pe) { + exception = pe; + } + break; + + case Double: + try { + this.setDoubleValue(schema.getConversionPattern() == null + ? Double.valueOf(value) + : FormatUtils.parseNumber(value, schema.getConversionPattern()).doubleValue()); + } catch (Exception pe) { + exception = pe; + } + break; + + case Date: + try { + this.setDateValue(schema.getConversionPattern() == null + ? FormatUtils.parseDate(value) + : FormatUtils.parseDate(value, schema.getConversionPattern())); + } catch (Exception pe) { + exception = pe; + } + break; + + case Encrypted: + try { + this.setStringValue(Encryptor.getInstance(getSecretKey(schema)). + encode(value, schema.getCipherAlgorithm())); + } catch (Exception pe) { + exception = pe; + } + break; + + case Binary: + this.setBinaryValue(Base64.getDecoder().decode(value)); + break; + + case String: + case Enum: + default: + this.setStringValue(value); + } + + if (exception != null) { + throw new ParsingValidationException( + "While trying to parse '" + value + "' as " + schema.getKey(), exception); + } + } + + @SuppressWarnings("unchecked") + @Override + public T getValue() { + return (T) (booleanValue != null + ? getBooleanValue() + : dateValue != null + ? getDateValue() + : doubleValue != null + ? getDoubleValue() + : longValue != null + ? getLongValue() + : binaryValue != null + ? getBinaryValue() + : getStringValue()); + } + + private Object getValue(final AttrSchemaType type) { + Object value; + switch (type) { + + case Boolean: + value = getBooleanValue(); + break; + + case Long: + value = getLongValue(); + break; + + case Double: + value = getDoubleValue(); + break; + + case Date: + value = getDateValue(); + break; + + case Binary: + value = getBinaryValue(); + break; + + case String: + case Enum: + case Encrypted: + value = getStringValue(); + break; + + default: + value = null; + } + + return value; + } + + private String getValueAsString(final AttrSchemaType type, final PlainSchema schema) { + if (getValue(type) == null) { + LOG.warn("Could not find expected value for type {} in {}, reverting to getValue().toString()", type, this); + + Object value = getValue(); + return Optional.ofNullable(value).map(Object::toString).orElse(null); + } + + String result; + switch (type) { + + case Boolean: + result = getBooleanValue().toString(); + break; + + case Long: + result = schema == null || schema.getConversionPattern() == null + ? getLongValue().toString() + : FormatUtils.format(getLongValue(), schema.getConversionPattern()); + break; + + case Double: + result = schema == null || schema.getConversionPattern() == null + ? getDoubleValue().toString() + : FormatUtils.format(getDoubleValue(), schema.getConversionPattern()); + break; + + case Date: + result = schema == null || schema.getConversionPattern() == null + ? FormatUtils.format(getDateValue()) + : FormatUtils.format(getDateValue(), schema.getConversionPattern()); + break; + + case Binary: + result = Base64.getEncoder().encodeToString(getBinaryValue()); + break; + + case Encrypted: + if (schema == null + || !SyncopeConstants.ENCRYPTED_DECODE_CONVERSION_PATTERN.equals(schema.getConversionPattern()) + || !schema.getCipherAlgorithm().isInvertible()) { + + result = getStringValue(); + } else { + try { + result = Encryptor.getInstance(getSecretKey(schema)). + decode(getStringValue(), schema.getCipherAlgorithm()); + } catch (Exception e) { + LOG.error("Could not decode encrypted value {} for schema {}", getStringValue(), schema, e); + result = getStringValue(); + } + } + break; + + case String: + case Enum: + default: + result = getStringValue(); + } + + return result; + } + + @Override + public String getValueAsString() { + PlainSchema schema = getAttr() == null || getAttr().getSchema() == null + ? null + : getAttr().getSchema(); + AttrSchemaType type = schema == null || schema.getType() == null + ? AttrSchemaType.String + : getAttr().getSchema().getType(); + + return getValueAsString(type, schema); + } + + @Override + public String getValueAsString(final AttrSchemaType type) { + return getValueAsString( + type, + getAttr() == null || getAttr().getSchema() == null ? null : getAttr().getSchema()); + } + + @Override + public String getValueAsString(final PlainSchema schema) { + return getValueAsString(schema.getType(), schema); + } + + @Override + public String toString() { + return new ToStringBuilder(this). + append(getKey()). + append(stringValue). + append(dateValue). + append(booleanValue). + append(longValue). + append(doubleValue). + append(binaryValue). + build(); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractProvidedKeyNode.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractProvidedKeyNode.java new file mode 100644 index 0000000000..9902a850cb --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AbstractProvidedKeyNode.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity; + +import org.apache.syncope.core.persistence.api.entity.ProvidedKeyEntity; +import org.springframework.data.neo4j.core.schema.Id; + +public abstract class AbstractProvidedKeyNode extends AbstractNode implements ProvidedKeyEntity { + + private static final long serialVersionUID = 821537874069666593L; + + @Id + private String id; + + @Override + public String getKey() { + return id; + } + + @Override + public void setKey(final String key) { + this.id = key; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AttributableCheck.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AttributableCheck.java new file mode 100644 index 0000000000..59217b30cb --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AttributableCheck.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = AttributableValidator.class) +@Documented +public @interface AttributableCheck { + + String message() default "{org.apache.syncope.core.persistence.validation.any}"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AttributableValidator.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AttributableValidator.java new file mode 100644 index 0000000000..2f4f4d63b7 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/AttributableValidator.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.persistence.neo4j.entity; + +import jakarta.validation.ConstraintValidatorContext; +import java.util.concurrent.atomic.AtomicReference; +import org.apache.syncope.core.persistence.common.validation.AbstractValidator; +import org.apache.syncope.core.persistence.common.validation.PlainAttrValidator; +import org.apache.syncope.core.persistence.common.validation.PlainAttrValueValidator; + +public class AttributableValidator extends AbstractValidator> { + + private static final PlainAttrValidator ATTR_VALIDATOR = new PlainAttrValidator(); + + private static final PlainAttrValueValidator ATTR_VALUE_VALIDATOR = new PlainAttrValueValidator(); + + @Override + public boolean isValid(final Neo4jAttributable entity, final ConstraintValidatorContext context) { + context.disableDefaultConstraintViolation(); + + AtomicReference isValid = new AtomicReference<>(Boolean.TRUE); + entity.getPlainAttrs().forEach(attr -> { + isValid.getAndSet(isValid.get() && ATTR_VALIDATOR.isValid(attr, context)); + attr.getValues().forEach( + value -> isValid.getAndSet(isValid.get() && ATTR_VALUE_VALIDATOR.isValid(value, context))); + }); + + return isValid.get(); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAccessToken.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAccessToken.java new file mode 100644 index 0000000000..6ac4a3d5c0 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAccessToken.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.persistence.neo4j.entity; + +import java.time.OffsetDateTime; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.syncope.core.persistence.api.entity.AccessToken; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jAccessToken.NODE) +public class Neo4jAccessToken extends AbstractProvidedKeyNode implements AccessToken { + + private static final long serialVersionUID = 6839211057212932936L; + + public static final String NODE = "AccessToken"; + + private String body; + + private OffsetDateTime expirationTime; + + private String owner; + + private byte[] authorities; + + @Override + public String getBody() { + return body; + } + + @Override + public void setBody(final String body) { + this.body = body; + } + + @Override + public OffsetDateTime getExpirationTime() { + return expirationTime; + } + + @Override + public void setExpirationTime(final OffsetDateTime expirationTime) { + this.expirationTime = expirationTime; + } + + @Override + public String getOwner() { + return owner; + } + + @Override + public void setOwner(final String owner) { + this.owner = owner; + } + + @Override + public byte[] getAuthorities() { + return authorities; + } + + @Override + public void setAuthorities(final byte[] authorities) { + this.authorities = ArrayUtils.clone(authorities); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAnyAbout.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAnyAbout.java new file mode 100644 index 0000000000..84c21e96b0 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAnyAbout.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.persistence.neo4j.entity; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.AnyAbout; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.Notification; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jAnyAbout.NODE) +public class Neo4jAnyAbout extends AbstractGeneratedKeyNode implements AnyAbout { + + private static final long serialVersionUID = 3517381731849788407L; + + public static final String NODE = "AnyAbout"; + + @NotNull + @Relationship(type = Neo4jNotification.NOTIFICATION_ABOUT_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jNotification notification; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jAnyType anyType; + + private String filter; + + @Override + public Notification getNotification() { + return notification; + } + + @Override + public void setNotification(final Notification notification) { + checkType(notification, Neo4jNotification.class); + this.notification = (Neo4jNotification) notification; + } + + @Override + public AnyType getAnyType() { + return anyType; + } + + @Override + public void setAnyType(final AnyType anyType) { + checkType(anyType, Neo4jAnyType.class); + this.anyType = (Neo4jAnyType) anyType; + } + + @Override + public String get() { + return filter; + } + + @Override + public void set(final String filter) { + this.filter = filter; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAnyTemplateRealm.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAnyTemplateRealm.java new file mode 100644 index 0000000000..6d9edd2984 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAnyTemplateRealm.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.core.persistence.neo4j.entity; + +import org.apache.syncope.core.persistence.api.entity.AnyTemplateRealm; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jAnyTemplateRealm.NODE) +public class Neo4jAnyTemplateRealm extends AbstractAnyTemplate implements AnyTemplateRealm { + + private static final long serialVersionUID = 1863029633568957907L; + + public static final String NODE = "AnyTemplateRealm"; + + public static final String REALM_ANY_TEMPLATE_REL = "REALM_ANY_TEMPLATE"; + + @Relationship(type = REALM_ANY_TEMPLATE_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jRealm realm; + + @Override + public Realm getRealm() { + return realm; + } + + @Override + public void setRealm(final Realm realm) { + checkType(realm, Neo4jRealm.class); + this.realm = (Neo4jRealm) realm; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAnyType.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAnyType.java new file mode 100644 index 0000000000..2aeb4e1e37 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAnyType.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.persistence.neo4j.entity; + +import jakarta.validation.constraints.NotNull; +import java.util.HashSet; +import java.util.Set; +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.AnyTypeClass; +import org.apache.syncope.core.persistence.common.validation.AnyTypeCheck; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jAnyType.NODE) +@AnyTypeCheck +public class Neo4jAnyType extends AbstractProvidedKeyNode implements AnyType { + + private static final long serialVersionUID = 2225660456911885733L; + + public static final String NODE = "AnyType"; + + @NotNull + private AnyTypeKind kind; + + @Relationship(type = "CLASS_OF", direction = Relationship.Direction.OUTGOING) + private Set classes = new HashSet<>(); + + @Override + public AnyTypeKind getKind() { + return kind; + } + + @Override + public void setKind(final AnyTypeKind kind) { + this.kind = kind; + } + + @Override + public boolean add(final AnyTypeClass anyTypeClass) { + checkType(anyTypeClass, Neo4jAnyTypeClass.class); + return classes.add((Neo4jAnyTypeClass) anyTypeClass); + } + + @Override + public Set getClasses() { + return classes; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAnyTypeClass.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAnyTypeClass.java new file mode 100644 index 0000000000..49b5c3656c --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAnyTypeClass.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.persistence.neo4j.entity; + +import java.util.ArrayList; +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.DerSchema; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.VirSchema; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jAnyTypeClass.NODE) +public class Neo4jAnyTypeClass extends AbstractProvidedKeyNode implements AnyTypeClass { + + private static final long serialVersionUID = 5243617517976085041L; + + public static final String NODE = "AnyTypeClass"; + + public static final String ANY_TYPE_CLASS_PLAIN_REL = "ANY_TYPE_CLASS_PLAIN"; + + public static final String ANY_TYPE_CLASS_DER_REL = "ANY_TYPE_CLASS_DER"; + + public static final String ANY_TYPE_CLASS_VIR_REL = "ANY_TYPE_CLASS_VIR"; + + @Relationship(type = ANY_TYPE_CLASS_PLAIN_REL, direction = Relationship.Direction.INCOMING) + private List plainSchemas = new ArrayList<>(); + + @Relationship(type = ANY_TYPE_CLASS_DER_REL, direction = Relationship.Direction.INCOMING) + private List derSchemas = new ArrayList<>(); + + @Relationship(type = ANY_TYPE_CLASS_VIR_REL, direction = Relationship.Direction.INCOMING) + private List virSchemas = new ArrayList<>(); + + @Override + public boolean add(final PlainSchema schema) { + checkType(schema, Neo4jPlainSchema.class); + return this.plainSchemas.add((Neo4jPlainSchema) schema); + } + + @Override + public List getPlainSchemas() { + return plainSchemas; + } + + @Override + public boolean add(final DerSchema schema) { + checkType(schema, Neo4jDerSchema.class); + return this.derSchemas.add((Neo4jDerSchema) schema); + } + + @Override + public List getDerSchemas() { + return derSchemas; + } + + @Override + public boolean add(final VirSchema schema) { + checkType(schema, Neo4jVirSchema.class); + return this.virSchemas.add((Neo4jVirSchema) schema); + } + + @Override + public List getVirSchemas() { + return virSchemas; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jApplication.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jApplication.java new file mode 100644 index 0000000000..e445e49eb0 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jApplication.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.core.persistence.neo4j.entity; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.entity.Application; +import org.apache.syncope.core.persistence.api.entity.Privilege; +import org.apache.syncope.core.persistence.common.validation.ApplicationCheck; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jApplication.NODE) +@ApplicationCheck +public class Neo4jApplication extends AbstractProvidedKeyNode implements Application { + + private static final long serialVersionUID = -5951400197744722305L; + + public static final String NODE = "Application"; + + public static final String APPLICATION_PRIVILEGE_REL = "APPLICATION_PRIVILEGE"; + + private String description; + + @Relationship(type = APPLICATION_PRIVILEGE_REL, direction = Relationship.Direction.INCOMING) + private List privileges = new ArrayList<>(); + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(final String description) { + this.description = description; + } + + @Override + public boolean add(final Privilege privilege) { + checkType(privilege, Neo4jPrivilege.class); + return privileges.contains((Neo4jPrivilege) privilege) || privileges.add((Neo4jPrivilege) privilege); + } + + @Override + public Optional getPrivilege(final String key) { + return privileges.stream().filter(privilege -> privilege.getKey().equals(key)).findFirst(); + } + + @Override + public List getPrivileges() { + return privileges; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAttributable.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAttributable.java new file mode 100644 index 0000000000..b4b2832dc0 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAttributable.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity; + +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; + +public interface Neo4jAttributable> { + + List> getPlainAttrs(); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAuditConf.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAuditConf.java new file mode 100644 index 0000000000..7fe48403ac --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAuditConf.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.core.persistence.neo4j.entity; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.AuditConf; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jAuditConf.NODE) +public class Neo4jAuditConf extends AbstractProvidedKeyNode implements AuditConf { + + private static final long serialVersionUID = 943012777014416027L; + + public static final String NODE = "AuditConf"; + + @NotNull + private Boolean active = true; + + @Override + public boolean isActive() { + return active; + } + + @Override + public void setActive(final boolean active) { + this.active = active; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAuditEntry.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAuditEntry.java new file mode 100644 index 0000000000..bcdc258642 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jAuditEntry.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.persistence.neo4j.entity; + +import org.apache.syncope.common.lib.audit.AuditEntry; +import org.springframework.data.neo4j.core.schema.GeneratedValue; +import org.springframework.data.neo4j.core.schema.Id; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.support.UUIDStringGenerator; + +@Node(Neo4jAuditEntry.NODE) +public class Neo4jAuditEntry extends AuditEntry { + + private static final long serialVersionUID = 279713256272635029L; + + public static final String NODE = "AuditEntry"; + + @Id + @GeneratedValue(UUIDStringGenerator.class) + private String id; + + public String getKey() { + return id; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jBatch.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jBatch.java new file mode 100644 index 0000000000..5505b9e2f9 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jBatch.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.persistence.neo4j.entity; + +import java.time.OffsetDateTime; +import org.apache.syncope.core.persistence.api.entity.Batch; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jBatch.NODE) +public class Neo4jBatch extends AbstractProvidedKeyNode implements Batch { + + private static final long serialVersionUID = 468423182798249255L; + + public static final String NODE = "Batch"; + + private OffsetDateTime expiryTime; + + private String results; + + @Override + public OffsetDateTime getExpiryTime() { + return expiryTime; + } + + @Override + public void setExpiryTime(final OffsetDateTime expiryTime) { + this.expiryTime = expiryTime; + } + + @Override + public String getResults() { + return results; + } + + @Override + public void setResults(final String results) { + this.results = results; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jConnInstance.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jConnInstance.java new file mode 100644 index 0000000000..94971e4d90 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jConnInstance.java @@ -0,0 +1,255 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity; + +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import org.apache.commons.lang3.StringUtils; +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.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.persistence.common.validation.ConnInstanceCheck; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.annotation.Transient; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.PostLoad; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jConnInstance.NODE) +@ConnInstanceCheck +public class Neo4jConnInstance extends AbstractGeneratedKeyNode implements ConnInstance { + + private static final long serialVersionUID = -2294708794497208872L; + + public static final String NODE = "ConnInstance"; + + protected static final TypeReference> TYPEREF = + new TypeReference>() { + }; + + private static final int DEFAULT_TIMEOUT = 10; + + /** + * URI identifying the local / remote ConnId location where the related connector bundle is found. + */ + @NotNull + private String location; + + /** + * Connector bundle class name. + * Within a given location, the triple + * (ConnectorBundle-Name, ConnectorBundle-Version, ConnectorBundle-Version) must be unique. + */ + @NotNull + private String connectorName; + + /** + * Qualified name for the connector bundle. + * Within a given location, the triple + * (ConnectorBundle-Name, ConnectorBundle-Version, ConnectorBundle-Version) must be unique. + */ + @NotNull + private String bundleName; + + /** + * Version of the bundle. + * Within a given location, the triple + * (ConnectorBundle-Name, ConnectorBundle-Version, ConnectorBundle-Version) must be unique. + */ + @NotNull + private String version; + + private String capabilities; + + @Transient + private Set capabilitiesSet = new HashSet<>(); + + /** + * The main configuration for the connector instance. This is directly implemented by the Configuration bean class + * which contains annotated ConfigurationProperties. + * + * @see org.identityconnectors.framework.api.ConfigurationProperty + */ + private String jsonConf; + + private String displayName; + + /** + * Connector request timeout. It is not applied in case of sync, full reconciliation and search. + * DEFAULT_TIMEOUT is the default value to be used in case of unspecified timeout. + */ + private Integer connRequestTimeout = DEFAULT_TIMEOUT; + + private String poolConf; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jRealm adminRealm; + + /** + * External resources associated to the connector. + */ + @Relationship(type = Neo4jExternalResource.RESOURCE_CONNECTOR_REL, direction = Relationship.Direction.INCOMING) + private List resources = new ArrayList<>(); + + @Override + public Realm getAdminRealm() { + return adminRealm; + } + + @Override + public void setAdminRealm(final Realm adminRealm) { + checkType(adminRealm, Neo4jRealm.class); + this.adminRealm = (Neo4jRealm) adminRealm; + } + + @Override + public String getLocation() { + return location; + } + + @Override + public void setLocation(final String location) { + this.location = location; + } + + @Override + public String getConnectorName() { + return connectorName; + } + + @Override + public void setConnectorName(final String connectorName) { + this.connectorName = connectorName; + } + + @Override + public String getBundleName() { + return bundleName; + } + + @Override + public void setBundleName(final String bundleName) { + this.bundleName = bundleName; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public void setVersion(final String version) { + this.version = version; + } + + @Override + public Set getConf() { + Set configuration = new HashSet<>(); + if (!StringUtils.isBlank(jsonConf)) { + configuration.addAll(List.of(POJOHelper.deserialize(jsonConf, ConnConfProperty[].class))); + } + + return configuration; + } + + @Override + public void setConf(final Collection conf) { + jsonConf = POJOHelper.serialize(new HashSet<>(conf)); + } + + @Override + public String getDisplayName() { + return displayName; + } + + @Override + public void setDisplayName(final String displayName) { + this.displayName = displayName; + } + + @Override + public List getResources() { + return resources; + } + + @Override + public boolean add(final ExternalResource resource) { + checkType(resource, Neo4jExternalResource.class); + return resources.contains((Neo4jExternalResource) resource) || resources.add((Neo4jExternalResource) resource); + } + + @Override + public Set getCapabilities() { + return capabilitiesSet; + } + + @Override + public Integer getConnRequestTimeout() { + // DEFAULT_TIMEOUT will be returned in case of null timeout: + // * instances created by the content loader + // * or with a timeout nullified explicitely + return Optional.ofNullable(connRequestTimeout).orElse(DEFAULT_TIMEOUT); + } + + @Override + public void setConnRequestTimeout(final Integer timeout) { + this.connRequestTimeout = timeout; + } + + @Override + public ConnPoolConf getPoolConf() { + return Optional.ofNullable(poolConf).map(pc -> POJOHelper.deserialize(pc, ConnPoolConf.class)).orElse(null); + } + + @Override + public void setPoolConf(final ConnPoolConf poolConf) { + this.poolConf = Optional.ofNullable(poolConf).map(POJOHelper::serialize).orElse(null); + } + + protected void json2list(final boolean clearFirst) { + if (clearFirst) { + getCapabilities().clear(); + } + if (capabilities != null) { + getCapabilities().addAll(POJOHelper.deserialize(capabilities, TYPEREF)); + } + } + + @PostLoad + public void postLoad() { + json2list(false); + } + + public void postSave() { + json2list(true); + } + + public void list2json() { + capabilities = POJOHelper.serialize(getCapabilities()); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDelegation.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDelegation.java new file mode 100644 index 0000000000..d9f602696c --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDelegation.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.persistence.neo4j.entity; + +import jakarta.validation.constraints.NotNull; +import java.time.OffsetDateTime; +import java.util.HashSet; +import java.util.Set; +import org.apache.syncope.core.persistence.api.entity.Delegation; +import org.apache.syncope.core.persistence.api.entity.Role; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.common.validation.DelegationCheck; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jDelegation.NODE) +@DelegationCheck +public class Neo4jDelegation extends AbstractGeneratedKeyNode implements Delegation { + + private static final long serialVersionUID = 17988340419552L; + + public static final String NODE = "Delegation"; + + public static final String DELEGATING_REL = "DELEGATING"; + + public static final String DELEGATED_REL = "DELEGATED"; + + @NotNull + @Relationship(type = DELEGATING_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jUser delegating; + + @NotNull + @Relationship(type = DELEGATED_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jUser delegated; + + @NotNull + private OffsetDateTime startDate; + + private OffsetDateTime endDate; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Set roles = new HashSet<>(); + + @Override + public User getDelegating() { + return delegating; + } + + @Override + public void setDelegating(final User delegating) { + checkType(delegating, Neo4jUser.class); + this.delegating = (Neo4jUser) delegating; + } + + @Override + public User getDelegated() { + return delegated; + } + + @Override + public void setDelegated(final User delegated) { + checkType(delegated, Neo4jUser.class); + this.delegated = (Neo4jUser) delegated; + } + + @Override + public OffsetDateTime getStart() { + return startDate; + } + + @Override + public void setStart(final OffsetDateTime start) { + this.startDate = start; + } + + @Override + public OffsetDateTime getEnd() { + return endDate; + } + + @Override + public void setEnd(final OffsetDateTime end) { + this.endDate = end; + } + + @Override + public boolean add(final Role role) { + checkType(role, Neo4jRole.class); + return roles.add((Neo4jRole) role); + } + + @Override + public Set getRoles() { + return roles; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDerSchema.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDerSchema.java new file mode 100644 index 0000000000..88e88c4e80 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDerSchema.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.persistence.neo4j.entity; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.DerSchema; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jDerSchema.NODE) +public class Neo4jDerSchema extends Neo4jSchema implements DerSchema { + + private static final long serialVersionUID = -8814919746941920274L; + + public static final String NODE = "DerSchema"; + + @NotNull + private String expression; + + @Relationship(type = Neo4jAnyTypeClass.ANY_TYPE_CLASS_DER_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jAnyTypeClass anyTypeClass; + + @Override + public String getExpression() { + return expression; + } + + @Override + public void setExpression(final String expression) { + this.expression = expression; + } + + @Override + public AttrSchemaType getType() { + return AttrSchemaType.String; + } + + @Override + public String getMandatoryCondition() { + return Boolean.FALSE.toString().toLowerCase(); + } + + @Override + public boolean isMultivalue() { + return Boolean.TRUE; + } + + @Override + public boolean isUniqueConstraint() { + return Boolean.FALSE; + } + + @Override + public boolean isReadonly() { + return Boolean.FALSE; + } + + @Override + public AnyTypeClass getAnyTypeClass() { + return anyTypeClass; + } + + @Override + public void setAnyTypeClass(final AnyTypeClass anyTypeClass) { + checkType(anyTypeClass, Neo4jAnyTypeClass.class); + this.anyTypeClass = (Neo4jAnyTypeClass) anyTypeClass; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDynRealm.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDynRealm.java new file mode 100644 index 0000000000..d19314ab24 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDynRealm.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.neo4j.entity; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.DynRealm; +import org.apache.syncope.core.persistence.api.entity.DynRealmMembership; +import org.apache.syncope.core.persistence.common.validation.DynRealmCheck; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jDynRealm.NODE) +@DynRealmCheck +public class Neo4jDynRealm extends AbstractProvidedKeyNode implements DynRealm { + + private static final long serialVersionUID = -6851035842423560341L; + + public static final String NODE = "DynRealm"; + + public static final String DYNREALM_MEMBERSHIP_REL = "DYNREALM_MEMBERSHIP"; + + @Relationship(type = DYNREALM_MEMBERSHIP_REL, direction = Relationship.Direction.INCOMING) + private List dynMemberships = new ArrayList<>(); + + @Override + public boolean add(final DynRealmMembership dynRealmMembership) { + checkType(dynRealmMembership, Neo4jDynRealmMembership.class); + return this.dynMemberships.add((Neo4jDynRealmMembership) dynRealmMembership); + } + + @Override + public Optional getDynMembership(final AnyType anyType) { + return dynMemberships.stream(). + filter(dynRealmMembership -> anyType != null && anyType.equals(dynRealmMembership.getAnyType())). + findFirst(); + } + + @Override + public List getDynMemberships() { + return dynMemberships; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDynRealmMembership.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDynRealmMembership.java new file mode 100644 index 0000000000..72cc3a2f1f --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jDynRealmMembership.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.persistence.neo4j.entity; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.DynRealm; +import org.apache.syncope.core.persistence.api.entity.DynRealmMembership; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jDynRealmMembership.NODE) +public class Neo4jDynRealmMembership extends AbstractGeneratedKeyNode implements DynRealmMembership { + + private static final long serialVersionUID = 8157856850557493134L; + + public static final String NODE = "DynRealmMembership"; + + @NotNull + private String fiql; + + @Relationship(type = Neo4jDynRealm.DYNREALM_MEMBERSHIP_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jDynRealm dynRealm; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jAnyType anyType; + + @Override + public String getFIQLCond() { + return fiql; + } + + @Override + public void setFIQLCond(final String fiql) { + this.fiql = fiql; + } + + @Override + public DynRealm getDynRealm() { + return dynRealm; + } + + @Override + public void setDynRealm(final DynRealm dynRealm) { + checkType(dynRealm, Neo4jDynRealm.class); + this.dynRealm = (Neo4jDynRealm) dynRealm; + } + + @Override + public AnyType getAnyType() { + return anyType; + } + + @Override + public void setAnyType(final AnyType anyType) { + checkType(anyType, Neo4jAnyType.class); + this.anyType = (Neo4jAnyType) anyType; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java new file mode 100644 index 0000000000..9ccca1bd05 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.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.persistence.neo4j.entity; + +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.persistence.api.entity.AccessToken; +import org.apache.syncope.core.persistence.api.entity.AnyAbout; +import org.apache.syncope.core.persistence.api.entity.AnyTemplateRealm; +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.Application; +import org.apache.syncope.core.persistence.api.entity.AuditConf; +import org.apache.syncope.core.persistence.api.entity.Batch; +import org.apache.syncope.core.persistence.api.entity.ConnInstance; +import org.apache.syncope.core.persistence.api.entity.Delegation; +import org.apache.syncope.core.persistence.api.entity.DerSchema; +import org.apache.syncope.core.persistence.api.entity.DynRealm; +import org.apache.syncope.core.persistence.api.entity.DynRealmMembership; +import org.apache.syncope.core.persistence.api.entity.Entity; +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.FIQLQuery; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.JobStatus; +import org.apache.syncope.core.persistence.api.entity.MailTemplate; +import org.apache.syncope.core.persistence.api.entity.Notification; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.Privilege; +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.Remediation; +import org.apache.syncope.core.persistence.api.entity.Report; +import org.apache.syncope.core.persistence.api.entity.ReportExec; +import org.apache.syncope.core.persistence.api.entity.Role; +import org.apache.syncope.core.persistence.api.entity.SRARoute; +import org.apache.syncope.core.persistence.api.entity.VirSchema; +import org.apache.syncope.core.persistence.api.entity.am.AttrRepo; +import org.apache.syncope.core.persistence.api.entity.am.AuthModule; +import org.apache.syncope.core.persistence.api.entity.am.AuthProfile; +import org.apache.syncope.core.persistence.api.entity.am.CASSPClientApp; +import org.apache.syncope.core.persistence.api.entity.am.OIDCJWKS; +import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp; +import org.apache.syncope.core.persistence.api.entity.am.SAML2IdPEntity; +import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp; +import org.apache.syncope.core.persistence.api.entity.am.SAML2SPEntity; +import org.apache.syncope.core.persistence.api.entity.am.WAConfigEntry; +import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership; +import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership; +import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttr; +import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttrUniqueValue; +import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.anyobject.ARelationship; +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr; +import org.apache.syncope.core.persistence.api.entity.group.GPlainAttrUniqueValue; +import org.apache.syncope.core.persistence.api.entity.group.GPlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.group.TypeExtension; +import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy; +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.PasswordPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.PullCorrelationRuleEntity; +import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.PushCorrelationRuleEntity; +import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; +import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask; +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.PullTask; +import org.apache.syncope.core.persistence.api.entity.task.PushTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.api.entity.user.LAPlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.LAPlainAttrUniqueValue; +import org.apache.syncope.core.persistence.api.entity.user.LAPlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount; +import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion; +import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; +import org.apache.syncope.core.persistence.api.entity.user.UMembership; +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrUniqueValue; +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.user.URelationship; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jAttrRepo; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jAuthModule; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jAuthProfile; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jCASSPClientApp; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jOIDCJWKS; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jOIDCRPClientApp; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jSAML2IdPEntity; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jSAML2SPClientApp; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jSAML2SPEntity; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jWAConfigEntry; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jADynGroupMembership; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAMembership; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAPlainAttr; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAPlainAttrUniqueValue; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAPlainAttrValue; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jARelationship; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAnyObject; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGPlainAttr; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGPlainAttrUniqueValue; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGPlainAttrValue; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jTypeExtension; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccessPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccountPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAttrReleasePolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAuthPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPasswordPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPropagationPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPullCorrelationRuleEntity; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPullPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPushCorrelationRuleEntity; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPushPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jTicketExpirationPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jAnyTemplatePullTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jMacroTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jNotificationTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jPropagationTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jPullTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jPushTask; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jSchedTask; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jLAPlainAttr; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jLAPlainAttrUniqueValue; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jLAPlainAttrValue; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jLinkedAccount; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jSecurityQuestion; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUDynGroupMembership; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUMembership; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUPlainAttr; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUPlainAttrUniqueValue; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUPlainAttrValue; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jURelationship; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; +import org.apache.syncope.core.spring.security.SecureRandomUtils; + +public class Neo4jEntityFactory implements EntityFactory { + + @SuppressWarnings("unchecked") + @Override + public E newEntity(final Class reference) { + E result; + + if (reference.equals(Realm.class)) { + result = (E) new Neo4jRealm(); + } else if (reference.equals(DynRealm.class)) { + result = (E) new Neo4jDynRealm(); + } else if (reference.equals(DynRealmMembership.class)) { + result = (E) new Neo4jDynRealmMembership(); + } else if (reference.equals(AnyTemplateRealm.class)) { + result = (E) new Neo4jAnyTemplateRealm(); + } else if (reference.equals(AccountPolicy.class)) { + result = (E) new Neo4jAccountPolicy(); + } else if (reference.equals(PasswordPolicy.class)) { + result = (E) new Neo4jPasswordPolicy(); + } else if (reference.equals(PropagationPolicy.class)) { + result = (E) new Neo4jPropagationPolicy(); + } else if (reference.equals(PushPolicy.class)) { + result = (E) new Neo4jPushPolicy(); + } else if (reference.equals(PullPolicy.class)) { + result = (E) new Neo4jPullPolicy(); + } else if (reference.equals(PullCorrelationRuleEntity.class)) { + result = (E) new Neo4jPullCorrelationRuleEntity(); + } else if (reference.equals(PushCorrelationRuleEntity.class)) { + result = (E) new Neo4jPushCorrelationRuleEntity(); + } else if (reference.equals(AnyTypeClass.class)) { + result = (E) new Neo4jAnyTypeClass(); + } else if (reference.equals(AnyType.class)) { + result = (E) new Neo4jAnyType(); + } else if (reference.equals(AnyObject.class)) { + result = (E) new Neo4jAnyObject(); + } else if (reference.equals(Role.class)) { + result = (E) new Neo4jRole(); + } else if (reference.equals(Application.class)) { + result = (E) new Neo4jApplication(); + } else if (reference.equals(Privilege.class)) { + result = (E) new Neo4jPrivilege(); + } else if (reference.equals(User.class)) { + result = (E) new Neo4jUser(); + } else if (reference.equals(Group.class)) { + result = (E) new Neo4jGroup(); + } else if (reference.equals(TypeExtension.class)) { + result = (E) new Neo4jTypeExtension(); + } else if (reference.equals(RelationshipType.class)) { + result = (E) new Neo4jRelationshipType(); + } else if (reference.equals(ARelationship.class)) { + result = (E) new Neo4jARelationship(); + } else if (reference.equals(URelationship.class)) { + result = (E) new Neo4jURelationship(); + } else if (reference.equals(AMembership.class)) { + result = (E) new Neo4jAMembership(); + } else if (reference.equals(UMembership.class)) { + result = (E) new Neo4jUMembership(); + } else if (reference.equals(LinkedAccount.class)) { + result = (E) new Neo4jLinkedAccount(); + } else if (reference.equals(AnyAbout.class)) { + result = (E) new Neo4jAnyAbout(); + } else if (reference.equals(MailTemplate.class)) { + result = (E) new Neo4jMailTemplate(); + } else if (reference.equals(Notification.class)) { + result = (E) new Neo4jNotification(); + } else if (reference.equals(ConnInstance.class)) { + result = (E) new Neo4jConnInstance(); + } else if (reference.equals(ExternalResource.class)) { + result = (E) new Neo4jExternalResource(); + } else if (reference.equals(PlainSchema.class)) { + result = (E) new Neo4jPlainSchema(); + } else if (reference.equals(APlainAttr.class)) { + result = (E) new Neo4jAPlainAttr(); + } else if (reference.equals(APlainAttrValue.class)) { + result = (E) new Neo4jAPlainAttrValue(); + } else if (reference.equals(APlainAttrUniqueValue.class)) { + result = (E) new Neo4jAPlainAttrUniqueValue(); + } else if (reference.equals(UPlainAttr.class)) { + result = (E) new Neo4jUPlainAttr(); + } else if (reference.equals(UPlainAttrValue.class)) { + result = (E) new Neo4jUPlainAttrValue(); + } else if (reference.equals(UPlainAttrUniqueValue.class)) { + result = (E) new Neo4jUPlainAttrUniqueValue(); + } else if (reference.equals(LAPlainAttr.class)) { + result = (E) new Neo4jLAPlainAttr(); + } else if (reference.equals(LAPlainAttrValue.class)) { + result = (E) new Neo4jLAPlainAttrValue(); + } else if (reference.equals(LAPlainAttrUniqueValue.class)) { + result = (E) new Neo4jLAPlainAttrUniqueValue(); + } else if (reference.equals(DerSchema.class)) { + result = (E) new Neo4jDerSchema(); + } else if (reference.equals(VirSchema.class)) { + result = (E) new Neo4jVirSchema(); + } else if (reference.equals(GPlainAttr.class)) { + result = (E) new Neo4jGPlainAttr(); + } else if (reference.equals(GPlainAttrValue.class)) { + result = (E) new Neo4jGPlainAttrValue(); + } else if (reference.equals(GPlainAttrUniqueValue.class)) { + result = (E) new Neo4jGPlainAttrUniqueValue(); + } else if (reference.equals(Report.class)) { + result = (E) new Neo4jReport(); + } else if (reference.equals(ReportExec.class)) { + result = (E) new Neo4jReportExec(); + } else if (reference.equals(NotificationTask.class)) { + result = (E) new Neo4jNotificationTask(); + } else if (reference.equals(PropagationTask.class)) { + result = (E) new Neo4jPropagationTask(); + } else if (reference.equals(PushTask.class)) { + result = (E) new Neo4jPushTask(); + } else if (reference.equals(PullTask.class)) { + result = (E) new Neo4jPullTask(); + } else if (reference.equals(MacroTask.class)) { + result = (E) new Neo4jMacroTask(); + } else if (reference.equals(SchedTask.class)) { + result = (E) new Neo4jSchedTask(); + } else if (reference.equals(AnyTemplatePullTask.class)) { + result = (E) new Neo4jAnyTemplatePullTask(); + } else if (reference.equals(SecurityQuestion.class)) { + result = (E) new Neo4jSecurityQuestion(); + } else if (reference.equals(AuditConf.class)) { + result = (E) new Neo4jAuditConf(); + } else if (reference.equals(ADynGroupMembership.class)) { + result = (E) new Neo4jADynGroupMembership(); + } else if (reference.equals(UDynGroupMembership.class)) { + result = (E) new Neo4jUDynGroupMembership(); + } else if (reference.equals(AccessToken.class)) { + result = (E) new Neo4jAccessToken(); + } else if (reference.equals(Implementation.class)) { + result = (E) new Neo4jImplementation(); + } else if (reference.equals(Remediation.class)) { + result = (E) new Neo4jRemediation(); + } else if (reference.equals(Batch.class)) { + result = (E) new Neo4jBatch(); + } else if (reference.equals(Delegation.class)) { + result = (E) new Neo4jDelegation(); + } else if (reference.equals(FIQLQuery.class)) { + result = (E) new Neo4jFIQLQuery(); + } else if (reference.equals(JobStatus.class)) { + result = (E) new Neo4jJobStatus(); + } else if (reference.equals(SRARoute.class)) { + result = (E) new Neo4jSRARoute(); + } else if (reference.equals(AuthModule.class)) { + result = (E) new Neo4jAuthModule(); + } else if (reference.equals(AttrRepo.class)) { + result = (E) new Neo4jAttrRepo(); + } else if (reference.equals(AuthPolicy.class)) { + result = (E) new Neo4jAuthPolicy(); + } else if (reference.equals(AccessPolicy.class)) { + result = (E) new Neo4jAccessPolicy(); + } else if (reference.equals(AttrReleasePolicy.class)) { + result = (E) new Neo4jAttrReleasePolicy(); + } else if (reference.equals(TicketExpirationPolicy.class)) { + result = (E) new Neo4jTicketExpirationPolicy(); + } else if (reference.equals(OIDCRPClientApp.class)) { + result = (E) new Neo4jOIDCRPClientApp(); + } else if (reference.equals(CASSPClientApp.class)) { + result = (E) new Neo4jCASSPClientApp(); + } else if (reference.equals(SAML2SPClientApp.class)) { + result = (E) new Neo4jSAML2SPClientApp(); + } else if (reference.equals(SAML2IdPEntity.class)) { + result = (E) new Neo4jSAML2IdPEntity(); + } else if (reference.equals(SAML2SPEntity.class)) { + result = (E) new Neo4jSAML2SPEntity(); + } else if (reference.equals(AuthProfile.class)) { + result = (E) new Neo4jAuthProfile(); + } else if (reference.equals(OIDCJWKS.class)) { + result = (E) new Neo4jOIDCJWKS(); + } else if (reference.equals(WAConfigEntry.class)) { + result = (E) new Neo4jWAConfigEntry(); + } else { + throw new IllegalArgumentException("Could not find a Neo4j implementation of " + reference.getName()); + } + + if (result instanceof AbstractGeneratedKeyNode generatedKeyEntity) { + generatedKeyEntity.setKey(SecureRandomUtils.generateRandomUUID().toString()); + } + + return result; + } + + @Override + public Class userClass() { + return Neo4jUser.class; + } + + @Override + public Class groupClass() { + return Neo4jGroup.class; + } + + @Override + public Class anyObjectClass() { + return Neo4jAnyObject.class; + } + + @Override + public Class anySearchDAOClass() { + return null; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jExternalResource.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jExternalResource.java new file mode 100644 index 0000000000..137dfe2351 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jExternalResource.java @@ -0,0 +1,392 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity; + +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.to.OrgUnit; +import org.apache.syncope.common.lib.to.Provision; +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.TraceLevel; +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.Implementation; +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.policy.PropagationPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; +import org.apache.syncope.core.persistence.common.validation.ExternalResourceCheck; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccountPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPasswordPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPropagationPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPullPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPushPolicy; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.annotation.Transient; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.PostLoad; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jExternalResource.NODE) +@ExternalResourceCheck +public class Neo4jExternalResource extends AbstractProvidedKeyNode implements ExternalResource { + + private static final long serialVersionUID = -6937712883512073278L; + + public static final String NODE = "ExternalResource"; + + public static final String RESOURCE_CONNECTOR_REL = "RESOURCE_CONNECTOR"; + + public static final String RESOURCE_PASSWORD_POLICY_REL = "RESOURCE_PASSWORD_POLICY"; + + public static final String RESOURCE_ACCOUNT_POLICY_REL = "RESOURCE_ACCOUNT_POLICY"; + + public static final String RESOURCE_PROPAGATION_POLICY_REL = "RESOURCE_PROPAGATION_POLICY"; + + public static final String RESOURCE_PULL_POLICY_REL = "RESOURCE_PULL_POLICY"; + + public static final String RESOURCE_PUSH_POLICY_REL = "RESOURCE_PUSH_POLICY"; + + public static final String RESOURCE_PROVISION_SORTER_REL = "RESOURCE_PROVISION_SORTER"; + + public static final String RESOURCE_PROPAGATION_ACTIONS_REL = "RESOURCE_PROPAGATION_ACTIONS"; + + protected static final TypeReference> CAPABILITY_TYPEREF = + new TypeReference>() { + }; + + protected static final TypeReference> PROVISION_TYPEREF = + new TypeReference>() { + }; + + /** + * Should this resource enforce the mandatory constraints? + */ + @NotNull + private Boolean enforceMandatoryCondition = false; + + /** + * Priority index for propagation ordering. + */ + private Integer propagationPriority; + + @NotNull + private TraceLevel createTraceLevel = TraceLevel.FAILURES; + + @NotNull + private TraceLevel updateTraceLevel = TraceLevel.FAILURES; + + @NotNull + private TraceLevel deleteTraceLevel = TraceLevel.FAILURES; + + @NotNull + private TraceLevel provisioningTraceLevel = TraceLevel.FAILURES; + + /** + * Configuration properties that are override from the connector instance. + */ + private String jsonConf; + + @NotNull + private Boolean overrideCapabilities = false; + + private String capabilitiesOverride; + + @Transient + private final Set capabilitiesOverrideSet = new HashSet<>(); + + private String provisions; + + @Transient + private final List provisionList = new ArrayList<>(); + + private String orgUnit; + + /** + * The resource type is identified by the associated connector. + */ + @Relationship(type = RESOURCE_CONNECTOR_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jConnInstance connector; + + @Relationship(type = RESOURCE_PASSWORD_POLICY_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jPasswordPolicy passwordPolicy; + + @Relationship(type = RESOURCE_ACCOUNT_POLICY_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jAccountPolicy accountPolicy; + + @Relationship(type = RESOURCE_PROPAGATION_POLICY_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jPropagationPolicy propagationPolicy; + + @Relationship(type = RESOURCE_PULL_POLICY_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jPullPolicy pullPolicy; + + @Relationship(type = RESOURCE_PUSH_POLICY_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jPushPolicy pushPolicy; + + @Relationship(type = RESOURCE_PROVISION_SORTER_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jImplementation provisionSorter; + + @Relationship(type = RESOURCE_PROPAGATION_ACTIONS_REL, direction = Relationship.Direction.INCOMING) + private List propagationActions = new ArrayList<>(); + + @Override + public boolean isEnforceMandatoryCondition() { + return enforceMandatoryCondition; + } + + @Override + public void setEnforceMandatoryCondition(final boolean enforceMandatoryCondition) { + this.enforceMandatoryCondition = enforceMandatoryCondition; + } + + @Override + public Optional getProvisionByAnyType(final String anyType) { + return getProvisions().stream(). + filter(provision -> provision.getAnyType().equals(anyType)).findFirst(); + } + + @Override + public Optional getProvisionByObjectClass(final String objectClass) { + return getProvisions().stream(). + filter(provision -> provision.getObjectClass().equals(objectClass)).findFirst(); + } + + @Override + public List getProvisions() { + return provisionList; + } + + @Override + public OrgUnit getOrgUnit() { + return Optional.ofNullable(orgUnit).map(ou -> POJOHelper.deserialize(ou, OrgUnit.class)).orElse(null); + } + + @Override + public void setOrgUnit(final OrgUnit orgUnit) { + this.orgUnit = orgUnit == null ? null : POJOHelper.serialize(orgUnit); + } + + @Override + public Integer getPropagationPriority() { + return propagationPriority; + } + + @Override + public void setPropagationPriority(final Integer propagationPriority) { + this.propagationPriority = propagationPriority; + } + + @Override + public TraceLevel getCreateTraceLevel() { + return createTraceLevel; + } + + @Override + public void setCreateTraceLevel(final TraceLevel createTraceLevel) { + this.createTraceLevel = createTraceLevel; + } + + @Override + + public TraceLevel getDeleteTraceLevel() { + return deleteTraceLevel; + } + + @Override + public void setDeleteTraceLevel(final TraceLevel deleteTraceLevel) { + this.deleteTraceLevel = deleteTraceLevel; + } + + @Override + public TraceLevel getUpdateTraceLevel() { + return updateTraceLevel; + } + + @Override + public void setUpdateTraceLevel(final TraceLevel updateTraceLevel) { + this.updateTraceLevel = updateTraceLevel; + } + + @Override + public TraceLevel getProvisioningTraceLevel() { + return provisioningTraceLevel; + } + + @Override + public void setProvisioningTraceLevel(final TraceLevel provisioningTraceLevel) { + this.provisioningTraceLevel = provisioningTraceLevel; + } + + @Override + public Set getConfOverride() { + Set confOverride = new HashSet<>(); + if (!StringUtils.isBlank(jsonConf)) { + confOverride.addAll(List.of(POJOHelper.deserialize(jsonConf, ConnConfProperty[].class))); + } + + return confOverride; + } + + @Override + public void setConfOverride(final Set confOverride) { + jsonConf = POJOHelper.serialize(new HashSet<>(confOverride)); + } + + @Override + public boolean isOverrideCapabilities() { + return overrideCapabilities; + } + + @Override + public void setOverrideCapabilities(final boolean overrideCapabilities) { + this.overrideCapabilities = overrideCapabilities; + } + + @Override + public Set getCapabilitiesOverride() { + return capabilitiesOverrideSet; + } + + @Override + public ConnInstance getConnector() { + return connector; + } + + @Override + public void setConnector(final ConnInstance connector) { + checkType(connector, Neo4jConnInstance.class); + this.connector = (Neo4jConnInstance) connector; + } + + @Override + public AccountPolicy getAccountPolicy() { + return accountPolicy; + } + + @Override + public void setAccountPolicy(final AccountPolicy accountPolicy) { + checkType(accountPolicy, Neo4jAccountPolicy.class); + this.accountPolicy = (Neo4jAccountPolicy) accountPolicy; + } + + @Override + public PasswordPolicy getPasswordPolicy() { + return passwordPolicy; + } + + @Override + public void setPasswordPolicy(final PasswordPolicy passwordPolicy) { + checkType(passwordPolicy, Neo4jPasswordPolicy.class); + this.passwordPolicy = (Neo4jPasswordPolicy) passwordPolicy; + } + + @Override + public PropagationPolicy getPropagationPolicy() { + return propagationPolicy; + } + + @Override + public void setPropagationPolicy(final PropagationPolicy propagationPolicy) { + checkType(propagationPolicy, Neo4jPropagationPolicy.class); + this.propagationPolicy = (Neo4jPropagationPolicy) propagationPolicy; + } + + @Override + public PullPolicy getPullPolicy() { + return pullPolicy; + } + + @Override + public void setPullPolicy(final PullPolicy pullPolicy) { + checkType(pullPolicy, Neo4jPullPolicy.class); + this.pullPolicy = (Neo4jPullPolicy) pullPolicy; + } + + @Override + public PushPolicy getPushPolicy() { + return pushPolicy; + } + + @Override + public void setPushPolicy(final PushPolicy pushPolicy) { + checkType(pushPolicy, Neo4jPushPolicy.class); + this.pushPolicy = (Neo4jPushPolicy) pushPolicy; + } + + @Override + public Implementation getProvisionSorter() { + return provisionSorter; + } + + @Override + public void setProvisionSorter(final Implementation provisionSorter) { + checkType(provisionSorter, Neo4jImplementation.class); + checkImplementationType(provisionSorter, IdMImplementationType.PROVISION_SORTER); + this.provisionSorter = (Neo4jImplementation) provisionSorter; + } + + @Override + public boolean add(final Implementation propagationAction) { + checkType(propagationAction, Neo4jImplementation.class); + checkImplementationType(propagationAction, IdMImplementationType.PROPAGATION_ACTIONS); + return propagationActions.contains((Neo4jImplementation) propagationAction) + || propagationActions.add((Neo4jImplementation) propagationAction); + } + + @Override + public List getPropagationActions() { + return propagationActions; + } + + protected void json2list(final boolean clearFirst) { + if (clearFirst) { + getCapabilitiesOverride().clear(); + getProvisions().clear(); + } + if (capabilitiesOverride != null) { + getCapabilitiesOverride().addAll(POJOHelper.deserialize(capabilitiesOverride, CAPABILITY_TYPEREF)); + } + if (provisions != null) { + getProvisions().addAll(POJOHelper.deserialize(provisions, PROVISION_TYPEREF)); + } + } + + @PostLoad + public void postLoad() { + json2list(false); + } + + public void postSave() { + json2list(true); + } + + public void list2json() { + capabilitiesOverride = POJOHelper.serialize(getCapabilitiesOverride()); + provisions = POJOHelper.serialize(getProvisions()); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jFIQLQuery.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jFIQLQuery.java new file mode 100644 index 0000000000..4414fcdedd --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jFIQLQuery.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.persistence.neo4j.entity; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.FIQLQuery; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jFIQLQuery.NODE) +public class Neo4jFIQLQuery extends AbstractGeneratedKeyNode implements FIQLQuery { + + private static final long serialVersionUID = -8800817340585235280L; + + public static final String NODE = "FIQLQuery"; + + @NotNull + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jUser owner; + + @NotNull + private String name; + + @NotNull + private String target; + + @NotNull + private String fiql; + + @Override + public Neo4jUser getOwner() { + return owner; + } + + @Override + public void setOwner(final User owner) { + checkType(owner, Neo4jUser.class); + this.owner = (Neo4jUser) owner; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(final String name) { + this.name = name; + } + + @Override + public String getTarget() { + return target; + } + + @Override + public void setTarget(final String target) { + this.target = target; + } + + @Override + public String getFIQL() { + return fiql; + } + + @Override + public void setFIQL(final String fiql) { + this.fiql = fiql; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jImplementation.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jImplementation.java new file mode 100644 index 0000000000..a580a8b286 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jImplementation.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.persistence.neo4j.entity; + +import org.apache.syncope.common.lib.types.ImplementationEngine; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.common.validation.ImplementationCheck; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jImplementation.NODE) +@ImplementationCheck +public class Neo4jImplementation extends AbstractProvidedKeyNode implements Implementation { + + private static final long serialVersionUID = 7668545382291708999L; + + public static final String NODE = "Implementation"; + + private ImplementationEngine engine; + + private String type; + + private String body; + + @Override + public ImplementationEngine getEngine() { + return engine; + } + + @Override + public void setEngine(final ImplementationEngine engine) { + this.engine = engine; + } + + @Override + public String getType() { + return type; + } + + @Override + public void setType(final String type) { + this.type = type; + } + + @Override + public String getBody() { + return body; + } + + @Override + public void setBody(final String body) { + this.body = body; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jJobStatus.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jJobStatus.java new file mode 100644 index 0000000000..dbd4abc900 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jJobStatus.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.neo4j.entity; + +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.core.persistence.api.entity.JobStatus; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jJobStatus.NODE) +public class Neo4jJobStatus extends AbstractProvidedKeyNode implements JobStatus { + + private static final long serialVersionUID = 9061740216669505871L; + + public static final String NODE = "JobStatus"; + + private static final int STATUS_MAX_LENGTH = 255; + + private String jobStatus; + + @Override + public String getStatus() { + return jobStatus; + } + + @Override + public void setStatus(final String status) { + jobStatus = StringUtils.abbreviate(status, STATUS_MAX_LENGTH); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jMailTemplate.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jMailTemplate.java new file mode 100644 index 0000000000..4dee28409e --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jMailTemplate.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.core.persistence.neo4j.entity; + +import org.apache.syncope.core.persistence.api.entity.MailTemplate; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jMailTemplate.NODE) +public class Neo4jMailTemplate extends AbstractProvidedKeyNode implements MailTemplate { + + private static final long serialVersionUID = 2668267884059219835L; + + public static final String NODE = "MailTemplate"; + + private String textTemplate; + + private String htmlTemplate; + + @Override + public String getTextTemplate() { + return textTemplate; + } + + @Override + public void setTextTemplate(final String textTemplate) { + this.textTemplate = textTemplate; + } + + @Override + public String getHTMLTemplate() { + return htmlTemplate; + } + + @Override + public void setHTMLTemplate(final String htmlTemplate) { + this.htmlTemplate = htmlTemplate; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jNotification.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jNotification.java new file mode 100644 index 0000000000..ce4c71a874 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jNotification.java @@ -0,0 +1,237 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity; + +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.types.IdRepoImplementationType; +import org.apache.syncope.common.lib.types.TraceLevel; +import org.apache.syncope.core.persistence.api.entity.AnyAbout; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.MailTemplate; +import org.apache.syncope.core.persistence.api.entity.Notification; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.annotation.Transient; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.PostLoad; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jNotification.NODE) +public class Neo4jNotification extends AbstractGeneratedKeyNode implements Notification { + + private static final long serialVersionUID = 3112582296912757537L; + + public static final String NODE = "Notification"; + + public static final String NOTIFICATION_ABOUT_REL = "NOTIFICATION_ABOUT"; + + protected static final TypeReference> TYPEREF = new TypeReference>() { + }; + + private String events; + + @Transient + private List eventsList = new ArrayList<>(); + + @Relationship(type = NOTIFICATION_ABOUT_REL, direction = Relationship.Direction.INCOMING) + private List abouts = new ArrayList<>(); + + private String recipientsFIQL; + + private String staticRecipients; + + @Transient + private List staticRecipientsList = new ArrayList<>(); + + @NotNull + private String recipientAttrName; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jImplementation recipientsProvider; + + @NotNull + private Boolean selfAsRecipient = false; + + @NotNull + private String sender; + + @NotNull + private String subject; + + @NotNull + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jMailTemplate template; + + @NotNull + private TraceLevel traceLevel = TraceLevel.ALL; + + @NotNull + private Boolean active = true; + + @Override + public String getRecipientsFIQL() { + return recipientsFIQL; + } + + @Override + public void setRecipientsFIQL(final String recipientsFIQL) { + this.recipientsFIQL = recipientsFIQL; + } + + @Override + public String getRecipientAttrName() { + return recipientAttrName; + } + + @Override + public void setRecipientAttrName(final String recipientAttrName) { + this.recipientAttrName = recipientAttrName; + } + + @Override + public Implementation getRecipientsProvider() { + return recipientsProvider; + } + + @Override + public void setRecipientsProvider(final Implementation recipientsProvider) { + checkType(recipientsProvider, Neo4jImplementation.class); + checkImplementationType(recipientsProvider, IdRepoImplementationType.RECIPIENTS_PROVIDER); + this.recipientsProvider = (Neo4jImplementation) recipientsProvider; + } + + @Override + public List getEvents() { + return eventsList; + } + + @Override + public boolean add(final AnyAbout about) { + checkType(about, Neo4jAnyAbout.class); + return this.abouts.add((Neo4jAnyAbout) about); + } + + @Override + public Optional getAbout(final AnyType anyType) { + return abouts.stream().filter(about -> anyType != null && anyType.equals(about.getAnyType())).findFirst(); + } + + @Override + public List getAbouts() { + return abouts; + } + + @Override + public List getStaticRecipients() { + return staticRecipientsList; + } + + @Override + public boolean isSelfAsRecipient() { + return selfAsRecipient; + } + + @Override + public void setSelfAsRecipient(final boolean selfAsRecipient) { + this.selfAsRecipient = selfAsRecipient; + } + + @Override + public String getSender() { + return sender; + } + + @Override + public void setSender(final String sender) { + this.sender = sender; + } + + @Override + public String getSubject() { + return subject; + } + + @Override + + public void setSubject(final String subject) { + this.subject = subject; + } + + @Override + public MailTemplate getTemplate() { + return template; + } + + @Override + public void setTemplate(final MailTemplate template) { + checkType(template, Neo4jMailTemplate.class); + this.template = (Neo4jMailTemplate) template; + } + + @Override + public TraceLevel getTraceLevel() { + return traceLevel; + } + + @Override + public void setTraceLevel(final TraceLevel traceLevel) { + this.traceLevel = traceLevel; + } + + @Override + public boolean isActive() { + return active; + } + + @Override + public void setActive(final boolean active) { + this.active = active; + } + + protected void json2list(final boolean clearFirst) { + if (clearFirst) { + getEvents().clear(); + getStaticRecipients().clear(); + } + if (events != null) { + getEvents().addAll(POJOHelper.deserialize(events, TYPEREF)); + } + if (staticRecipients != null) { + getStaticRecipients().addAll(POJOHelper.deserialize(staticRecipients, TYPEREF)); + } + } + + @PostLoad + public void postLoad() { + json2list(false); + } + + public void postSave() { + json2list(true); + } + + public void list2json() { + events = POJOHelper.serialize(getEvents()); + staticRecipients = POJOHelper.serialize(getStaticRecipients()); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPlainAttr.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPlainAttr.java new file mode 100644 index 0000000000..ff2ec8d8b1 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPlainAttr.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity; + +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; + +public interface Neo4jPlainAttr> extends PlainAttr { + + String getSchemaKey(); + + void setSchemaKey(String schemaKey); + + boolean add(PlainAttrValue value); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPlainSchema.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPlainSchema.java new file mode 100644 index 0000000000..76af0299db --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPlainSchema.java @@ -0,0 +1,202 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.common.lib.types.IdRepoImplementationType; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.common.validation.PlainSchemaCheck; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jPlainSchema.NODE) +@PlainSchemaCheck +public class Neo4jPlainSchema extends Neo4jSchema implements PlainSchema { + + private static final long serialVersionUID = -4249639444022112516L; + + public static final String NODE = "PlainSchema"; + + @NotNull + private AttrSchemaType type = AttrSchemaType.String; + + @NotNull + private String mandatoryCondition = Boolean.FALSE.toString(); + + private Boolean multivalue = false; + + private Boolean uniqueConstraint = false; + + private Boolean readonly = false; + + private String conversionPattern; + + private String enumerationValues; + + private String enumerationKeys; + + private String secretKey; + + private CipherAlgorithm cipherAlgorithm; + + private String mimeType; + + @Relationship(type = Neo4jAnyTypeClass.ANY_TYPE_CLASS_PLAIN_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jAnyTypeClass anyTypeClass; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jImplementation validator; + + @Override + public AttrSchemaType getType() { + return type; + } + + @Override + public void setType(final AttrSchemaType type) { + this.type = type; + } + + @Override + public String getMandatoryCondition() { + return mandatoryCondition; + } + + @Override + public void setMandatoryCondition(final String condition) { + this.mandatoryCondition = condition; + } + + @Override + public boolean isMultivalue() { + return multivalue; + } + + @Override + public void setMultivalue(final boolean multivalue) { + this.multivalue = multivalue; + } + + @Override + public boolean isUniqueConstraint() { + return uniqueConstraint; + } + + @Override + public void setUniqueConstraint(final boolean uniquevalue) { + this.uniqueConstraint = uniquevalue; + } + + @Override + public boolean isReadonly() { + return readonly; + } + + @Override + public void setReadonly(final boolean readonly) { + this.readonly = readonly; + } + + @Override + public String getEnumerationValues() { + return enumerationValues; + } + + @Override + public void setEnumerationValues(final String enumerationValues) { + this.enumerationValues = enumerationValues; + } + + @Override + public String getEnumerationKeys() { + return enumerationKeys; + } + + @Override + public void setEnumerationKeys(final String enumerationKeys) { + this.enumerationKeys = enumerationKeys; + } + + @Override + public String getConversionPattern() { + return conversionPattern; + } + + @Override + public void setConversionPattern(final String conversionPattern) { + this.conversionPattern = conversionPattern; + } + + @Override + public String getSecretKey() { + return secretKey; + } + + @Override + public void setSecretKey(final String secretKey) { + this.secretKey = secretKey; + } + + @Override + public CipherAlgorithm getCipherAlgorithm() { + return cipherAlgorithm; + } + + @Override + public void setCipherAlgorithm(final CipherAlgorithm cipherAlgorithm) { + this.cipherAlgorithm = cipherAlgorithm; + } + + @Override + public String getMimeType() { + return mimeType; + } + + @Override + public void setMimeType(final String mimeType) { + this.mimeType = mimeType; + } + + @Override + public AnyTypeClass getAnyTypeClass() { + return anyTypeClass; + } + + @Override + public void setAnyTypeClass(final AnyTypeClass anyTypeClass) { + checkType(anyTypeClass, Neo4jAnyTypeClass.class); + this.anyTypeClass = (Neo4jAnyTypeClass) anyTypeClass; + } + + @Override + public Implementation getValidator() { + return validator; + } + + @Override + public void setValidator(final Implementation validator) { + checkType(validator, Neo4jImplementation.class); + checkImplementationType(validator, IdRepoImplementationType.VALIDATOR); + this.validator = (Neo4jImplementation) validator; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPrivilege.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPrivilege.java new file mode 100644 index 0000000000..4a3d0f9568 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPrivilege.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.neo4j.entity; + +import org.apache.syncope.core.persistence.api.entity.Application; +import org.apache.syncope.core.persistence.api.entity.Privilege; +import org.apache.syncope.core.persistence.common.validation.PrivilegeCheck; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jPrivilege.NODE) +@PrivilegeCheck +public class Neo4jPrivilege extends AbstractProvidedKeyNode implements Privilege { + + private static final long serialVersionUID = -6479069294944858456L; + + public static final String NODE = "Privilege"; + + @Relationship(type = Neo4jApplication.APPLICATION_PRIVILEGE_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jApplication application; + + private String description; + + private String spec; + + @Override + public Application getApplication() { + return application; + } + + @Override + public void setApplication(final Application application) { + checkType(application, Neo4jApplication.class); + this.application = (Neo4jApplication) application; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(final String description) { + this.description = description; + } + + @Override + public String getSpec() { + return spec; + } + + @Override + public void setSpec(final String spec) { + this.spec = spec; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRealm.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRealm.java new file mode 100644 index 0000000000..df16b314f9 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRealm.java @@ -0,0 +1,249 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.types.IdRepoImplementationType; +import org.apache.syncope.core.persistence.api.entity.AnyTemplateRealm; +import org.apache.syncope.core.persistence.api.entity.AnyType; +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.AccessPolicy; +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.PasswordPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; +import org.apache.syncope.core.persistence.common.validation.RealmCheck; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccessPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccountPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAttrReleasePolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAuthPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPasswordPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jTicketExpirationPolicy; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jRealm.NODE) +@RealmCheck +public class Neo4jRealm extends AbstractGeneratedKeyNode implements Realm { + + private static final long serialVersionUID = 1807304075247552949L; + + public static final String NODE = "Realm"; + + public static final String PARENT_REL = "PARENT"; + + public static final String REALM_PASSWORD_POLICY_REL = "REALM_PASSWORD_POLICY"; + + public static final String REALM_ACCOUNT_POLICY_REL = "REALM_ACCOUNT_POLICY"; + + public static final String REALM_AUTH_POLICY_REL = "REALM_AUTH_POLICY"; + + public static final String REALM_ACCESS_POLICY_REL = "REALM_ACCESS_POLICY"; + + public static final String REALM_ATTR_RELEASE_POLICY_REL = "REALM_ATTR_RELEASE_POLICY"; + + public static final String REALM_TICKET_EXPIRATION_POLICY_REL = "REALM_TICKET_EXPIRATION_POLICY"; + + public static final String REALM_RESOURCE_REL = "REALM_RESOURCE"; + + @Size(min = 1) + private String name; + + @Relationship(type = PARENT_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jRealm parent; + + @NotNull + private String fullPath; + + @Relationship(type = REALM_PASSWORD_POLICY_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jPasswordPolicy passwordPolicy; + + @Relationship(type = REALM_ACCOUNT_POLICY_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jAccountPolicy accountPolicy; + + @Relationship(type = REALM_AUTH_POLICY_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jAuthPolicy authPolicy; + + @Relationship(type = REALM_ACCESS_POLICY_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jAccessPolicy accessPolicy; + + @Relationship(type = REALM_ATTR_RELEASE_POLICY_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jAttrReleasePolicy attrReleasePolicy; + + @Relationship(type = REALM_TICKET_EXPIRATION_POLICY_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jTicketExpirationPolicy ticketExpirationPolicy; + + @Relationship(direction = Relationship.Direction.INCOMING) + private List actions = new ArrayList<>(); + + @Relationship(type = Neo4jAnyTemplateRealm.REALM_ANY_TEMPLATE_REL, direction = Relationship.Direction.INCOMING) + private List templates = new ArrayList<>(); + + @Relationship(type = REALM_RESOURCE_REL, direction = Relationship.Direction.INCOMING) + private List resources = new ArrayList<>(); + + @Override + public String getName() { + return name; + } + + @Override + public Realm getParent() { + return parent; + } + + @Override + public String getFullPath() { + return fullPath; + } + + @Override + public AccountPolicy getAccountPolicy() { + return accountPolicy == null && getParent() != null ? getParent().getAccountPolicy() : accountPolicy; + } + + @Override + public PasswordPolicy getPasswordPolicy() { + return passwordPolicy == null && getParent() != null ? getParent().getPasswordPolicy() : passwordPolicy; + } + + @Override + public void setName(final String name) { + this.name = name; + } + + @Override + public void setParent(final Realm parent) { + checkType(parent, Neo4jRealm.class); + this.parent = (Neo4jRealm) parent; + } + + public void setFullPath(final String fullPath) { + this.fullPath = fullPath; + } + + @Override + public void setAccountPolicy(final AccountPolicy accountPolicy) { + checkType(accountPolicy, Neo4jAccountPolicy.class); + this.accountPolicy = (Neo4jAccountPolicy) accountPolicy; + } + + @Override + public void setPasswordPolicy(final PasswordPolicy passwordPolicy) { + checkType(passwordPolicy, Neo4jPasswordPolicy.class); + this.passwordPolicy = (Neo4jPasswordPolicy) passwordPolicy; + } + + @Override + public AuthPolicy getAuthPolicy() { + return authPolicy; + } + + @Override + public void setAuthPolicy(final AuthPolicy authPolicy) { + checkType(authPolicy, Neo4jAuthPolicy.class); + this.authPolicy = (Neo4jAuthPolicy) authPolicy; + } + + @Override + public AccessPolicy getAccessPolicy() { + return accessPolicy; + } + + @Override + public void setAccessPolicy(final AccessPolicy accessPolicy) { + checkType(accessPolicy, Neo4jAccessPolicy.class); + this.accessPolicy = (Neo4jAccessPolicy) accessPolicy; + } + + @Override + public boolean add(final Implementation action) { + checkType(action, Neo4jImplementation.class); + checkImplementationType(action, IdRepoImplementationType.LOGIC_ACTIONS); + return actions.contains((Neo4jImplementation) action) || actions.add((Neo4jImplementation) action); + } + + @Override + public void setAttrReleasePolicy(final AttrReleasePolicy policy) { + checkType(policy, Neo4jAttrReleasePolicy.class); + this.attrReleasePolicy = (Neo4jAttrReleasePolicy) policy; + } + + @Override + public AttrReleasePolicy getAttrReleasePolicy() { + return this.attrReleasePolicy; + } + + @Override + public TicketExpirationPolicy getTicketExpirationPolicy() { + return this.ticketExpirationPolicy; + } + + @Override + public void setTicketExpirationPolicy(final TicketExpirationPolicy policy) { + checkType(policy, Neo4jTicketExpirationPolicy.class); + this.ticketExpirationPolicy = (Neo4jTicketExpirationPolicy) policy; + } + + @Override + public List getActions() { + return actions; + } + + @Override + public boolean add(final AnyTemplateRealm template) { + checkType(template, Neo4jAnyTemplateRealm.class); + return this.templates.add((Neo4jAnyTemplateRealm) template); + } + + @Override + public Optional getTemplate(final AnyType anyType) { + return templates.stream(). + filter(template -> anyType != null && anyType.equals(template.getAnyType())). + findFirst(); + } + + @Override + public List getTemplates() { + return templates; + } + + @Override + public boolean add(final ExternalResource resource) { + checkType(resource, Neo4jExternalResource.class); + return resources.contains((Neo4jExternalResource) resource) || resources.add((Neo4jExternalResource) resource); + } + + @Override + public List getResourceKeys() { + return getResources().stream().map(ExternalResource::getKey).toList(); + } + + @Override + public List getResources() { + return resources; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRelationshipType.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRelationshipType.java new file mode 100644 index 0000000000..7f18898fdb --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRelationshipType.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.core.persistence.neo4j.entity; + +import org.apache.syncope.core.persistence.api.entity.RelationshipType; +import org.apache.syncope.core.persistence.common.validation.RelationshipTypeCheck; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jRelationshipType.NODE) +@RelationshipTypeCheck +public class Neo4jRelationshipType extends AbstractProvidedKeyNode implements RelationshipType { + + private static final long serialVersionUID = -753673974614737065L; + + public static final String NODE = "RelationshipType"; + + private String description; + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(final String description) { + this.description = description; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRemediation.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRemediation.java new file mode 100644 index 0000000000..51181f2ea3 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRemediation.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.core.persistence.neo4j.entity; + +import jakarta.validation.constraints.NotNull; +import java.time.OffsetDateTime; +import org.apache.syncope.common.lib.request.AnyCR; +import org.apache.syncope.common.lib.request.AnyUR; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.core.persistence.api.entity.AnyType; +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.common.validation.RemediationCheck; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jPullTask; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jRemediation.NODE) +@RemediationCheck +public class Neo4jRemediation extends AbstractGeneratedKeyNode implements Remediation { + + private static final long serialVersionUID = -1612530286294448682L; + + public static final String NODE = "Remediation"; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jAnyType anyType; + + @NotNull + private ResourceOperation operation; + + @NotNull + private String payload; + + @NotNull + private String error; + + @NotNull + private OffsetDateTime instant; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jPullTask pullTask; + + @NotNull + private String remoteName; + + @Override + public AnyType getAnyType() { + return anyType; + } + + @Override + public void setAnyType(final AnyType anyType) { + checkType(anyType, Neo4jAnyType.class); + this.anyType = (Neo4jAnyType) anyType; + } + + @Override + public ResourceOperation getOperation() { + return operation; + } + + @Override + public void setOperation(final ResourceOperation operation) { + this.operation = operation; + } + + @Override + public C getPayloadAsCR(final Class reference) { + return POJOHelper.deserialize(this.payload, reference); + } + + @Override + public U getPayloadAsUR(final Class reference) { + return POJOHelper.deserialize(this.payload, reference); + } + + @Override + public String getPayloadAsKey() { + return this.payload; + } + + @Override + public void setPayload(final AnyCR anyCR) { + this.payload = POJOHelper.serialize(anyCR); + } + + @Override + public void setPayload(final AnyUR anyUR) { + this.payload = POJOHelper.serialize(anyUR); + } + + @Override + public void setPayload(final String key) { + this.payload = key; + } + + @Override + public String getError() { + return error; + } + + @Override + public void setError(final String error) { + this.error = error; + } + + @Override + public OffsetDateTime getInstant() { + return instant; + } + + @Override + public void setInstant(final OffsetDateTime instant) { + this.instant = instant; + } + + @Override + public PullTask getPullTask() { + return pullTask; + } + + @Override + public void setPullTask(final PullTask pullTask) { + checkType(pullTask, Neo4jPullTask.class); + this.pullTask = (Neo4jPullTask) pullTask; + } + + @Override + public String getRemoteName() { + return remoteName; + } + + @Override + public void setRemoteName(final String remoteName) { + this.remoteName = remoteName; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jReport.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jReport.java new file mode 100644 index 0000000000..c6359f32c8 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jReport.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.persistence.neo4j.entity; + +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; +import org.apache.syncope.common.lib.types.IdRepoImplementationType; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.Report; +import org.apache.syncope.core.persistence.api.entity.ReportExec; +import org.apache.syncope.core.persistence.common.validation.ReportCheck; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jReport.NODE) +@ReportCheck +public class Neo4jReport extends AbstractGeneratedKeyNode implements Report { + + private static final long serialVersionUID = -587652654964285834L; + + public static final String NODE = "Report"; + + public static final String REPORT_EXEC_REL = "REPORT_EXEC"; + + @NotNull + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jImplementation jobDelegate; + + @NotNull + private String name; + + @NotNull + private String mimeType; + + @NotNull + private String fileExt; + + private String cronExpression; + + @Relationship(type = REPORT_EXEC_REL, direction = Relationship.Direction.INCOMING) + private List executions = new ArrayList<>(); + + @NotNull + private Boolean active = true; + + @Override + public Implementation getJobDelegate() { + return jobDelegate; + } + + @Override + public void setJobDelegate(final Implementation jobDelegate) { + checkType(jobDelegate, Neo4jImplementation.class); + checkImplementationType(jobDelegate, IdRepoImplementationType.REPORT_DELEGATE); + this.jobDelegate = (Neo4jImplementation) jobDelegate; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(final String name) { + this.name = name; + } + + @Override + public String getMimeType() { + return mimeType; + } + + @Override + public void setMimeType(final String mimeType) { + this.mimeType = mimeType; + } + + @Override + public String getFileExt() { + return fileExt; + } + + @Override + public void setFileExt(final String fileExt) { + this.fileExt = fileExt; + } + + @Override + public String getCronExpression() { + return cronExpression; + } + + @Override + public void setCronExpression(final String cronExpression) { + this.cronExpression = cronExpression; + } + + @Override + public boolean isActive() { + return active; + } + + @Override + public void setActive(final boolean active) { + this.active = active; + } + + @Override + public boolean add(final ReportExec exec) { + checkType(exec, Neo4jReportExec.class); + return exec != null && !executions.contains((Neo4jReportExec) exec) && executions.add((Neo4jReportExec) exec); + } + + @Override + public List getExecs() { + return executions; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jReportExec.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jReportExec.java new file mode 100644 index 0000000000..d7d30e3ca7 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jReportExec.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity; + +import jakarta.validation.constraints.NotNull; +import java.util.Optional; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.syncope.core.persistence.api.entity.Report; +import org.apache.syncope.core.persistence.api.entity.ReportExec; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jReportExec.NODE) +public class Neo4jReportExec extends AbstractExec implements ReportExec { + + private static final long serialVersionUID = -6178274296037547769L; + + public static final String NODE = "ReportExec"; + + /** + * The referred report. + */ + @NotNull + @Relationship(type = Neo4jReport.REPORT_EXEC_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jReport report; + + /** + * Report execution result, stored as an XML stream. + */ + private Byte[] execResult; + + @Override + public Report getReport() { + return report; + } + + @Override + public void setReport(final Report report) { + checkType(report, Neo4jReport.class); + this.report = (Neo4jReport) report; + } + + @Override + public byte[] getExecResult() { + return Optional.ofNullable(execResult).map(ArrayUtils::toPrimitive).orElse(null); + } + + @Override + public void setExecResult(final byte[] execResult) { + this.execResult = Optional.ofNullable(execResult).map(ArrayUtils::toObject).orElse(null); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRole.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRole.java new file mode 100644 index 0000000000..0cc6848096 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRole.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity; + +import com.fasterxml.jackson.core.type.TypeReference; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.apache.syncope.core.persistence.api.entity.Application; +import org.apache.syncope.core.persistence.api.entity.DynRealm; +import org.apache.syncope.core.persistence.api.entity.Privilege; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.Role; +import org.apache.syncope.core.persistence.common.validation.RoleCheck; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.annotation.Transient; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.PostLoad; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jRole.NODE) +@RoleCheck +public class Neo4jRole extends AbstractProvidedKeyNode implements Role { + + private static final long serialVersionUID = -7657701119422588832L; + + public static final String NODE = "SyncopeRole"; + + public static final String ROLE_REALM_REL = "ROLE_REALM"; + + public static final String ROLE_PRIVILEGE_REL = "ROLE_PRIVILEGE"; + + protected static final TypeReference> TYPEREF = new TypeReference>() { + }; + + private String entitlements; + + @Transient + private Set entitlementsSet = new HashSet<>(); + + private String dynMembershipCond; + + private String anyLayout; + + @Relationship(type = ROLE_REALM_REL, direction = Relationship.Direction.OUTGOING) + private List realms = new ArrayList<>(); + + @Relationship(direction = Relationship.Direction.INCOMING) + private List dynRealms = new ArrayList<>(); + + @Relationship(type = ROLE_PRIVILEGE_REL, direction = Relationship.Direction.OUTGOING) + private Set privileges = new HashSet<>(); + + @Override + public Set getEntitlements() { + return entitlementsSet; + } + + @Override + public String getDynMembershipCond() { + return dynMembershipCond; + } + + @Override + public void setDynMembershipCond(final String dynMembershipCond) { + this.dynMembershipCond = dynMembershipCond; + } + + @Override + public boolean add(final Realm realm) { + checkType(realm, Neo4jRealm.class); + return realms.contains((Neo4jRealm) realm) || realms.add((Neo4jRealm) realm); + } + + @Override + public List getRealms() { + return realms; + } + + @Override + public boolean add(final DynRealm dynamicRealm) { + checkType(dynamicRealm, Neo4jDynRealm.class); + return dynRealms.contains((Neo4jDynRealm) dynamicRealm) || dynRealms.add((Neo4jDynRealm) dynamicRealm); + } + + @Override + public List getDynRealms() { + return dynRealms; + } + + @Override + public String getAnyLayout() { + return anyLayout; + } + + @Override + public void setAnyLayout(final String anyLayout) { + this.anyLayout = anyLayout; + } + + @Override + public boolean add(final Privilege privilege) { + checkType(privilege, Neo4jPrivilege.class); + return privileges.add((Neo4jPrivilege) privilege); + } + + @Override + public Set getPrivileges(final Application application) { + return privileges.stream(). + filter(privilege -> privilege.getApplication().equals(application)). + collect(Collectors.toSet()); + } + + @Override + public Set getPrivileges() { + return privileges; + } + + protected void json2list(final boolean clearFirst) { + if (clearFirst) { + getEntitlements().clear(); + } + if (entitlements != null) { + getEntitlements().addAll(POJOHelper.deserialize(entitlements, TYPEREF)); + } + } + + @PostLoad + public void postLoad() { + json2list(false); + } + + public void postSave() { + json2list(true); + } + + public void list2json() { + entitlements = POJOHelper.serialize(getEntitlements()); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jSRARoute.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jSRARoute.java new file mode 100644 index 0000000000..5267618b0b --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jSRARoute.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.persistence.neo4j.entity; + +import jakarta.validation.constraints.NotNull; +import java.net.URI; +import java.util.List; +import java.util.Optional; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.syncope.common.lib.types.SRARouteFilter; +import org.apache.syncope.common.lib.types.SRARoutePredicate; +import org.apache.syncope.common.lib.types.SRARouteType; +import org.apache.syncope.core.persistence.api.entity.SRARoute; +import org.apache.syncope.core.persistence.common.validation.SRARouteCheck; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jSRARoute.NODE) +@SRARouteCheck +public class Neo4jSRARoute extends AbstractGeneratedKeyNode implements SRARoute { + + private static final long serialVersionUID = -8718852361106840530L; + + public static final String NODE = "SRARoute"; + + @NotNull + private String name; + + @NotNull + private String target; + + private String error; + + @NotNull + private SRARouteType routeType; + + @NotNull + private Boolean logout = false; + + private String postLogout; + + @NotNull + private Boolean csrf = true; + + private Integer routeOrder; + + private String predicates; + + private String filters; + + @Override + public String getName() { + return name; + } + + @Override + public void setName(final String name) { + this.name = name; + } + + @Override + public URI getTarget() { + return Optional.ofNullable(target).map(URI::create).orElse(null); + } + + @Override + public void setTarget(final URI target) { + this.target = Optional.ofNullable(target).map(URI::toASCIIString).orElse(null); + } + + @Override + public URI getError() { + return Optional.ofNullable(error).map(URI::create).orElse(null); + } + + @Override + public void setError(final URI error) { + this.error = Optional.ofNullable(error).map(URI::toASCIIString).orElse(null); + } + + @Override + public SRARouteType getType() { + return routeType; + } + + @Override + public void setType(final SRARouteType type) { + this.routeType = type; + } + + @Override + public boolean isLogout() { + return BooleanUtils.isNotFalse(logout); + } + + @Override + public void setLogout(final boolean logout) { + this.logout = logout; + } + + @Override + public URI getPostLogout() { + return Optional.ofNullable(postLogout).map(URI::create).orElse(null); + } + + @Override + public void setPostLogout(final URI postLogout) { + this.postLogout = Optional.ofNullable(postLogout).map(URI::toASCIIString).orElse(null); + } + + @Override + public boolean isCsrf() { + return csrf; + } + + @Override + public void setCsrf(final boolean csrf) { + this.csrf = csrf; + } + + @Override + public int getOrder() { + return Optional.ofNullable(routeOrder).orElse(0); + } + + @Override + public void setOrder(final int order) { + this.routeOrder = order; + } + + @Override + public List getFilters() { + return Optional.ofNullable(filters). + map(f -> List.of(POJOHelper.deserialize(f, SRARouteFilter[].class))). + orElse(List.of()); + } + + @Override + public void setFilters(final List filters) { + this.filters = POJOHelper.serialize(filters); + } + + @Override + public List getPredicates() { + return Optional.ofNullable(predicates). + map(f -> List.of(POJOHelper.deserialize(f, SRARoutePredicate[].class))). + orElse(List.of()); + } + + @Override + public void setPredicates(final List predicates) { + this.predicates = POJOHelper.serialize(predicates); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jSchema.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jSchema.java new file mode 100644 index 0000000000..649ee1b398 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jSchema.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.core.persistence.neo4j.entity; + +import com.fasterxml.jackson.core.type.TypeReference; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.entity.Schema; +import org.apache.syncope.core.persistence.common.validation.SchemaKeyCheck; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.annotation.Transient; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.PostLoad; + +@Node(Neo4jSchema.NODE) +@SchemaKeyCheck +public abstract class Neo4jSchema extends AbstractProvidedKeyNode implements Schema { + + private static final long serialVersionUID = -90123646849845441L; + + public static final String NODE = "Schema"; + + protected static final TypeReference> LABEL_TYPEREF = + new TypeReference>() { + }; + + private String labels; + + @Transient + private Map labelMap = new HashMap<>(); + + @Override + public Optional getLabel(final Locale locale) { + return Optional.ofNullable(labelMap.get(locale)); + } + + @Override + public Map getLabels() { + return labelMap; + } + + protected void json2map(final boolean clearFirst) { + if (clearFirst) { + getLabels().clear(); + } + if (labels != null) { + getLabels().putAll(POJOHelper.deserialize(labels, LABEL_TYPEREF)); + } + } + + @PostLoad + public void postLoad() { + json2map(false); + } + + public void postSave() { + json2map(true); + } + + public void map2json() { + labels = POJOHelper.serialize(getLabels()); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jVirSchema.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jVirSchema.java new file mode 100644 index 0000000000..671e560ea2 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jVirSchema.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.persistence.neo4j.entity; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.common.lib.types.AttrSchemaType; +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.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.VirSchema; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jVirSchema.NODE) +public class Neo4jVirSchema extends Neo4jSchema implements VirSchema { + + private static final long serialVersionUID = -4136127093718028088L; + + public static final String NODE = "VirSchema"; + + public static final String VIRSCHEMA_RESOURCE_REL = "VIRSCHEMA_RESOURCE"; + + public static final String VIRSCHEMA_ANYTYPE_REL = "VIRSCHEMA_ANYTYPE"; + + @NotNull + private String extAttrName; + + private Boolean readonly = false; + + @Relationship(type = Neo4jAnyTypeClass.ANY_TYPE_CLASS_VIR_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jAnyTypeClass anyTypeClass; + + @NotNull + @Relationship(type = VIRSCHEMA_RESOURCE_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jExternalResource resource; + + @NotNull + @Relationship(type = VIRSCHEMA_ANYTYPE_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jAnyType anyType; + + @Override + public String getExtAttrName() { + return extAttrName; + } + + @Override + public void setExtAttrName(final String extAttrName) { + this.extAttrName = extAttrName; + } + + @Override + public boolean isReadonly() { + return readonly; + } + + @Override + public void setReadonly(final boolean readonly) { + this.readonly = readonly; + } + + @Override + public AttrSchemaType getType() { + return AttrSchemaType.String; + } + + @Override + public String getMandatoryCondition() { + return Boolean.FALSE.toString().toLowerCase(); + } + + @Override + public boolean isMultivalue() { + return true; + } + + @Override + public boolean isUniqueConstraint() { + return false; + } + + @Override + public AnyTypeClass getAnyTypeClass() { + return anyTypeClass; + } + + @Override + public void setAnyTypeClass(final AnyTypeClass anyTypeClass) { + checkType(anyTypeClass, Neo4jAnyTypeClass.class); + this.anyTypeClass = (Neo4jAnyTypeClass) anyTypeClass; + } + + @Override + public ExternalResource getResource() { + return resource; + } + + @Override + public void setResource(final ExternalResource resource) { + checkType(resource, Neo4jExternalResource.class); + this.resource = (Neo4jExternalResource) resource; + } + + @Override + public AnyType getAnyType() { + return anyType; + } + + @Override + public void setAnyType(final AnyType anyType) { + checkType(anyType, Neo4jAnyType.class); + this.anyType = (Neo4jAnyType) anyType; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/AbstractClientApp.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/AbstractClientApp.java new file mode 100644 index 0000000000..02ae059356 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/AbstractClientApp.java @@ -0,0 +1,245 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity.am; + +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.Attr; +import org.apache.syncope.common.lib.clientapps.UsernameAttributeProviderConf; +import org.apache.syncope.common.lib.types.LogoutType; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.am.ClientApp; +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.persistence.neo4j.entity.AbstractGeneratedKeyNode; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccessPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAttrReleasePolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAuthPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jTicketExpirationPolicy; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.neo4j.core.schema.Relationship; + +public abstract class AbstractClientApp extends AbstractGeneratedKeyNode implements ClientApp { + + private static final long serialVersionUID = 7422422526695279794L; + + protected static final TypeReference> ATTR_TYPEREF = new TypeReference>() { + }; + + @NotNull + private String name; + + @NotNull + private Long clientAppId; + + private String description; + + private String logo; + + private String usernameAttributeProviderConf; + + private String theme; + + private String informationUrl; + + private String privacyUrl; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jRealm realm; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jAuthPolicy authPolicy; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jAccessPolicy accessPolicy; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jAttrReleasePolicy attrReleasePolicy; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jTicketExpirationPolicy ticketExpirationPolicy; + + private String properties; + + private LogoutType logoutType; + + @Override + public Long getClientAppId() { + return clientAppId; + } + + @Override + public void setClientAppId(final Long clientAppId) { + this.clientAppId = clientAppId; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(final String name) { + this.name = name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(final String description) { + this.description = description; + } + + @Override + public String getLogo() { + return logo; + } + + @Override + public void setLogo(final String logo) { + this.logo = logo; + } + + @Override + public String getTheme() { + return theme; + } + + @Override + public void setTheme(final String theme) { + this.theme = theme; + } + + @Override + public String getInformationUrl() { + return informationUrl; + } + + @Override + public void setInformationUrl(final String informationUrl) { + this.informationUrl = informationUrl; + } + + @Override + public String getPrivacyUrl() { + return privacyUrl; + } + + @Override + public void setPrivacyUrl(final String privacyUrl) { + this.privacyUrl = privacyUrl; + } + + @Override + public UsernameAttributeProviderConf getUsernameAttributeProviderConf() { + return Optional.ofNullable(usernameAttributeProviderConf). + map(conf -> POJOHelper.deserialize(conf, UsernameAttributeProviderConf.class)).orElse(null); + } + + @Override + public void setUsernameAttributeProviderConf(final UsernameAttributeProviderConf conf) { + this.usernameAttributeProviderConf = conf == null ? null : POJOHelper.serialize(conf); + } + + @Override + public Neo4jAuthPolicy getAuthPolicy() { + return authPolicy; + } + + @Override + public void setAuthPolicy(final AuthPolicy authPolicy) { + checkType(authPolicy, Neo4jAuthPolicy.class); + this.authPolicy = (Neo4jAuthPolicy) authPolicy; + } + + @Override + public Neo4jAccessPolicy getAccessPolicy() { + return accessPolicy; + } + + @Override + public void setAccessPolicy(final AccessPolicy accessPolicy) { + checkType(accessPolicy, Neo4jAccessPolicy.class); + this.accessPolicy = (Neo4jAccessPolicy) accessPolicy; + } + + @Override + public AttrReleasePolicy getAttrReleasePolicy() { + return this.attrReleasePolicy; + } + + @Override + public void setAttrReleasePolicy(final AttrReleasePolicy policy) { + checkType(policy, Neo4jAttrReleasePolicy.class); + this.attrReleasePolicy = (Neo4jAttrReleasePolicy) policy; + } + + @Override + public TicketExpirationPolicy getTicketExpirationPolicy() { + return this.ticketExpirationPolicy; + } + + @Override + public void setTicketExpirationPolicy(final TicketExpirationPolicy policy) { + checkType(policy, Neo4jTicketExpirationPolicy.class); + this.ticketExpirationPolicy = (Neo4jTicketExpirationPolicy) policy; + } + + @Override + public Realm getRealm() { + return realm; + } + + @Override + public void setRealm(final Realm realm) { + checkType(realm, Neo4jRealm.class); + this.realm = (Neo4jRealm) realm; + } + + @Override + public List getProperties() { + return properties == null + ? new ArrayList<>(0) + : POJOHelper.deserialize(properties, ATTR_TYPEREF); + } + + @Override + public void setProperties(final List properties) { + this.properties = POJOHelper.serialize(properties); + } + + @Override + public LogoutType getLogoutType() { + return this.logoutType; + } + + @Override + public void setLogoutType(final LogoutType logoutType) { + this.logoutType = logoutType; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAttrRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAttrRepo.java new file mode 100644 index 0000000000..bd8eef827c --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAttrRepo.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.core.persistence.neo4j.entity.am; + +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.attr.AttrRepoConf; +import org.apache.syncope.common.lib.to.Item; +import org.apache.syncope.common.lib.types.AttrRepoState; +import org.apache.syncope.core.persistence.api.entity.am.AttrRepo; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractProvidedKeyNode; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.annotation.Transient; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.PostLoad; + +@Node(Neo4jAttrRepo.NODE) +public class Neo4jAttrRepo extends AbstractProvidedKeyNode implements AttrRepo { + + private static final long serialVersionUID = 7337970107878689617L; + + public static final String NODE = "AttrRepo"; + + protected static final TypeReference> TYPEREF = new TypeReference>() { + }; + + private String description; + + @NotNull + private AttrRepoState attrRepoState; + + @NotNull + private Integer attrRepoOrder = 0; + + private String items; + + @Transient + private final List itemList = new ArrayList<>(); + + private String jsonConf; + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(final String description) { + this.description = description; + } + + @Override + public AttrRepoState getState() { + return attrRepoState; + } + + @Override + public void setState(final AttrRepoState state) { + this.attrRepoState = state; + } + + @Override + public int getOrder() { + return Optional.ofNullable(attrRepoOrder).orElse(0); + } + + @Override + public void setOrder(final int order) { + this.attrRepoOrder = order; + } + + @Override + public List getItems() { + return itemList; + } + + @Override + public AttrRepoConf getConf() { + AttrRepoConf conf = null; + if (!StringUtils.isBlank(jsonConf)) { + conf = POJOHelper.deserialize(jsonConf, AttrRepoConf.class); + } + + return conf; + } + + @Override + public void setConf(final AttrRepoConf conf) { + jsonConf = POJOHelper.serialize(conf); + } + + protected void json2list(final boolean clearFirst) { + if (clearFirst) { + getItems().clear(); + } + if (items != null) { + getItems().addAll(POJOHelper.deserialize(items, TYPEREF)); + } + } + + @PostLoad + public void postLoad() { + json2list(false); + } + + public void postSave() { + json2list(true); + } + + public void list2json() { + items = POJOHelper.serialize(getItems()); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAuthModule.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAuthModule.java new file mode 100644 index 0000000000..c01c9bac5e --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAuthModule.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.core.persistence.neo4j.entity.am; + +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.auth.AuthModuleConf; +import org.apache.syncope.common.lib.to.Item; +import org.apache.syncope.common.lib.types.AuthModuleState; +import org.apache.syncope.core.persistence.api.entity.am.AuthModule; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractProvidedKeyNode; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.annotation.Transient; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.PostLoad; + +@Node(Neo4jAuthModule.NODE) +public class Neo4jAuthModule extends AbstractProvidedKeyNode implements AuthModule { + + private static final long serialVersionUID = 5681033638234853077L; + + public static final String NODE = "AuthModule"; + + protected static final TypeReference> TYPEREF = new TypeReference>() { + }; + + private String description; + + @NotNull + private AuthModuleState authModuleState; + + @NotNull + private Integer authModuleOrder = 0; + + private String items; + + @Transient + private final List itemList = new ArrayList<>(); + + private String jsonConf; + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(final String description) { + this.description = description; + } + + @Override + public AuthModuleState getState() { + return authModuleState; + } + + @Override + public void setState(final AuthModuleState state) { + this.authModuleState = state; + } + + @Override + public int getOrder() { + return Optional.ofNullable(authModuleOrder).orElse(0); + } + + @Override + public void setOrder(final int order) { + this.authModuleOrder = order; + } + + @Override + public List getItems() { + return itemList; + } + + @Override + public AuthModuleConf getConf() { + AuthModuleConf conf = null; + if (!StringUtils.isBlank(jsonConf)) { + conf = POJOHelper.deserialize(jsonConf, AuthModuleConf.class); + } + + return conf; + } + + @Override + public void setConf(final AuthModuleConf conf) { + jsonConf = POJOHelper.serialize(conf); + } + + protected void json2list(final boolean clearFirst) { + if (clearFirst) { + getItems().clear(); + } + if (items != null) { + getItems().addAll(POJOHelper.deserialize(items, TYPEREF)); + } + } + + @PostLoad + public void postLoad() { + json2list(false); + } + + public void postSave() { + json2list(true); + } + + public void list2json() { + items = POJOHelper.serialize(getItems()); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAuthProfile.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAuthProfile.java new file mode 100644 index 0000000000..5927b1b82b --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jAuthProfile.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.persistence.neo4j.entity.am; + +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +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.WebAuthnDeviceCredential; +import org.apache.syncope.core.persistence.api.entity.am.AuthProfile; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractGeneratedKeyNode; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jAuthProfile.NODE) +public class Neo4jAuthProfile extends AbstractGeneratedKeyNode implements AuthProfile { + + private static final long serialVersionUID = 57352617217394093L; + + public static final String NODE = "AuthProfile"; + + protected static final TypeReference> GOOGLE_MFA_TOKENS_TYPEREF = + new TypeReference>() { + }; + + protected static final TypeReference> GOOGLE_MFA_ACCOUNTS_TYPEREF = + new TypeReference>() { + }; + + protected static final TypeReference> MFA_TRUSTED_DEVICE_TYPEREF = + new TypeReference>() { + }; + + protected static final TypeReference> IMPERSONATION_TYPEREF = + new TypeReference>() { + }; + + protected static final TypeReference> WEBAUTHN_TYPEREF = + new TypeReference>() { + }; + + @NotNull + private String owner; + + private String impersonationAccounts; + + private String googleMfaAuthAccounts; + + private String googleMfaAuthTokens; + + private String mfaTrustedDevices; + + private String webAuthnDeviceCredentials; + + @Override + public String getOwner() { + return owner; + } + + @Override + public void setOwner(final String owner) { + this.owner = owner; + } + + @Override + public List getGoogleMfaAuthTokens() { + return Optional.ofNullable(googleMfaAuthTokens). + map(v -> POJOHelper.deserialize(v, GOOGLE_MFA_TOKENS_TYPEREF)).orElseGet(() -> new ArrayList<>(0)); + } + + @Override + public void setGoogleMfaAuthTokens(final List tokens) { + googleMfaAuthTokens = POJOHelper.serialize(tokens); + } + + @Override + public List getGoogleMfaAuthAccounts() { + return Optional.ofNullable(googleMfaAuthAccounts). + map(v -> POJOHelper.deserialize(v, GOOGLE_MFA_ACCOUNTS_TYPEREF)).orElseGet(() -> new ArrayList<>(0)); + } + + @Override + public void setGoogleMfaAuthAccounts(final List accounts) { + googleMfaAuthAccounts = POJOHelper.serialize(accounts); + } + + @Override + public List getMfaTrustedDevices() { + return Optional.ofNullable(mfaTrustedDevices). + map(v -> POJOHelper.deserialize(v, MFA_TRUSTED_DEVICE_TYPEREF)).orElseGet(() -> new ArrayList<>(0)); + } + + @Override + public void setMfaTrustedDevices(final List devices) { + mfaTrustedDevices = POJOHelper.serialize(devices); + } + + @Override + public List getImpersonationAccounts() { + return Optional.ofNullable(impersonationAccounts). + map(v -> POJOHelper.deserialize(v, IMPERSONATION_TYPEREF)).orElseGet(() -> new ArrayList<>(0)); + } + + @Override + public void setImpersonationAccounts(final List accounts) { + impersonationAccounts = POJOHelper.serialize(accounts); + } + + @Override + public List getWebAuthnDeviceCredentials() { + return Optional.ofNullable(webAuthnDeviceCredentials). + map(v -> POJOHelper.deserialize(v, WEBAUTHN_TYPEREF)).orElseGet(() -> new ArrayList<>(0)); + } + + @Override + public void setWebAuthnDeviceCredentials(final List credentials) { + webAuthnDeviceCredentials = POJOHelper.serialize(credentials); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jCASSPClientApp.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jCASSPClientApp.java new file mode 100644 index 0000000000..7bb19fb1da --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jCASSPClientApp.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.core.persistence.neo4j.entity.am; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.am.CASSPClientApp; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jCASSPClientApp.NODE) +public class Neo4jCASSPClientApp extends AbstractClientApp implements CASSPClientApp { + + public static final String NODE = "CASSPClientApp"; + + private static final long serialVersionUID = 6422422526695279794L; + + @NotNull + private String serviceId; + + @Override + public String getServiceId() { + return serviceId; + } + + @Override + public void setServiceId(final String serviceId) { + this.serviceId = serviceId; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCJWKS.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCJWKS.java new file mode 100644 index 0000000000..7224ad9fb7 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCJWKS.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.neo4j.entity.am; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.am.OIDCJWKS; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractGeneratedKeyNode; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jOIDCJWKS.NODE) +public class Neo4jOIDCJWKS extends AbstractGeneratedKeyNode implements OIDCJWKS { + + public static final String NODE = "OIDCJWKS"; + + private static final long serialVersionUID = 47352617217394093L; + + @NotNull + private String json; + + @Override + public String getJson() { + return this.json; + } + + @Override + public void setJson(final String json) { + this.json = json; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java new file mode 100644 index 0000000000..677d2d8f24 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jOIDCRPClientApp.java @@ -0,0 +1,267 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity.am; + +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.validation.constraints.NotNull; +import java.util.HashSet; +import java.util.Set; +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.core.persistence.api.entity.am.OIDCRPClientApp; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.annotation.Transient; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.PostLoad; + +@Node(Neo4jOIDCRPClientApp.NODE) +public class Neo4jOIDCRPClientApp extends AbstractClientApp implements OIDCRPClientApp { + + private static final long serialVersionUID = 7422422526695279794L; + + public static final String NODE = "OIDCRPClientApp"; + + protected static final TypeReference> STRING_TYPEREF = new TypeReference>() { + }; + + protected static final TypeReference> GRANT_TYPE_TYPEREF = + new TypeReference>() { + }; + + protected static final TypeReference> RESPONSE_TYPE_TYPEREF = + new TypeReference>() { + }; + + protected static final TypeReference> SCOPE_TYPEREF = + new TypeReference>() { + }; + + @NotNull + private String clientId; + + private String clientSecret; + + private boolean signIdToken; + + private boolean jwtAccessToken; + + private boolean bypassApprovalPrompt = true; + + private boolean generateRefreshToken = true; + + private OIDCSubjectType subjectType; + + private String redirectUris; + + @Transient + private Set redirectUrisSet = new HashSet<>(); + + private String supportedGrantTypes; + + @Transient + private Set supportedGrantTypesSet = new HashSet<>(); + + private String supportedResponseTypes; + + @Transient + private Set supportedResponseTypesSet = new HashSet<>(); + + private String scopes; + + @Transient + private Set scopesSet = new HashSet<>(); + + private String jwks; + + private String jwksUri; + + private OIDCClientAuthenticationMethod tokenEndpointAuthenticationMethod; + + private String logoutUri; + + @Override + public Set getRedirectUris() { + return redirectUrisSet; + } + + @Override + public String getClientId() { + return clientId; + } + + @Override + public void setClientId(final String clientId) { + this.clientId = clientId; + } + + @Override + public String getClientSecret() { + return clientSecret; + } + + @Override + public void setClientSecret(final String clientSecret) { + this.clientSecret = clientSecret; + } + + @Override + public boolean isSignIdToken() { + return signIdToken; + } + + @Override + public void setSignIdToken(final boolean signIdToken) { + this.signIdToken = signIdToken; + } + + @Override + public boolean isJwtAccessToken() { + return jwtAccessToken; + } + + @Override + public void setJwtAccessToken(final boolean jwtAccessToken) { + this.jwtAccessToken = jwtAccessToken; + } + + @Override + public boolean isBypassApprovalPrompt() { + return bypassApprovalPrompt; + } + + @Override + public void setBypassApprovalPrompt(final boolean bypassApprovalPrompt) { + this.bypassApprovalPrompt = bypassApprovalPrompt; + } + + @Override + public boolean isGenerateRefreshToken() { + return generateRefreshToken; + } + + @Override + public void setGenerateRefreshToken(final boolean generateRefreshToken) { + this.generateRefreshToken = generateRefreshToken; + } + + @Override + public OIDCSubjectType getSubjectType() { + return subjectType; + } + + @Override + public void setSubjectType(final OIDCSubjectType subjectType) { + this.subjectType = subjectType; + } + + @Override + public Set getSupportedGrantTypes() { + return supportedGrantTypesSet; + } + + @Override + public Set getSupportedResponseTypes() { + return supportedResponseTypesSet; + } + + @Override + public Set getScopes() { + return scopesSet; + } + + @Override + public String getJwks() { + return jwks; + } + + @Override + public void setJwks(final String jwks) { + this.jwks = jwks; + } + + @Override + public String getJwksUri() { + return jwksUri; + } + + @Override + public void setJwksUri(final String jwksUri) { + this.jwksUri = jwksUri; + } + + @Override + public OIDCClientAuthenticationMethod getTokenEndpointAuthenticationMethod() { + return tokenEndpointAuthenticationMethod; + } + + @Override + public void setTokenEndpointAuthenticationMethod( + final OIDCClientAuthenticationMethod tokenEndpointAuthenticationMethod) { + + this.tokenEndpointAuthenticationMethod = tokenEndpointAuthenticationMethod; + } + + @Override + public String getLogoutUri() { + return logoutUri; + } + + @Override + public void setLogoutUri(final String logoutUri) { + this.logoutUri = logoutUri; + } + + protected void json2list(final boolean clearFirst) { + if (clearFirst) { + getRedirectUris().clear(); + getSupportedGrantTypes().clear(); + getSupportedResponseTypes().clear(); + } + if (redirectUris != null) { + getRedirectUris().addAll(POJOHelper.deserialize(redirectUris, STRING_TYPEREF)); + } + if (supportedGrantTypes != null) { + getSupportedGrantTypes().addAll(POJOHelper.deserialize(supportedGrantTypes, GRANT_TYPE_TYPEREF)); + } + if (supportedResponseTypes != null) { + getSupportedResponseTypes().addAll(POJOHelper.deserialize(supportedResponseTypes, RESPONSE_TYPE_TYPEREF)); + } + if (scopes != null) { + getScopes().addAll(POJOHelper.deserialize(scopes, SCOPE_TYPEREF)); + } + } + + @PostLoad + public void postLoad() { + json2list(false); + } + + public void postSave() { + json2list(true); + } + + public void list2json() { + redirectUris = POJOHelper.serialize(getRedirectUris()); + supportedGrantTypes = POJOHelper.serialize(getSupportedGrantTypes()); + supportedResponseTypes = POJOHelper.serialize(getSupportedResponseTypes()); + scopes = POJOHelper.serialize(getScopes()); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jSAML2IdPEntity.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jSAML2IdPEntity.java new file mode 100644 index 0000000000..0938799ce6 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jSAML2IdPEntity.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.persistence.neo4j.entity.am; + +import jakarta.validation.constraints.NotNull; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.syncope.core.persistence.api.entity.am.SAML2IdPEntity; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractProvidedKeyNode; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jSAML2IdPEntity.NODE) +public class Neo4jSAML2IdPEntity extends AbstractProvidedKeyNode implements SAML2IdPEntity { + + public static final String NODE = "SAML2IdPEntity"; + + private static final long serialVersionUID = 57352617217394093L; + + @NotNull + private byte[] metadata; + + private byte[] signingCertificate; + + private byte[] signingKey; + + private byte[] encryptionCertificate; + + private byte[] encryptionKey; + + @Override + public byte[] getMetadata() { + return metadata; + } + + @Override + public void setMetadata(final byte[] metadata) { + this.metadata = ArrayUtils.clone(metadata); + } + + @Override + public byte[] getSigningCertificate() { + return signingCertificate; + } + + @Override + public void setSigningCertificate(final byte[] signingCertificate) { + this.signingCertificate = ArrayUtils.clone(signingCertificate); + } + + @Override + public byte[] getSigningKey() { + return signingKey; + } + + @Override + public void setSigningKey(final byte[] signingKey) { + this.signingKey = ArrayUtils.clone(signingKey); + } + + @Override + public byte[] getEncryptionCertificate() { + return encryptionCertificate; + } + + @Override + public void setEncryptionCertificate(final byte[] encryptionCertificate) { + this.encryptionCertificate = ArrayUtils.clone(encryptionCertificate); + } + + @Override + public byte[] getEncryptionKey() { + return encryptionKey; + } + + @Override + public void setEncryptionKey(final byte[] encryptionKey) { + this.encryptionKey = ArrayUtils.clone(encryptionKey); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jSAML2SPClientApp.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jSAML2SPClientApp.java new file mode 100644 index 0000000000..17ea8b8b18 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jSAML2SPClientApp.java @@ -0,0 +1,322 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity.am; + +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.syncope.common.lib.types.SAML2SPNameId; +import org.apache.syncope.common.lib.types.XmlSecAlgorithm; +import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.annotation.Transient; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.PostLoad; + +@Node(Neo4jSAML2SPClientApp.NODE) +public class Neo4jSAML2SPClientApp extends AbstractClientApp implements SAML2SPClientApp { + + private static final long serialVersionUID = 6422422526695279794L; + + public static final String NODE = "SAML2SPClientApp"; + + protected static final TypeReference> STRING_TYPEREF = new TypeReference>() { + }; + + protected static final TypeReference> XMLSECAGO_TYPEREF = + new TypeReference>() { + }; + + @NotNull + private String entityId; + + private String metadataLocation; + + private String metadataSignatureLocation; + + private boolean signAssertions; + + private boolean signResponses; + + private boolean encryptionOptional; + + private boolean encryptAssertions; + + private String requiredAuthenticationContextClass; + + private SAML2SPNameId requiredNameIdFormat; + + private Integer skewAllowance; + + private String nameIdQualifier; + + private String assertionAudiences; + + @Transient + private Set assertionAudiencesSet = new HashSet<>(); + + private String serviceProviderNameIdQualifier; + + private String signingSignatureAlgorithms; + + @Transient + private List signingSignatureAlgorithmsList = new ArrayList<>(); + + private String signingSignatureReferenceDigestMethods; + + @Transient + private List signingSignatureReferenceDigestMethodsList = new ArrayList<>(); + + private String encryptionDataAlgorithms; + + @Transient + private List encryptionDataAlgorithmsList = new ArrayList<>(); + + private String encryptionKeyAlgorithms; + + @Transient + private List encryptionKeyAlgorithmsList = new ArrayList<>(); + + private String signingSignatureBlackListedAlgorithms; + + @Transient + private List signingSignatureBlackListedAlgorithmsList = new ArrayList<>(); + + private String encryptionBlackListedAlgorithms; + + @Transient + private List encryptionBlackListedAlgorithmsList = new ArrayList<>(); + + @Override + public String getEntityId() { + return entityId; + } + + @Override + public void setEntityId(final String entityId) { + this.entityId = entityId; + } + + @Override + public String getMetadataLocation() { + return metadataLocation; + } + + @Override + public void setMetadataLocation(final String metadataLocation) { + this.metadataLocation = metadataLocation; + } + + @Override + public String getMetadataSignatureLocation() { + return metadataSignatureLocation; + } + + @Override + public void setMetadataSignatureLocation(final String metadataSignatureLocation) { + this.metadataSignatureLocation = metadataSignatureLocation; + } + + @Override + public boolean isSignAssertions() { + return signAssertions; + } + + @Override + public void setSignAssertions(final boolean signAssertions) { + this.signAssertions = signAssertions; + } + + @Override + public boolean isSignResponses() { + return signResponses; + } + + @Override + public void setSignResponses(final boolean signResponses) { + this.signResponses = signResponses; + } + + @Override + public boolean isEncryptionOptional() { + return encryptionOptional; + } + + @Override + public void setEncryptionOptional(final boolean encryptionOptional) { + this.encryptionOptional = encryptionOptional; + } + + @Override + public boolean isEncryptAssertions() { + return encryptAssertions; + } + + @Override + public void setEncryptAssertions(final boolean encryptAssertions) { + this.encryptAssertions = encryptAssertions; + } + + @Override + public String getRequiredAuthenticationContextClass() { + return requiredAuthenticationContextClass; + } + + @Override + public void setRequiredAuthenticationContextClass(final String requiredAuthenticationContextClass) { + this.requiredAuthenticationContextClass = requiredAuthenticationContextClass; + } + + @Override + public SAML2SPNameId getRequiredNameIdFormat() { + return requiredNameIdFormat; + } + + @Override + public void setRequiredNameIdFormat(final SAML2SPNameId requiredNameIdFormat) { + this.requiredNameIdFormat = requiredNameIdFormat; + } + + @Override + public Integer getSkewAllowance() { + return skewAllowance; + } + + @Override + public void setSkewAllowance(final Integer skewAllowance) { + this.skewAllowance = skewAllowance; + } + + @Override + public String getNameIdQualifier() { + return nameIdQualifier; + } + + @Override + public void setNameIdQualifier(final String nameIdQualifier) { + this.nameIdQualifier = nameIdQualifier; + } + + @Override + public Set getAssertionAudiences() { + return assertionAudiencesSet; + } + + @Override + public String getServiceProviderNameIdQualifier() { + return serviceProviderNameIdQualifier; + } + + @Override + public void setServiceProviderNameIdQualifier(final String serviceProviderNameIdQualifier) { + this.serviceProviderNameIdQualifier = serviceProviderNameIdQualifier; + } + + @Override + public List getSigningSignatureAlgorithms() { + return signingSignatureAlgorithmsList; + } + + @Override + public List getSigningSignatureReferenceDigestMethods() { + return signingSignatureReferenceDigestMethodsList; + } + + @Override + public List getEncryptionDataAlgorithms() { + return encryptionDataAlgorithmsList; + } + + @Override + public List getEncryptionKeyAlgorithms() { + return encryptionKeyAlgorithmsList; + } + + @Override + public List getSigningSignatureBlackListedAlgorithms() { + return signingSignatureBlackListedAlgorithmsList; + } + + @Override + public List getEncryptionBlackListedAlgorithms() { + return encryptionBlackListedAlgorithmsList; + } + + protected void json2list(final boolean clearFirst) { + if (clearFirst) { + getAssertionAudiences().clear(); + getSigningSignatureAlgorithms().clear(); + getSigningSignatureReferenceDigestMethods().clear(); + getEncryptionDataAlgorithms().clear(); + getEncryptionKeyAlgorithms().clear(); + getSigningSignatureBlackListedAlgorithms().clear(); + getEncryptionBlackListedAlgorithms().clear(); + } + if (assertionAudiences != null) { + getAssertionAudiences().addAll( + POJOHelper.deserialize(assertionAudiences, STRING_TYPEREF)); + } + if (signingSignatureAlgorithms != null) { + getSigningSignatureAlgorithms().addAll( + POJOHelper.deserialize(signingSignatureAlgorithms, XMLSECAGO_TYPEREF)); + } + if (signingSignatureReferenceDigestMethods != null) { + getSigningSignatureReferenceDigestMethods().addAll( + POJOHelper.deserialize(signingSignatureReferenceDigestMethods, XMLSECAGO_TYPEREF)); + } + if (encryptionDataAlgorithms != null) { + getEncryptionDataAlgorithms().addAll( + POJOHelper.deserialize(encryptionDataAlgorithms, XMLSECAGO_TYPEREF)); + } + if (encryptionKeyAlgorithms != null) { + getEncryptionKeyAlgorithms().addAll( + POJOHelper.deserialize(encryptionKeyAlgorithms, XMLSECAGO_TYPEREF)); + } + if (signingSignatureBlackListedAlgorithms != null) { + getSigningSignatureBlackListedAlgorithms().addAll( + POJOHelper.deserialize(signingSignatureBlackListedAlgorithms, XMLSECAGO_TYPEREF)); + } + if (encryptionBlackListedAlgorithms != null) { + getEncryptionBlackListedAlgorithms().addAll( + POJOHelper.deserialize(encryptionBlackListedAlgorithms, XMLSECAGO_TYPEREF)); + } + } + + @PostLoad + public void postLoad() { + json2list(false); + } + + public void postSave() { + json2list(true); + } + + public void list2json() { + assertionAudiences = POJOHelper.serialize(getAssertionAudiences()); + signingSignatureAlgorithms = POJOHelper.serialize(getSigningSignatureAlgorithms()); + signingSignatureReferenceDigestMethods = POJOHelper.serialize(getSigningSignatureReferenceDigestMethods()); + encryptionDataAlgorithms = POJOHelper.serialize(getEncryptionDataAlgorithms()); + encryptionKeyAlgorithms = POJOHelper.serialize(getEncryptionKeyAlgorithms()); + signingSignatureBlackListedAlgorithms = POJOHelper.serialize(getSigningSignatureBlackListedAlgorithms()); + encryptionBlackListedAlgorithms = POJOHelper.serialize(getEncryptionBlackListedAlgorithms()); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jSAML2SPEntity.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jSAML2SPEntity.java new file mode 100644 index 0000000000..880d6783e4 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jSAML2SPEntity.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.persistence.neo4j.entity.am; + +import jakarta.validation.constraints.NotNull; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.syncope.core.persistence.api.entity.am.SAML2SPEntity; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractProvidedKeyNode; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jSAML2SPEntity.NODE) +public class Neo4jSAML2SPEntity extends AbstractProvidedKeyNode implements SAML2SPEntity { + + public static final String NODE = "SAML2SPEntity"; + + private static final long serialVersionUID = 12342617217394093L; + + @NotNull + private byte[] keystore; + + @NotNull + private byte[] metadata; + + @Override + public byte[] getKeystore() { + return keystore; + } + + @Override + public void setKeystore(final byte[] keystore) { + this.keystore = ArrayUtils.clone(keystore); + } + + @Override + public byte[] getMetadata() { + return metadata; + } + + @Override + public void setMetadata(final byte[] metadata) { + this.metadata = ArrayUtils.clone(metadata); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jWAConfigEntry.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jWAConfigEntry.java new file mode 100644 index 0000000000..c743dde7c7 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/am/Neo4jWAConfigEntry.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.persistence.neo4j.entity.am; + +import com.fasterxml.jackson.core.type.TypeReference; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.entity.am.WAConfigEntry; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractProvidedKeyNode; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jWAConfigEntry.NODE) +public class Neo4jWAConfigEntry extends AbstractProvidedKeyNode implements WAConfigEntry { + + private static final long serialVersionUID = 6422422526695279794L; + + public static final String NODE = "WAConfigEntry"; + + protected static TypeReference> TYPEREF = new TypeReference>() { + }; + + private String waConfigValues; + + @Override + public List getValues() { + return Optional.ofNullable(waConfigValues).map(v -> POJOHelper.deserialize(v, TYPEREF)).orElse(List.of()); + } + + @Override + public void setValues(final List values) { + this.waConfigValues = POJOHelper.serialize(values); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jADynGroupMembership.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jADynGroupMembership.java new file mode 100644 index 0000000000..9d8ec31e9c --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jADynGroupMembership.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.persistence.neo4j.entity.anyobject; + +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership; +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.neo4j.entity.AbstractDynMembership; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyType; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jADynGroupMembership.NODE) +public class Neo4jADynGroupMembership extends AbstractDynMembership implements ADynGroupMembership { + + private static final long serialVersionUID = -7336814163949640354L; + + public static final String NODE = "ADynGroupMembership"; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jGroup group; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jAnyType anyType; + + @Override + public Group getGroup() { + return group; + } + + @Override + public void setGroup(final Group role) { + checkType(role, Neo4jGroup.class); + this.group = (Neo4jGroup) role; + } + + @Override + public AnyType getAnyType() { + return anyType; + } + + @Override + public void setAnyType(final AnyType anyType) { + checkType(anyType, Neo4jAnyType.class); + this.anyType = (Neo4jAnyType) anyType; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAMembership.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAMembership.java new file mode 100644 index 0000000000..b9d6af28d5 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAMembership.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.persistence.neo4j.entity.anyobject; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.entity.MembershipType; +import org.apache.syncope.core.persistence.api.entity.RelationshipType; +import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership; +import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttr; +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.neo4j.entity.AbstractMembership; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; +import org.springframework.data.neo4j.core.schema.CompositeProperty; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jAMembership.NODE) +public class Neo4jAMembership extends AbstractMembership implements AMembership { + + private static final long serialVersionUID = -14584450896965100L; + + public static final String NODE = "AMembership"; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jAnyObject leftEnd; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jGroup rightEnd; + + @CompositeProperty + protected Map plainAttrs = new HashMap<>(); + + @Override + public MembershipType getType() { + return MembershipType.getInstance(); + } + + @Override + public void setType(final RelationshipType type) { + // cannot be changed + } + + @Override + public AnyObject getLeftEnd() { + return leftEnd; + } + + @Override + public void setLeftEnd(final AnyObject leftEnd) { + checkType(leftEnd, Neo4jAnyObject.class); + this.leftEnd = (Neo4jAnyObject) leftEnd; + } + + @Override + public Group getRightEnd() { + return rightEnd; + } + + @Override + public void setRightEnd(final Group rightEnd) { + checkType(rightEnd, Neo4jGroup.class); + this.rightEnd = (Neo4jGroup) rightEnd; + } + + @Override + public List getPlainAttrs() { + return plainAttrs.entrySet().stream(). + sorted(Comparator.comparing(Map.Entry::getKey)). + map(Map.Entry::getValue).toList(); + } + + @Override + public Optional getPlainAttr(final String plainSchema) { + return Optional.ofNullable(plainAttrs.get(plainSchema)); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAPlainAttr.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAPlainAttr.java new file mode 100644 index 0000000000..8f4264e133 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAPlainAttr.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity.anyobject; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership; +import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttr; +import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractPlainAttr; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jPlainAttr; +import org.apache.syncope.core.spring.ApplicationContextProvider; + +public class Neo4jAPlainAttr extends AbstractPlainAttr implements APlainAttr, Neo4jPlainAttr { + + private static final long serialVersionUID = 806271775349587902L; + + /** + * The owner of this attribute. + */ + @JsonIgnore + private Neo4jAnyObject owner; + + /** + * The membership of this attribute; might be {@code NULL} if this attribute is not related to a membership. + */ + @JsonProperty("membership") + private String membershipKey; + + /** + * Values of this attribute (if schema is not UNIQUE). + */ + private final List values = new ArrayList<>(); + + /** + * Value of this attribute (if schema is UNIQUE). + */ + @JsonProperty + private Neo4jAPlainAttrUniqueValue uniqueValue; + + @Override + public AnyObject getOwner() { + return owner; + } + + @Override + public void setOwner(final AnyObject owner) { + checkType(owner, Neo4jAnyObject.class); + this.owner = (Neo4jAnyObject) owner; + } + + public String getMembershipKey() { + return membershipKey; + } + + @JsonSetter("membership") + public void setMembershipKey(final String membershipKey) { + this.membershipKey = membershipKey; + } + + @JsonIgnore + @Override + public AMembership getMembership() { + return ApplicationContextProvider.getBeanFactory().getBean(AnyObjectDAO.class).findMembership(membershipKey); + } + + @JsonIgnore + @Override + public void setMembership(final AMembership membership) { + checkType(membership, Neo4jAMembership.class); + if (membership != null) { + this.membershipKey = membership.getKey(); + } + } + + @Override + protected boolean addForMultiValue(final PlainAttrValue attrValue) { + checkType(attrValue, Neo4jAPlainAttrValue.class); + return values.add((Neo4jAPlainAttrValue) attrValue); + } + + @Override + public boolean add(final PlainAttrValue value) { + return addForMultiValue(value); + } + + @Override + public List getValues() { + return values; + } + + @Override + public Neo4jAPlainAttrUniqueValue getUniqueValue() { + return uniqueValue; + } + + @JsonIgnore + @Override + public void setUniqueValue(final PlainAttrUniqueValue uniqueValue) { + checkType(uniqueValue, Neo4jAPlainAttrUniqueValue.class); + this.uniqueValue = (Neo4jAPlainAttrUniqueValue) uniqueValue; + } + + @Override + public int hashCode() { + return new HashCodeBuilder(). + append(schemaKey). + append(membershipKey). + append(values). + append(uniqueValue). + build(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Neo4jAPlainAttr other = (Neo4jAPlainAttr) obj; + return new EqualsBuilder(). + append(schemaKey, other.schemaKey). + append(membershipKey, other.membershipKey). + append(values, other.values). + append(uniqueValue, other.uniqueValue). + build(); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAPlainAttrUniqueValue.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAPlainAttrUniqueValue.java new file mode 100644 index 0000000000..f06ec68dcb --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAPlainAttrUniqueValue.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.persistence.neo4j.entity.anyobject; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttr; +import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttrUniqueValue; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractPlainAttrValue; + +public class Neo4jAPlainAttrUniqueValue extends AbstractPlainAttrValue implements APlainAttrUniqueValue { + + private static final long serialVersionUID = -4053996864791245312L; + + @JsonIgnore + @NotNull + private Neo4jAPlainAttr attr; + + @Override + public APlainAttr getAttr() { + return attr; + } + + @Override + public void setAttr(final PlainAttr attr) { + checkType(attr, Neo4jAPlainAttr.class); + this.attr = (Neo4jAPlainAttr) attr; + } + + @JsonIgnore + @Override + public PlainSchema getSchema() { + return getAttr() == null ? null : getAttr().getSchema(); + } + + @Override + public void setSchema(final PlainSchema schema) { + // nothing to do + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAPlainAttrValue.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAPlainAttrValue.java new file mode 100644 index 0000000000..4eb20001d3 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAPlainAttrValue.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.persistence.neo4j.entity.anyobject; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttr; +import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttrValue; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractPlainAttrValue; + +public class Neo4jAPlainAttrValue extends AbstractPlainAttrValue implements APlainAttrValue { + + private static final long serialVersionUID = -8657212700294416428L; + + @JsonIgnore + @NotNull + private Neo4jAPlainAttr attr; + + @Override + public APlainAttr getAttr() { + return attr; + } + + @Override + public void setAttr(final PlainAttr attr) { + checkType(attr, Neo4jAPlainAttr.class); + this.attr = (Neo4jAPlainAttr) attr; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jARelationship.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jARelationship.java new file mode 100644 index 0000000000..1dbb810c20 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jARelationship.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.persistence.neo4j.entity.anyobject; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.MembershipType; +import org.apache.syncope.core.persistence.api.entity.RelationshipType; +import org.apache.syncope.core.persistence.api.entity.anyobject.ARelationship; +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractGeneratedKeyNode; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRelationshipType; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jARelationship.NODE) +public class Neo4jARelationship extends AbstractGeneratedKeyNode implements ARelationship { + + private static final long serialVersionUID = 2778494939240083204L; + + public static final String NODE = "ARelationship"; + + public static final String SOURCE_REL = "ARELATIONSHIP_SOURCE"; + + public static final String DEST_REL = "ARELATIONSHIP_DEST"; + + @NotNull + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jRelationshipType type; + + @Relationship(type = SOURCE_REL, direction = Relationship.Direction.OUTGOING) + private AnyObject leftEnd; + + @Relationship(type = DEST_REL, direction = Relationship.Direction.OUTGOING) + private AnyObject rightEnd; + + @Override + public RelationshipType getType() { + return type; + } + + @Override + public void setType(final RelationshipType type) { + if (MembershipType.getInstance().getKey().equalsIgnoreCase(type.getKey())) { + throw new IllegalArgumentException("This is not a membership"); + } + checkType(type, Neo4jRelationshipType.class); + this.type = (Neo4jRelationshipType) type; + } + + @Override + public AnyObject getLeftEnd() { + return leftEnd; + } + + @Override + public void setLeftEnd(final AnyObject leftEnd) { + checkType(leftEnd, Neo4jAnyObject.class); + this.leftEnd = (Neo4jAnyObject) leftEnd; + } + + @Override + public AnyObject getRightEnd() { + return rightEnd; + } + + @Override + public void setRightEnd(final AnyObject rightEnd) { + checkType(rightEnd, Neo4jAnyObject.class); + this.rightEnd = (Neo4jAnyObject) rightEnd; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAnyObject.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAnyObject.java new file mode 100644 index 0000000000..9ddf0a72ac --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/anyobject/Neo4jAnyObject.java @@ -0,0 +1,183 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity.anyobject; + +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +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.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.RelationshipType; +import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership; +import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttr; +import org.apache.syncope.core.persistence.api.entity.anyobject.ARelationship; +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.persistence.common.validation.AnyObjectCheck; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractGroupableRelatable; +import org.apache.syncope.core.persistence.neo4j.entity.AttributableCheck; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyType; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyTypeClass; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAttributable; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jPlainAttr; +import org.springframework.data.neo4j.core.schema.CompositeProperty; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jAnyObject.NODE) +@AnyObjectCheck +@AttributableCheck +public class Neo4jAnyObject + extends AbstractGroupableRelatable + implements AnyObject, Neo4jAttributable { + + private static final long serialVersionUID = -3905046855521446823L; + + public static final String NODE = "AnyObject"; + + protected static final TypeReference> TYPEREF = new TypeReference>() { + }; + + @CompositeProperty(converterRef = "aPlainAttrsConverter") + protected Map plainAttrs = new HashMap<>(); + + @NotNull(message = "Blank name") + protected String name; + + @NotNull + @Relationship(direction = Relationship.Direction.OUTGOING) + protected Neo4jAnyType type; + + /** + * Provisioning external resources. + */ + @Relationship(direction = Relationship.Direction.OUTGOING) + protected List resources = new ArrayList<>(); + + @Relationship(direction = Relationship.Direction.OUTGOING) + protected List auxClasses = new ArrayList<>(); + + @Relationship(type = Neo4jARelationship.SOURCE_REL, direction = Relationship.Direction.INCOMING) + protected List relationships = new ArrayList<>(); + + @Relationship(direction = Relationship.Direction.INCOMING) + protected List memberships = new ArrayList<>(); + + @Override + protected Map>> plainAttrs() { + return plainAttrs; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(final String name) { + this.name = name; + } + + @Override + public AnyType getType() { + return type; + } + + @Override + public void setType(final AnyType type) { + checkType(type, Neo4jAnyType.class); + this.type = (Neo4jAnyType) type; + } + + @Override + public boolean add(final ExternalResource resource) { + checkType(resource, Neo4jExternalResource.class); + return resources.contains((Neo4jExternalResource) resource) || resources.add((Neo4jExternalResource) resource); + } + + @Override + public List getResources() { + return resources; + } + + @Override + public boolean add(final APlainAttr attr) { + checkType(attr, Neo4jAPlainAttr.class); + return plainAttrs.put(attr.getSchema().getKey(), (Neo4jAPlainAttr) attr) != null; + } + + @Override + protected Map internalGetPlainAttrs() { + return plainAttrs; + } + + @Override + public boolean add(final AnyTypeClass auxClass) { + checkType(auxClass, Neo4jAnyTypeClass.class); + return auxClasses.contains((Neo4jAnyTypeClass) auxClass) || auxClasses.add((Neo4jAnyTypeClass) auxClass); + } + + @Override + public List getAuxClasses() { + return auxClasses; + } + + @Override + public boolean add(final ARelationship relationship) { + checkType(relationship, Neo4jARelationship.class); + return this.relationships.add((Neo4jARelationship) relationship); + } + + @Override + public Optional getRelationship( + final RelationshipType relationshipType, final String otherEndKey) { + + return getRelationships().stream().filter(relationship -> relationshipType.equals(relationship.getType()) + && otherEndKey != null && otherEndKey.equals(relationship.getRightEnd().getKey())). + findFirst(); + } + + @Override + public List getRelationships() { + return relationships; + } + + @Override + public boolean add(final AMembership membership) { + checkType(membership, Neo4jAMembership.class); + return this.memberships.add((Neo4jAMembership) membership); + } + + @Override + public boolean remove(final AMembership membership) { + checkType(membership, Neo4jAMembership.class); + return this.memberships.remove((Neo4jAMembership) membership); + } + + @Override + protected List internalGetMemberships() { + return memberships; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGPlainAttr.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGPlainAttr.java new file mode 100644 index 0000000000..de002b13b2 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGPlainAttr.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.core.persistence.neo4j.entity.group; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr; +import org.apache.syncope.core.persistence.api.entity.group.GPlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractPlainAttr; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jPlainAttr; + +public class Neo4jGPlainAttr extends AbstractPlainAttr implements GPlainAttr, Neo4jPlainAttr { + + private static final long serialVersionUID = 806271775349587902L; + + /** + * The owner of this attribute. + */ + @JsonIgnore + private Neo4jGroup owner; + + /** + * Values of this attribute (if schema is not UNIQUE). + */ + private final List values = new ArrayList<>(); + + /** + * Value of this attribute (if schema is UNIQUE). + */ + @JsonProperty + private Neo4jGPlainAttrUniqueValue uniqueValue; + + @Override + public Group getOwner() { + return owner; + } + + @Override + public void setOwner(final Group owner) { + checkType(owner, Neo4jGroup.class); + this.owner = (Neo4jGroup) owner; + } + + @Override + protected boolean addForMultiValue(final PlainAttrValue attrValue) { + checkType(attrValue, Neo4jGPlainAttrValue.class); + return values.add((Neo4jGPlainAttrValue) attrValue); + } + + @Override + public boolean add(final PlainAttrValue value) { + return addForMultiValue(value); + } + + @Override + public List getValues() { + return values; + } + + @Override + public Neo4jGPlainAttrUniqueValue getUniqueValue() { + return uniqueValue; + } + + @JsonIgnore + @Override + public void setUniqueValue(final PlainAttrUniqueValue uniqueValue) { + checkType(uniqueValue, Neo4jGPlainAttrUniqueValue.class); + this.uniqueValue = (Neo4jGPlainAttrUniqueValue) uniqueValue; + } + + @Override + public int hashCode() { + return new HashCodeBuilder(). + append(schemaKey). + append(values). + append(uniqueValue). + build(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Neo4jGPlainAttr other = (Neo4jGPlainAttr) obj; + return new EqualsBuilder(). + append(schemaKey, other.schemaKey). + append(values, other.values). + append(uniqueValue, other.uniqueValue). + build(); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGPlainAttrUniqueValue.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGPlainAttrUniqueValue.java new file mode 100644 index 0000000000..f17ec0929e --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGPlainAttrUniqueValue.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.persistence.neo4j.entity.group; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr; +import org.apache.syncope.core.persistence.api.entity.group.GPlainAttrUniqueValue; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractPlainAttrValue; + +public class Neo4jGPlainAttrUniqueValue extends AbstractPlainAttrValue implements GPlainAttrUniqueValue { + + private static final long serialVersionUID = -4053996864791245312L; + + @JsonIgnore + @NotNull + private Neo4jGPlainAttr attr; + + @Override + public GPlainAttr getAttr() { + return attr; + } + + @Override + public void setAttr(final PlainAttr attr) { + checkType(attr, Neo4jGPlainAttr.class); + this.attr = (Neo4jGPlainAttr) attr; + } + + @JsonIgnore + @Override + public PlainSchema getSchema() { + return getAttr() == null ? null : getAttr().getSchema(); + } + + @Override + public void setSchema(final PlainSchema schema) { + // nothing to do + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGPlainAttrValue.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGPlainAttrValue.java new file mode 100644 index 0000000000..7861d0892d --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGPlainAttrValue.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.persistence.neo4j.entity.group; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr; +import org.apache.syncope.core.persistence.api.entity.group.GPlainAttrValue; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractPlainAttrValue; + +public class Neo4jGPlainAttrValue extends AbstractPlainAttrValue implements GPlainAttrValue { + + private static final long serialVersionUID = -8657212700294416428L; + + @JsonIgnore + @NotNull + private Neo4jGPlainAttr attr; + + @Override + public GPlainAttr getAttr() { + return attr; + } + + @Override + public void setAttr(final PlainAttr attr) { + checkType(attr, Neo4jGPlainAttr.class); + this.attr = (Neo4jGPlainAttr) attr; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGroup.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGroup.java new file mode 100644 index 0000000000..dc423edfa8 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jGroup.java @@ -0,0 +1,236 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity.group; + +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +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.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership; +import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.group.TypeExtension; +import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.common.validation.GroupCheck; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractAny; +import org.apache.syncope.core.persistence.neo4j.entity.AttributableCheck; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyTypeClass; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAttributable; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jPlainAttr; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jADynGroupMembership; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUDynGroupMembership; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUser; +import org.apache.syncope.core.spring.ApplicationContextProvider; +import org.springframework.data.neo4j.core.schema.CompositeProperty; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jGroup.NODE) +@GroupCheck +@AttributableCheck +public class Neo4jGroup extends AbstractAny implements Group, Neo4jAttributable { + + private static final long serialVersionUID = -5281258853142421875L; + + public static final String NODE = "SyncopeGroup"; + + public static final String GROUP_TYPE_EXTENSION_REL = "GROUP_TYPE_EXTENSION"; + + @NotNull + private String name; + + @Relationship(direction = Relationship.Direction.OUTGOING) + protected Neo4jUser userOwner; + + @Relationship(direction = Relationship.Direction.OUTGOING) + protected Neo4jGroup groupOwner; + + @CompositeProperty(converterRef = "gPlainAttrsConverter") + protected Map plainAttrs = new HashMap<>(); + + /** + * Provisioning external resources. + */ + @Relationship(direction = Relationship.Direction.OUTGOING) + protected List resources = new ArrayList<>(); + + @Relationship(direction = Relationship.Direction.OUTGOING) + protected List auxClasses = new ArrayList<>(); + + @Relationship(direction = Relationship.Direction.INCOMING) + private Neo4jUDynGroupMembership uDynMembership; + + @Relationship(direction = Relationship.Direction.INCOMING) + private List aDynMemberships = new ArrayList<>(); + + @Relationship(type = GROUP_TYPE_EXTENSION_REL, direction = Relationship.Direction.INCOMING) + private List typeExtensions = new ArrayList<>(); + + @Override + protected Map>> plainAttrs() { + return plainAttrs; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(final String name) { + this.name = name; + } + + @Override + public AnyType getType() { + return ApplicationContextProvider.getBeanFactory().getBean(AnyTypeDAO.class).getGroup(); + } + + @Override + public void setType(final AnyType type) { + // nothing to do + } + + @Override + public boolean add(final ExternalResource resource) { + checkType(resource, Neo4jExternalResource.class); + return resources.contains((Neo4jExternalResource) resource) || resources.add((Neo4jExternalResource) resource); + } + + @Override + public List getResources() { + return resources; + } + + @Override + public User getUserOwner() { + return userOwner; + } + + @Override + public void setUserOwner(final User userOwner) { + checkType(userOwner, Neo4jUser.class); + this.userOwner = (Neo4jUser) userOwner; + } + + @Override + public Group getGroupOwner() { + return groupOwner; + } + + @Override + public void setGroupOwner(final Group group) { + checkType(group, Neo4jGroup.class); + this.groupOwner = (Neo4jGroup) group; + } + + @Override + public boolean add(final GPlainAttr attr) { + checkType(attr, Neo4jGPlainAttr.class); + return plainAttrs.put(attr.getSchema().getKey(), (Neo4jGPlainAttr) attr) != null; + } + + @Override + public boolean remove(final GPlainAttr attr) { + checkType(attr, Neo4jGPlainAttr.class); + return getPlainAttrs().remove((Neo4jGPlainAttr) attr); + } + + @Override + public Optional getPlainAttr(final String plainSchema) { + return getPlainAttrs().stream(). + filter(plainAttr -> plainAttr != null && plainAttr.getSchema() != null + && plainSchema.equals(plainAttr.getSchema().getKey())).findFirst(); + } + + @Override + public List getPlainAttrs() { + return plainAttrs.entrySet().stream(). + sorted(Comparator.comparing(Map.Entry::getKey)). + map(Map.Entry::getValue).toList(); + } + + @Override + public UDynGroupMembership getUDynMembership() { + return uDynMembership; + } + + @Override + public void setUDynMembership(final UDynGroupMembership uDynMembership) { + checkType(uDynMembership, Neo4jUDynGroupMembership.class); + this.uDynMembership = (Neo4jUDynGroupMembership) uDynMembership; + } + + @Override + public boolean add(final AnyTypeClass auxClass) { + checkType(auxClass, Neo4jAnyTypeClass.class); + return auxClasses.contains((Neo4jAnyTypeClass) auxClass) || this.auxClasses.add((Neo4jAnyTypeClass) auxClass); + } + + @Override + public List getAuxClasses() { + return auxClasses; + } + + @Override + public boolean add(final ADynGroupMembership dynGroupMembership) { + checkType(dynGroupMembership, Neo4jADynGroupMembership.class); + return this.aDynMemberships.add((Neo4jADynGroupMembership) dynGroupMembership); + } + + @Override + public Optional getADynMembership(final AnyType anyType) { + return aDynMemberships.stream(). + filter(dynGroupMembership -> anyType != null && anyType.equals(dynGroupMembership.getAnyType())). + findFirst(); + } + + @Override + public List getADynMemberships() { + return aDynMemberships; + } + + @Override + public boolean add(final TypeExtension typeExtension) { + checkType(typeExtension, Neo4jTypeExtension.class); + return this.typeExtensions.add((Neo4jTypeExtension) typeExtension); + } + + @Override + public Optional getTypeExtension(final AnyType anyType) { + return typeExtensions.stream(). + filter(typeExtension -> typeExtension.getAnyType().equals(anyType)). + findFirst(); + } + + @Override + public List getTypeExtensions() { + return typeExtensions; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jTypeExtension.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jTypeExtension.java new file mode 100644 index 0000000000..07108c04b0 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/group/Neo4jTypeExtension.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.core.persistence.neo4j.entity.group; + +import java.util.ArrayList; +import java.util.List; +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.group.Group; +import org.apache.syncope.core.persistence.api.entity.group.TypeExtension; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractGeneratedKeyNode; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyType; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyTypeClass; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jTypeExtension.NODE) +public class Neo4jTypeExtension extends AbstractGeneratedKeyNode implements TypeExtension { + + private static final long serialVersionUID = -8367626793791263551L; + + public static final String NODE = "TypeExtension"; + + @Relationship(type = Neo4jGroup.GROUP_TYPE_EXTENSION_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jGroup group; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jAnyType anyType; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private List auxClasses = new ArrayList<>(); + + @Override + public Group getGroup() { + return group; + } + + @Override + public void setGroup(final Group group) { + checkType(group, Neo4jGroup.class); + this.group = (Neo4jGroup) group; + } + + @Override + public AnyType getAnyType() { + return anyType; + } + + @Override + public void setAnyType(final AnyType anyType) { + checkType(anyType, Neo4jAnyType.class); + this.anyType = (Neo4jAnyType) anyType; + } + + @Override + public boolean add(final AnyTypeClass auxClass) { + checkType(auxClass, Neo4jAnyTypeClass.class); + return auxClasses.contains((Neo4jAnyTypeClass) auxClass) || auxClasses.add((Neo4jAnyTypeClass) auxClass); + } + + @Override + public List getAuxClasses() { + return auxClasses; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/AbstractCorrelationRuleEntity.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/AbstractCorrelationRuleEntity.java new file mode 100644 index 0000000000..390750e6e4 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/AbstractCorrelationRuleEntity.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.persistence.neo4j.entity.policy; + +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.policy.CorrelationRuleEntity; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractGeneratedKeyNode; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyType; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementation; +import org.springframework.data.neo4j.core.schema.Relationship; + +abstract class AbstractCorrelationRuleEntity extends AbstractGeneratedKeyNode implements CorrelationRuleEntity { + + private static final long serialVersionUID = 4017405130146577834L; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jAnyType anyType; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jImplementation implementation; + + @Override + public AnyType getAnyType() { + return anyType; + } + + @Override + public void setAnyType(final AnyType anyType) { + checkType(anyType, Neo4jAnyType.class); + this.anyType = (Neo4jAnyType) anyType; + } + + @Override + public Implementation getImplementation() { + return implementation; + } + + protected abstract String getImplementationType(); + + @Override + public void setImplementation(final Implementation implementation) { + checkType(implementation, Neo4jImplementation.class); + checkImplementationType(implementation, getImplementationType()); + this.implementation = (Neo4jImplementation) implementation; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAccessPolicy.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAccessPolicy.java new file mode 100644 index 0000000000..f54d47b65a --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAccessPolicy.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.neo4j.entity.policy; + +import java.util.Optional; +import org.apache.syncope.common.lib.policy.AccessPolicyConf; +import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jAccessPolicy.NODE) +public class Neo4jAccessPolicy extends Neo4jPolicy implements AccessPolicy { + + private static final long serialVersionUID = -4190607009908888884L; + + public static final String NODE = "AccessPolicy"; + + private String jsonConf; + + @Override + public AccessPolicyConf getConf() { + return Optional.ofNullable(jsonConf).map(c -> POJOHelper.deserialize(c, AccessPolicyConf.class)).orElse(null); + } + + @Override + public void setConf(final AccessPolicyConf conf) { + jsonConf = Optional.ofNullable(conf).map(POJOHelper::serialize).orElse(null); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAccountPolicy.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAccountPolicy.java new file mode 100644 index 0000000000..e32bdfed97 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAccountPolicy.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.persistence.neo4j.entity.policy; + +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.syncope.common.lib.types.IdRepoImplementationType; +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.policy.AccountPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementation; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jAccountPolicy.NODE) +public class Neo4jAccountPolicy extends Neo4jPolicy implements AccountPolicy { + + private static final long serialVersionUID = -2767606675667839060L; + + public static final String NODE = "AccountPolicy"; + + public static final String ACCOUNT_POLICY_RULE_REL = "ACCOUNT_POLICY_RULE"; + + @NotNull + private Boolean propagateSuspension = false; + + private int maxAuthenticationAttempts; + + @Relationship(type = ACCOUNT_POLICY_RULE_REL, direction = Relationship.Direction.OUTGOING) + private List rules = new ArrayList<>(); + + /** + * Resources for alternative user authentication: if empty, only internal storage will be used. + */ + @Relationship(direction = Relationship.Direction.INCOMING) + private Set resources = new HashSet<>(); + + @Override + public boolean isPropagateSuspension() { + return propagateSuspension; + } + + @Override + public void setPropagateSuspension(final boolean propagateSuspension) { + this.propagateSuspension = propagateSuspension; + } + + @Override + public int getMaxAuthenticationAttempts() { + return maxAuthenticationAttempts; + } + + @Override + public void setMaxAuthenticationAttempts(final int maxAuthenticationAttempts) { + this.maxAuthenticationAttempts = maxAuthenticationAttempts; + } + + @Override + public boolean add(final Implementation rule) { + checkType(rule, Neo4jImplementation.class); + checkImplementationType(rule, IdRepoImplementationType.ACCOUNT_RULE); + return rules.contains((Neo4jImplementation) rule) || rules.add((Neo4jImplementation) rule); + } + + @Override + public List getRules() { + return rules; + } + + @Override + public boolean add(final ExternalResource resource) { + checkType(resource, Neo4jExternalResource.class); + return resources.contains((Neo4jExternalResource) resource) || resources.add((Neo4jExternalResource) resource); + } + + @Override + public Set getResources() { + return resources; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAttrReleasePolicy.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAttrReleasePolicy.java new file mode 100644 index 0000000000..f44580f83a --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAttrReleasePolicy.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.core.persistence.neo4j.entity.policy; + +import java.util.Optional; +import org.apache.syncope.common.lib.policy.AttrReleasePolicyConf; +import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jAttrReleasePolicy.NODE) +public class Neo4jAttrReleasePolicy extends Neo4jPolicy implements AttrReleasePolicy { + + private static final long serialVersionUID = -4190607669908888884L; + + public static final String NODE = "AttrReleasePolicy"; + + private Integer arporder = 0; + + private Boolean status; + + private String jsonConf; + + @Override + public int getOrder() { + return Optional.ofNullable(arporder).orElse(0); + } + + @Override + public void setOrder(final int order) { + this.arporder = order; + } + + @Override + public boolean getStatus() { + return status == null ? true : status; + } + + @Override + public void setStatus(final Boolean status) { + this.status = status; + } + + @Override + public AttrReleasePolicyConf getConf() { + return Optional.ofNullable(jsonConf). + map(c -> POJOHelper.deserialize(c, AttrReleasePolicyConf.class)).orElse(null); + } + + @Override + public void setConf(final AttrReleasePolicyConf conf) { + jsonConf = Optional.ofNullable(conf).map(POJOHelper::serialize).orElse(null); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAuthPolicy.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAuthPolicy.java new file mode 100644 index 0000000000..7256a9d95e --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAuthPolicy.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.persistence.neo4j.entity.policy; + +import java.util.Optional; +import org.apache.syncope.common.lib.policy.AuthPolicyConf; +import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jAuthPolicy.NODE) +public class Neo4jAuthPolicy extends Neo4jPolicy implements AuthPolicy { + + private static final long serialVersionUID = -4190607009908888884L; + + public static final String NODE = "AuthPolicy"; + + private String jsonConf; + + @Override + public AuthPolicyConf getConf() { + return jsonConf == null + ? null + : POJOHelper.deserialize(jsonConf, AuthPolicyConf.class); + } + + @Override + public void setConf(final AuthPolicyConf conf) { + jsonConf = Optional.ofNullable(conf).map(POJOHelper::serialize).orElse(null); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPasswordPolicy.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPasswordPolicy.java new file mode 100644 index 0000000000..4342f8e90a --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPasswordPolicy.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.persistence.neo4j.entity.policy; + +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; +import org.apache.syncope.common.lib.types.IdRepoImplementationType; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementation; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jPasswordPolicy.NODE) +public class Neo4jPasswordPolicy extends Neo4jPolicy implements PasswordPolicy { + + private static final long serialVersionUID = 9138550910385232849L; + + public static final String NODE = "PasswordPolicy"; + + public static final String PASSWORD_POLICY_RULE_REL = "PASSWORD_POLICY_RULE"; + + @NotNull + private Boolean allowNullPassword = false; + + private int historyLength; + + @Relationship(type = PASSWORD_POLICY_RULE_REL, direction = Relationship.Direction.OUTGOING) + private List rules = new ArrayList<>(); + + @Override + public boolean isAllowNullPassword() { + return allowNullPassword; + } + + @Override + public void setAllowNullPassword(final boolean allowNullPassword) { + this.allowNullPassword = allowNullPassword; + } + + @Override + public int getHistoryLength() { + return historyLength; + } + + @Override + public void setHistoryLength(final int historyLength) { + this.historyLength = historyLength; + } + + @Override + public boolean add(final Implementation rule) { + checkType(rule, Neo4jImplementation.class); + checkImplementationType(rule, IdRepoImplementationType.PASSWORD_RULE); + return rules.contains((Neo4jImplementation) rule) || rules.add((Neo4jImplementation) rule); + } + + @Override + public List getRules() { + return rules; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPolicy.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPolicy.java new file mode 100644 index 0000000000..83f7f33cb4 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPolicy.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.persistence.neo4j.entity.policy; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.policy.Policy; +import org.apache.syncope.core.persistence.common.validation.PolicyCheck; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractGeneratedKeyNode; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jPolicy.NODE) +@PolicyCheck +public abstract class Neo4jPolicy extends AbstractGeneratedKeyNode implements Policy { + + private static final long serialVersionUID = -5844833125843247458L; + + public static final String NODE = "Policy"; + + @NotNull + private String name; + + @Override + public String getName() { + return name; + } + + @Override + public void setName(final String name) { + this.name = name; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPropagationPolicy.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPropagationPolicy.java new file mode 100644 index 0000000000..0321df735b --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPropagationPolicy.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.persistence.neo4j.entity.policy; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import java.util.Optional; +import org.apache.syncope.common.lib.types.BackOffStrategy; +import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jPropagationPolicy.NODE) +public class Neo4jPropagationPolicy extends Neo4jPolicy implements PropagationPolicy { + + private static final long serialVersionUID = 17400846199535L; + + public static final String NODE = "PropagationPolicy"; + + @NotNull + private Boolean fetchAroundProvisioning = true; + + @NotNull + private Boolean updateDelta = false; + + @NotNull + private BackOffStrategy backOffStrategy; + + private String backOffParams; + + @Min(1) + @NotNull + private Integer maxAttempts = 3; + + @Override + public boolean isFetchAroundProvisioning() { + return fetchAroundProvisioning; + } + + @Override + public void setFetchAroundProvisioning(final boolean fetchAroundProvisioning) { + this.fetchAroundProvisioning = fetchAroundProvisioning; + } + + @Override + public boolean isUpdateDelta() { + return updateDelta; + } + + @Override + public void setUpdateDelta(final boolean updateDelta) { + this.updateDelta = updateDelta; + } + + @Override + public BackOffStrategy getBackOffStrategy() { + return backOffStrategy; + } + + @Override + public void setBackOffStrategy(final BackOffStrategy backOffStrategy) { + this.backOffStrategy = backOffStrategy; + } + + @Override + public String getBackOffParams() { + return backOffParams; + } + + @Override + public void setBackOffParams(final String backOffParams) { + this.backOffParams = backOffParams; + } + + @Override + public int getMaxAttempts() { + return Optional.ofNullable(maxAttempts).orElse(3); + } + + @Override + public void setMaxAttempts(final int maxAttempts) { + this.maxAttempts = maxAttempts; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPullCorrelationRuleEntity.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPullCorrelationRuleEntity.java new file mode 100644 index 0000000000..2e0130ac01 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPullCorrelationRuleEntity.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.core.persistence.neo4j.entity.policy; + +import org.apache.syncope.common.lib.types.IdMImplementationType; +import org.apache.syncope.core.persistence.api.entity.policy.PullCorrelationRuleEntity; +import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jPullCorrelationRuleEntity.NODE) +public class Neo4jPullCorrelationRuleEntity extends AbstractCorrelationRuleEntity implements PullCorrelationRuleEntity { + + private static final long serialVersionUID = 4276417265524083919L; + + public static final String NODE = "PullCorrelationRuleEntity"; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jPullPolicy pullPolicy; + + @Override + protected String getImplementationType() { + return IdMImplementationType.PULL_CORRELATION_RULE; + } + + @Override + public PullPolicy getPullPolicy() { + return pullPolicy; + } + + @Override + public void setPullPolicy(final PullPolicy pullPolicy) { + checkType(pullPolicy, Neo4jPullPolicy.class); + this.pullPolicy = (Neo4jPullPolicy) pullPolicy; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPullPolicy.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPullPolicy.java new file mode 100644 index 0000000000..efdd0580c5 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPullPolicy.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.core.persistence.neo4j.entity.policy; + +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.types.ConflictResolutionAction; +import org.apache.syncope.core.persistence.api.entity.policy.PullCorrelationRuleEntity; +import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jPullPolicy.NODE) +public class Neo4jPullPolicy extends Neo4jPolicy implements PullPolicy { + + private static final long serialVersionUID = -6090413855809521279L; + + public static final String NODE = "PullPolicy"; + + @NotNull + private ConflictResolutionAction conflictResolutionAction; + + @Relationship(type = "PULL_POLICY", direction = Relationship.Direction.INCOMING) + private List correlationRules = new ArrayList<>(); + + @Override + public ConflictResolutionAction getConflictResolutionAction() { + return conflictResolutionAction; + } + + @Override + public void setConflictResolutionAction(final ConflictResolutionAction conflictResolutionAction) { + this.conflictResolutionAction = conflictResolutionAction; + } + + @Override + public boolean add(final PullCorrelationRuleEntity filter) { + checkType(filter, Neo4jPullCorrelationRuleEntity.class); + return this.correlationRules.add((Neo4jPullCorrelationRuleEntity) filter); + } + + @Override + public Optional getCorrelationRule(final String anyType) { + return correlationRules.stream(). + filter(rule -> anyType != null && anyType.equals(rule.getAnyType().getKey())).findFirst(); + } + + @Override + public List getCorrelationRules() { + return correlationRules; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPushCorrelationRuleEntity.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPushCorrelationRuleEntity.java new file mode 100644 index 0000000000..84ef1bb53b --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPushCorrelationRuleEntity.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.core.persistence.neo4j.entity.policy; + +import org.apache.syncope.common.lib.types.IdMImplementationType; +import org.apache.syncope.core.persistence.api.entity.policy.PushCorrelationRuleEntity; +import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jPushCorrelationRuleEntity.NODE) +public class Neo4jPushCorrelationRuleEntity extends AbstractCorrelationRuleEntity implements PushCorrelationRuleEntity { + + private static final long serialVersionUID = 4276417265524083919L; + + public static final String NODE = "PushCorrelationRuleEntity"; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jPushPolicy pushPolicy; + + @Override + protected String getImplementationType() { + return IdMImplementationType.PUSH_CORRELATION_RULE; + } + + @Override + public PushPolicy getPushPolicy() { + return pushPolicy; + } + + @Override + public void setPushPolicy(final PushPolicy pushPolicy) { + checkType(pushPolicy, Neo4jPushPolicy.class); + this.pushPolicy = (Neo4jPushPolicy) pushPolicy; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPushPolicy.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPushPolicy.java new file mode 100644 index 0000000000..07b929641b --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPushPolicy.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.core.persistence.neo4j.entity.policy; + +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.types.ConflictResolutionAction; +import org.apache.syncope.core.persistence.api.entity.policy.PushCorrelationRuleEntity; +import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jPushPolicy.NODE) +public class Neo4jPushPolicy extends Neo4jPolicy implements PushPolicy { + + private static final long serialVersionUID = -5875589156893921113L; + + public static final String NODE = "PushPolicy"; + + @NotNull + private ConflictResolutionAction conflictResolutionAction; + + @Relationship(type = "PUSH_POLICY", direction = Relationship.Direction.INCOMING) + private List correlationRules = new ArrayList<>(); + + @Override + public ConflictResolutionAction getConflictResolutionAction() { + return conflictResolutionAction; + } + + @Override + public void setConflictResolutionAction(final ConflictResolutionAction conflictResolutionAction) { + this.conflictResolutionAction = conflictResolutionAction; + } + + @Override + public boolean add(final PushCorrelationRuleEntity filter) { + checkType(filter, Neo4jPushCorrelationRuleEntity.class); + return this.correlationRules.add((Neo4jPushCorrelationRuleEntity) filter); + } + + @Override + public Optional getCorrelationRule(final String anyType) { + return correlationRules.stream(). + filter(rule -> anyType != null && anyType.equals(rule.getAnyType().getKey())).findFirst(); + } + + @Override + public List getCorrelationRules() { + return correlationRules; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jTicketExpirationPolicy.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jTicketExpirationPolicy.java new file mode 100644 index 0000000000..a06df9cf0d --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jTicketExpirationPolicy.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.persistence.neo4j.entity.policy; + +import java.util.Optional; +import org.apache.syncope.common.lib.policy.TicketExpirationPolicyConf; +import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jTicketExpirationPolicy.NODE) +public class Neo4jTicketExpirationPolicy extends Neo4jPolicy implements TicketExpirationPolicy { + + private static final long serialVersionUID = -4190607009908888884L; + + public static final String NODE = "TicketExpirationPolicy"; + + private String jsonConf; + + @Override + public TicketExpirationPolicyConf getConf() { + return Optional.ofNullable(jsonConf). + map(c -> POJOHelper.deserialize(c, TicketExpirationPolicyConf.class)). + orElse(null); + } + + @Override + public void setConf(final TicketExpirationPolicyConf conf) { + jsonConf = Optional.ofNullable(conf).map(POJOHelper::serialize).orElse(null); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/AbstractTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/AbstractTask.java new file mode 100644 index 0000000000..2a910624ff --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/AbstractTask.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.core.persistence.neo4j.entity.task; + +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.task.Task; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractGeneratedKeyNode; + +public abstract class AbstractTask> extends AbstractGeneratedKeyNode implements Task { + + private static final long serialVersionUID = 5837401178128177511L; + + protected abstract List> executions(); + + protected abstract Class> executionClass(); + + protected abstract boolean doAdd(TaskExec exec); + + @Override + public boolean add(final TaskExec exec) { + Class> clazz = executionClass(); + checkType(exec, clazz); + return exec != null + && !executions().contains(clazz.cast(exec)) + && doAdd(exec); + } + + @Override + public List> getExecs() { + return executions(); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/AbstractTaskExec.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/AbstractTaskExec.java new file mode 100644 index 0000000000..6be95f4bbd --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/AbstractTaskExec.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.persistence.neo4j.entity.task; + +import org.apache.syncope.core.persistence.api.entity.task.Task; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractExec; + +public abstract class AbstractTaskExec> extends AbstractExec implements TaskExec { + + private static final long serialVersionUID = 1909033231464074554L; + + @Override + public String toString() { + return new StringBuilder(getClass().getSimpleName()).append('{'). + append("id=").append(getKey()).append(", "). + append("start=").append(start).append(", "). + append("end=").append(end).append(", "). + append("task=").append(getTask()).append(", "). + append("status=").append(status).append(", "). + append("message=").append(message). + append('}'). + toString(); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jAnyTemplatePullTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jAnyTemplatePullTask.java new file mode 100644 index 0000000000..7f374fa04d --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jAnyTemplatePullTask.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.core.persistence.neo4j.entity.task; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask; +import org.apache.syncope.core.persistence.api.entity.task.PullTask; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractAnyTemplate; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jAnyTemplatePullTask.NODE) +public class Neo4jAnyTemplatePullTask extends AbstractAnyTemplate implements AnyTemplatePullTask { + + private static final long serialVersionUID = 3517381731849788407L; + + public static final String NODE = "AnyTemplatePullTask"; + + @NotNull + @Relationship(type = Neo4jPullTask.PULL_TASK_TEMPLATE_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jPullTask pullTask; + + @Override + public PullTask getPullTask() { + return pullTask; + } + + @Override + public void setPullTask(final PullTask pullTask) { + checkType(pullTask, Neo4jPullTask.class); + this.pullTask = (Neo4jPullTask) pullTask; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java new file mode 100644 index 0000000000..1a690a146e --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity.task; + +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; +import org.apache.syncope.common.lib.command.CommandArgs; +import org.apache.syncope.common.lib.types.IdRepoImplementationType; +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.task.MacroTask; +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.neo4j.entity.Neo4jImplementation; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.annotation.Transient; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.PostLoad; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jMacroTask.NODE) +public class Neo4jMacroTask extends Neo4jSchedTask implements MacroTask { + + private static final long serialVersionUID = 8261850094316787406L; + + public static final String NODE = "MacroTask"; + + public static final String MACRO_TASK_EXEC_REL = "MACRO_TASK_EXEC"; + + protected static final TypeReference> TYPEREF = new TypeReference>() { + }; + + @NotNull + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jRealm realm; + + @NotNull + private Boolean continueOnError = false; + + @NotNull + private Boolean saveExecs = true; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private List commands = new ArrayList<>(); + + private String commandArgs; + + @Transient + private final List commandArgsList = new ArrayList<>(); + + @Relationship(type = MACRO_TASK_EXEC_REL, direction = Relationship.Direction.INCOMING) + private List executions = new ArrayList<>(); + + @Override + public Realm getRealm() { + return realm; + } + + @Override + public void setRealm(final Realm realm) { + checkType(realm, Neo4jRealm.class); + this.realm = (Neo4jRealm) realm; + } + + @Override + public void add(final Implementation command, final CommandArgs args) { + checkType(command, Neo4jImplementation.class); + checkImplementationType(command, IdRepoImplementationType.COMMAND); + commands.add((Neo4jImplementation) command); + + getCommandArgs().add(args); + } + + @Override + public List getCommands() { + return commands; + } + + @Override + public List getCommandArgs() { + return commandArgsList; + } + + @Override + public boolean isContinueOnError() { + return continueOnError == null ? false : continueOnError; + } + + @Override + public void setContinueOnError(final boolean continueOnError) { + this.continueOnError = continueOnError; + } + + @Override + public boolean isSaveExecs() { + return saveExecs == null ? true : saveExecs; + } + + @Override + public void setSaveExecs(final boolean saveExecs) { + this.saveExecs = saveExecs; + } + + @Override + protected boolean doAdd(final TaskExec exec) { + return executions.add((Neo4jMacroTaskExec) exec); + } + + @Override + protected Class> executionClass() { + return Neo4jMacroTaskExec.class; + } + + @Override + protected List> executions() { + return executions; + } + + protected void json2list(final boolean clearFirst) { + if (clearFirst) { + getCommandArgs().clear(); + } + if (commandArgs != null) { + getCommandArgs().addAll(POJOHelper.deserialize(commandArgs, TYPEREF)); + } + } + + @PostLoad + public void postLoad() { + json2list(false); + } + + public void postSave() { + json2list(true); + } + + public void list2json() { + commandArgs = POJOHelper.serialize(getCommandArgs(), TYPEREF); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTaskExec.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTaskExec.java new file mode 100644 index 0000000000..e1a7a996a0 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTaskExec.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.core.persistence.neo4j.entity.task; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.task.MacroTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jMacroTaskExec.NODE) +public class Neo4jMacroTaskExec extends AbstractTaskExec implements TaskExec { + + private static final long serialVersionUID = 1909033231464074554L; + + public static final String NODE = "MacroTaskExec"; + + @NotNull + @Relationship(type = Neo4jMacroTask.MACRO_TASK_EXEC_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jMacroTask task; + + @Override + public MacroTask getTask() { + return task; + } + + @Override + public void setTask(final SchedTask task) { + checkType(task, MacroTask.class); + this.task = (Neo4jMacroTask) task; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jNotificationTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jNotificationTask.java new file mode 100644 index 0000000000..17c5b4ebd7 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jNotificationTask.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.core.persistence.neo4j.entity.task; + +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.TraceLevel; +import org.apache.syncope.core.persistence.api.entity.Notification; +import org.apache.syncope.core.persistence.api.entity.task.NotificationTask; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jNotification; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.annotation.Transient; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.PostLoad; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jNotificationTask.NODE) +public class Neo4jNotificationTask extends AbstractTask implements NotificationTask { + + private static final long serialVersionUID = 95731573485279180L; + + public static final String NODE = "NotificationTask"; + + public static final String NOTIFICATION_TASK_EXEC_REL = "NOTIFICATION_TASK_EXEC"; + + protected static final TypeReference> TYPEREF = new TypeReference>() { + }; + + @NotNull + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jNotification notification; + + private AnyTypeKind anyTypeKind; + + private String entityKey; + + private String recipients; + + @Transient + private Set recipientsSet = new HashSet<>(); + + @Relationship(type = NOTIFICATION_TASK_EXEC_REL, direction = Relationship.Direction.INCOMING) + private List executions = new ArrayList<>(); + + @NotNull + private String sender; + + @NotNull + private String subject; + + @NotNull + private String textBody; + + @NotNull + private String htmlBody; + + @NotNull + private Boolean executed = false; + + @NotNull + private TraceLevel traceLevel; + + @Override + public Notification getNotification() { + return notification; + } + + @Override + public void setNotification(final Notification notification) { + checkType(notification, Neo4jNotification.class); + this.notification = (Neo4jNotification) notification; + } + + @Override + public AnyTypeKind getAnyTypeKind() { + return anyTypeKind; + } + + @Override + public void setAnyTypeKind(final AnyTypeKind anyTypeKind) { + this.anyTypeKind = anyTypeKind; + } + + @Override + public String getEntityKey() { + return entityKey; + } + + @Override + public void setEntityKey(final String entityKey) { + this.entityKey = entityKey; + } + + @Override + public Set getRecipients() { + return recipientsSet; + } + + @Override + public String getSender() { + return sender; + } + + @Override + public void setSender(final String sender) { + this.sender = sender; + } + + @Override + public String getSubject() { + return subject; + } + + @Override + public void setSubject(final String subject) { + this.subject = subject; + } + + @Override + public String getTextBody() { + return textBody; + } + + @Override + public void setTextBody(final String textBody) { + this.textBody = textBody; + } + + @Override + public String getHtmlBody() { + return htmlBody; + } + + @Override + public void setHtmlBody(final String htmlBody) { + this.htmlBody = htmlBody; + } + + @Override + public boolean isExecuted() { + return executed; + } + + @Override + public void setExecuted(final boolean executed) { + this.executed = executed; + } + + @Override + public TraceLevel getTraceLevel() { + return traceLevel; + } + + @Override + public void setTraceLevel(final TraceLevel traceLevel) { + this.traceLevel = traceLevel; + } + + @Override + protected boolean doAdd(final TaskExec exec) { + return executions.add((Neo4jNotificationTaskExec) exec); + } + + @Override + protected Class> executionClass() { + return Neo4jNotificationTaskExec.class; + } + + @Override + protected List> executions() { + return executions; + } + + protected void json2list(final boolean clearFirst) { + if (clearFirst) { + getRecipients().clear(); + } + if (recipients != null) { + getRecipients().addAll(POJOHelper.deserialize(recipients, TYPEREF)); + } + } + + @PostLoad + public void postLoad() { + json2list(false); + } + + public void postSave() { + json2list(true); + } + + public void list2json() { + recipients = POJOHelper.serialize(getRecipients()); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jNotificationTaskExec.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jNotificationTaskExec.java new file mode 100644 index 0000000000..dd102ea8aa --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jNotificationTaskExec.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.persistence.neo4j.entity.task; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.task.NotificationTask; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jNotificationTaskExec.NODE) +public class Neo4jNotificationTaskExec + extends AbstractTaskExec + implements TaskExec { + + private static final long serialVersionUID = 1909033231464074554L; + + public static final String NODE = "NotificationTaskExec"; + + @NotNull + @Relationship(type = Neo4jNotificationTask.NOTIFICATION_TASK_EXEC_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jNotificationTask task; + + @Override + public NotificationTask getTask() { + return task; + } + + @Override + public void setTask(final NotificationTask task) { + checkType(task, NotificationTask.class); + this.task = (Neo4jNotificationTask) task; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPropagationTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPropagationTask.java new file mode 100644 index 0000000000..8112054ddd --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPropagationTask.java @@ -0,0 +1,201 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity.task; + +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.ResourceOperation; +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.PropagationTask; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.apache.syncope.core.persistence.common.validation.PropagationTaskCheck; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +/** + * Encapsulate all information about a propagation task. + */ +@Node(Neo4jPropagationTask.NODE) +@PropagationTaskCheck +public class Neo4jPropagationTask extends AbstractTask implements PropagationTask { + + private static final long serialVersionUID = 7086054884614511210L; + + public static final String NODE = "PropagationTask"; + + public static final String PROPAGATION_TASK_EXEC_REL = "PROPAGATION_TASK_EXEC"; + + /** + * @see ResourceOperation + */ + private ResourceOperation operation; + + /** + * The connObjectKey on the external resource. + */ + private String connObjectKey; + + /** + * The (optional) former connObjectKey on the external resource. + */ + private String oldConnObjectKey; + + /** + * Data to be propagated. + */ + private String propagationData; + + private String objectClassName; + + private AnyTypeKind anyTypeKind; + + private String anyType; + + private String entityKey; + + /** + * ExternalResource to which the propagation happens. + */ + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jExternalResource resource; + + @Relationship(type = PROPAGATION_TASK_EXEC_REL, direction = Relationship.Direction.INCOMING) + private List executions = new ArrayList<>(); + + @Override + public String getConnObjectKey() { + return connObjectKey; + } + + @Override + public void setConnObjectKey(final String connObjectKey) { + this.connObjectKey = connObjectKey; + } + + @Override + public String getOldConnObjectKey() { + return oldConnObjectKey; + } + + @Override + public void setOldConnObjectKey(final String oldConnObjectKey) { + this.oldConnObjectKey = oldConnObjectKey; + } + + @Override + public String getSerializedPropagationData() { + return propagationData; + } + + @Override + public PropagationData getPropagationData() { + PropagationData result = null; + if (StringUtils.isNotBlank(propagationData)) { + result = POJOHelper.deserialize(propagationData, PropagationData.class); + } + return result; + } + + @Override + public void setPropagationData(final PropagationData propagationData) { + this.propagationData = POJOHelper.serialize(propagationData); + } + + @Override + public ResourceOperation getOperation() { + return operation; + } + + @Override + + public void setOperation(final ResourceOperation operation) { + this.operation = operation; + } + + @Override + public String getObjectClassName() { + return objectClassName; + } + + @Override + public void setObjectClassName(final String objectClassName) { + this.objectClassName = objectClassName; + } + + @Override + public AnyTypeKind getAnyTypeKind() { + return anyTypeKind; + } + + @Override + public void setAnyTypeKind(final AnyTypeKind anyTypeKind) { + this.anyTypeKind = anyTypeKind; + } + + @Override + public String getAnyType() { + return anyType; + } + + @Override + public void setAnyType(final String anyType) { + this.anyType = anyType; + } + + @Override + public String getEntityKey() { + return entityKey; + } + + @Override + public void setEntityKey(final String entityKey) { + this.entityKey = entityKey; + } + + @Override + public ExternalResource getResource() { + return resource; + } + + @Override + public void setResource(final ExternalResource resource) { + checkType(resource, Neo4jExternalResource.class); + this.resource = (Neo4jExternalResource) resource; + } + + @Override + protected boolean doAdd(final TaskExec exec) { + return executions.add((Neo4jPropagationTaskExec) exec); + } + + @Override + protected Class> executionClass() { + return Neo4jPropagationTaskExec.class; + } + + @Override + protected List> executions() { + return executions; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPropagationTaskExec.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPropagationTaskExec.java new file mode 100644 index 0000000000..bc26fc1925 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPropagationTaskExec.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.core.persistence.neo4j.entity.task; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jPropagationTaskExec.NODE) +public class Neo4jPropagationTaskExec extends AbstractTaskExec implements TaskExec { + + private static final long serialVersionUID = 1909033231464074554L; + + public static final String NODE = "PropagationTaskExec"; + + @NotNull + @Relationship(type = Neo4jPropagationTask.PROPAGATION_TASK_EXEC_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jPropagationTask task; + + @Override + public PropagationTask getTask() { + return task; + } + + @Override + public void setTask(final PropagationTask task) { + checkType(task, PropagationTask.class); + this.task = (Neo4jPropagationTask) task; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jProvisioningTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jProvisioningTask.java new file mode 100644 index 0000000000..94cad26d4c --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jProvisioningTask.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.persistence.neo4j.entity.task; + +import jakarta.validation.constraints.NotNull; +import java.util.Optional; +import org.apache.syncope.common.lib.types.MatchingRule; +import org.apache.syncope.common.lib.types.ThreadPoolSettings; +import org.apache.syncope.common.lib.types.UnmatchingRule; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.common.validation.ProvisioningTaskCheck; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jProvisioningTask.NODE) +@ProvisioningTaskCheck +public abstract class Neo4jProvisioningTask + extends Neo4jSchedTask implements ProvisioningTask { + + private static final long serialVersionUID = -4141057723006682562L; + + public static final String NODE = "ProvisioningTask"; + + @NotNull + private Boolean performCreate = false; + + @NotNull + private Boolean performUpdate = false; + + @NotNull + private Boolean performDelete = false; + + @NotNull + private Boolean syncStatus = false; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jExternalResource resource; + + /** + * @see UnmatchingRule + */ + @NotNull + protected UnmatchingRule unmatchingRule; + + /** + * @see MatchingRule + */ + @NotNull + protected MatchingRule matchingRule; + + protected String concurrentSettings; + + @Override + public boolean isPerformCreate() { + return performCreate; + } + + @Override + + public void setPerformCreate(final boolean performCreate) { + this.performCreate = performCreate; + } + + @Override + + public boolean isPerformUpdate() { + return performUpdate; + } + + @Override + + public void setPerformUpdate(final boolean performUpdate) { + this.performUpdate = performUpdate; + } + + @Override + public boolean isPerformDelete() { + return performDelete; + } + + @Override + public void setPerformDelete(final boolean performDelete) { + this.performDelete = performDelete; + } + + @Override + public boolean isSyncStatus() { + return syncStatus; + } + + @Override + public void setSyncStatus(final boolean syncStatus) { + this.syncStatus = syncStatus; + } + + @Override + public UnmatchingRule getUnmatchingRule() { + return this.unmatchingRule; + } + + @Override + public void setUnmatchingRule(final UnmatchingRule unmatchigRule) { + this.unmatchingRule = unmatchigRule; + } + + @Override + public MatchingRule getMatchingRule() { + return this.matchingRule; + } + + @Override + public void setMatchingRule(final MatchingRule matchigRule) { + this.matchingRule = matchigRule; + } + + @Override + public ThreadPoolSettings getConcurrentSettings() { + return Optional.ofNullable(concurrentSettings). + map(s -> POJOHelper.deserialize(s, ThreadPoolSettings.class)).orElse(null); + } + + @Override + public void setConcurrentSettings(final ThreadPoolSettings settings) { + this.concurrentSettings = Optional.ofNullable(settings).map(POJOHelper::serialize).orElse(null); + } + + @Override + public ExternalResource getResource() { + return resource; + } + + @Override + public void setResource(final ExternalResource resource) { + checkType(resource, Neo4jExternalResource.class); + this.resource = (Neo4jExternalResource) resource; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPullTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPullTask.java new file mode 100644 index 0000000000..c9c00a2839 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPullTask.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.persistence.neo4j.entity.task; + +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.types.IdMImplementationType; +import org.apache.syncope.common.lib.types.PullMode; +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.task.AnyTemplatePullTask; +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.TaskExec; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementation; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jPullTask.NODE) +public class Neo4jPullTask extends Neo4jProvisioningTask implements PullTask { + + private static final long serialVersionUID = -4141057723006682563L; + + public static final String NODE = "PullTask"; + + public static final String PULL_TASK_PULL_ACTIONS_REL = "PULL_TASK_PULL_ACTIONS"; + + public static final String PULL_TASK_TEMPLATE_REL = "PULL_TASK_TEMPLATE"; + + public static final String PULL_TASK_EXEC_REL = "PULL_TASK_EXEC"; + + @NotNull + private PullMode pullMode; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jImplementation reconFilterBuilder; + + @NotNull + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jRealm destinationRealm; + + @Relationship(type = PULL_TASK_PULL_ACTIONS_REL, direction = Relationship.Direction.OUTGOING) + private List actions = new ArrayList<>(); + + @Relationship(type = PULL_TASK_TEMPLATE_REL, direction = Relationship.Direction.INCOMING) + private List templates = new ArrayList<>(); + + @Relationship(type = PULL_TASK_EXEC_REL, direction = Relationship.Direction.INCOMING) + private List executions = new ArrayList<>(); + + @NotNull + private Boolean remediation = false; + + @Override + public PullMode getPullMode() { + return pullMode; + } + + @Override + public void setPullMode(final PullMode pullMode) { + this.pullMode = pullMode; + } + + @Override + public Implementation getReconFilterBuilder() { + return reconFilterBuilder; + } + + @Override + public void setReconFilterBuilder(final Implementation reconFilterBuilder) { + checkType(reconFilterBuilder, Neo4jImplementation.class); + checkImplementationType(reconFilterBuilder, IdMImplementationType.RECON_FILTER_BUILDER); + this.reconFilterBuilder = (Neo4jImplementation) reconFilterBuilder; + } + + @Override + public Realm getDestinationRealm() { + return destinationRealm; + } + + @Override + public void setDestinationRealm(final Realm destinationRealm) { + checkType(destinationRealm, Neo4jRealm.class); + this.destinationRealm = (Neo4jRealm) destinationRealm; + } + + @Override + public boolean add(final Implementation action) { + checkType(action, Neo4jImplementation.class); + checkImplementationType(action, IdMImplementationType.PULL_ACTIONS); + return actions.contains((Neo4jImplementation) action) || actions.add((Neo4jImplementation) action); + } + + @Override + public List getActions() { + return actions; + } + + @Override + public boolean add(final AnyTemplatePullTask template) { + checkType(template, Neo4jAnyTemplatePullTask.class); + return this.templates.add((Neo4jAnyTemplatePullTask) template); + } + + @Override + public Optional getTemplate(final String anyType) { + return templates.stream(). + filter(template -> anyType != null && anyType.equals(template.getAnyType().getKey())). + findFirst(); + } + + @Override + public List getTemplates() { + return templates; + } + + @Override + public void setRemediation(final boolean remediation) { + this.remediation = remediation; + } + + @Override + public boolean isRemediation() { + return concurrentSettings != null ? true : remediation; + } + + @Override + protected boolean doAdd(final TaskExec exec) { + return executions.add((Neo4jPullTaskExec) exec); + } + + @Override + protected Class> executionClass() { + return Neo4jPullTaskExec.class; + } + + @Override + protected List> executions() { + return executions; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPullTaskExec.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPullTaskExec.java new file mode 100644 index 0000000000..ee0c954c0c --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPullTaskExec.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.persistence.neo4j.entity.task; + +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.TaskExec; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jPullTaskExec.NODE) +public class Neo4jPullTaskExec extends AbstractTaskExec implements TaskExec { + + private static final long serialVersionUID = 1909033231464074554L; + + public static final String NODE = "PullTaskExec"; + + @Relationship(type = Neo4jPullTask.PULL_TASK_EXEC_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jPullTask task; + + @Override + public PullTask getTask() { + return task; + } + + @Override + public void setTask(final SchedTask task) { + checkType(task, PullTask.class); + this.task = (Neo4jPullTask) task; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPushTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPushTask.java new file mode 100644 index 0000000000..7403d4ecbe --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPushTask.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.neo4j.entity.task; + +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.apache.syncope.common.lib.types.IdMImplementationType; +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.task.PushTask; +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.neo4j.entity.Neo4jImplementation; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.annotation.Transient; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.PostLoad; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jPushTask.NODE) +public class Neo4jPushTask extends Neo4jProvisioningTask implements PushTask { + + private static final long serialVersionUID = -4141057723006682564L; + + public static final String NODE = "PushTask"; + + public static final String PUSH_TASK_PUSH_ACTIONS_REL = "PUSH_TASK_PUSH_ACTIONS"; + + public static final String PUSH_TASK_EXEC_REL = "PUSH_TASK_EXEC"; + + protected static final TypeReference> FILTER_TYPEREF = + new TypeReference>() { + }; + + @NotNull + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jRealm sourceRealm; + + private String filters; + + @Transient + private Map filterMap = new HashMap<>(); + + @Relationship(type = PUSH_TASK_PUSH_ACTIONS_REL, direction = Relationship.Direction.OUTGOING) + private List actions = new ArrayList<>(); + + @Relationship(type = PUSH_TASK_EXEC_REL, direction = Relationship.Direction.INCOMING) + private List executions = new ArrayList<>(); + + @Override + public Neo4jRealm getSourceRealm() { + return sourceRealm; + } + + @Override + public void setSourceRealm(final Realm sourceRealm) { + checkType(sourceRealm, Neo4jRealm.class); + this.sourceRealm = (Neo4jRealm) sourceRealm; + } + + @Override + public boolean add(final Implementation action) { + checkType(action, Neo4jImplementation.class); + checkImplementationType(action, IdMImplementationType.PUSH_ACTIONS); + return actions.contains((Neo4jImplementation) action) || actions.add((Neo4jImplementation) action); + } + + @Override + public List getActions() { + return actions; + } + + @Override + public Optional getFilter(final String anyType) { + return Optional.ofNullable(filterMap.get(anyType)); + } + + @Override + public Map getFilters() { + return filterMap; + } + + @Override + protected boolean doAdd(final TaskExec exec) { + return executions.add((Neo4jPushTaskExec) exec); + } + + @Override + protected Class> executionClass() { + return Neo4jPushTaskExec.class; + } + + @Override + protected List> executions() { + return executions; + } + + protected void json2map(final boolean clearFirst) { + if (clearFirst) { + getFilters().clear(); + } + if (filters != null) { + getFilters().putAll(POJOHelper.deserialize(filters, FILTER_TYPEREF)); + } + } + + @PostLoad + public void postLoad() { + json2map(false); + } + + public void postSave() { + json2map(true); + } + + public void map2json() { + filters = POJOHelper.serialize(getFilters()); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPushTaskExec.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPushTaskExec.java new file mode 100644 index 0000000000..6f1e7526e4 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPushTaskExec.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.core.persistence.neo4j.entity.task; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.task.PushTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jPushTaskExec.NODE) +public class Neo4jPushTaskExec extends AbstractTaskExec implements TaskExec { + + private static final long serialVersionUID = 1909033231464074554L; + + public static final String NODE = "PushTaskExec"; + + @NotNull + @Relationship(type = Neo4jPushTask.PUSH_TASK_EXEC_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jPushTask task; + + @Override + public PushTask getTask() { + return task; + } + + @Override + public void setTask(final SchedTask task) { + checkType(task, PushTask.class); + this.task = (Neo4jPushTask) task; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jSchedTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jSchedTask.java new file mode 100644 index 0000000000..f73ebbe4a8 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jSchedTask.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.neo4j.entity.task; + +import jakarta.validation.constraints.NotNull; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.List; +import org.apache.syncope.common.lib.types.IdRepoImplementationType; +import org.apache.syncope.core.persistence.api.entity.Implementation; +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.common.validation.SchedTaskCheck; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementation; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jSchedTask.NODE) +@SchedTaskCheck +public class Neo4jSchedTask extends AbstractTask implements SchedTask { + + private static final long serialVersionUID = 7596236684832602180L; + + public static final String NODE = "SchedTask"; + + public static final String SCHED_TASK_JOB_DELEGATE_REL = "SCHED_TASK_JOB_DELEGATE"; + + public static final String SCHED_TASK_EXEC_REL = "SCHED_TASK_EXEC"; + + @NotNull + @Relationship(type = SCHED_TASK_JOB_DELEGATE_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jImplementation jobDelegate; + + private OffsetDateTime startAt; + + private String cronExpression; + + @NotNull + private String name; + + private String description; + + @NotNull + private Boolean active = true; + + @Relationship(type = SCHED_TASK_EXEC_REL, direction = Relationship.Direction.INCOMING) + private List executions = new ArrayList<>(); + + @Override + public Implementation getJobDelegate() { + return jobDelegate; + } + + @Override + public void setJobDelegate(final Implementation jobDelegate) { + checkType(jobDelegate, Neo4jImplementation.class); + checkImplementationType(jobDelegate, IdRepoImplementationType.TASKJOB_DELEGATE); + this.jobDelegate = (Neo4jImplementation) jobDelegate; + } + + @Override + public OffsetDateTime getStartAt() { + return startAt; + } + + @Override + public void setStartAt(final OffsetDateTime startAt) { + this.startAt = startAt; + } + + @Override + public String getCronExpression() { + return cronExpression; + } + + @Override + public void setCronExpression(final String cronExpression) { + this.cronExpression = cronExpression; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(final String description) { + this.description = description; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(final String name) { + this.name = name; + } + + @Override + public boolean isActive() { + return active; + } + + @Override + public void setActive(final boolean active) { + this.active = active; + } + + @Override + protected boolean doAdd(final TaskExec exec) { + return executions.add((Neo4jSchedTaskExec) exec); + } + + @Override + protected Class> executionClass() { + return Neo4jSchedTaskExec.class; + } + + @Override + protected List> executions() { + return executions; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jSchedTaskExec.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jSchedTaskExec.java new file mode 100644 index 0000000000..61b8feffeb --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jSchedTaskExec.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.core.persistence.neo4j.entity.task; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jSchedTaskExec.NODE) +public class Neo4jSchedTaskExec extends AbstractTaskExec implements TaskExec { + + private static final long serialVersionUID = 1909033231464074554L; + + public static final String NODE = "SchedTaskExec"; + + @NotNull + @Relationship(type = Neo4jSchedTask.SCHED_TASK_EXEC_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jSchedTask task; + + @Override + public SchedTask getTask() { + return task; + } + + @Override + public void setTask(final SchedTask task) { + checkType(task, SchedTask.class); + this.task = (Neo4jSchedTask) task; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jTaskUtils.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jTaskUtils.java new file mode 100644 index 0000000000..89ea92a48a --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jTaskUtils.java @@ -0,0 +1,352 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity.task; + +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.apache.syncope.common.lib.to.TaskTO; +import org.apache.syncope.common.lib.types.TaskType; +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.PullTask; +import org.apache.syncope.core.persistence.api.entity.task.PushTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.api.entity.task.Task; +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.spring.security.SecureRandomUtils; + +@SuppressWarnings("unchecked") +public final class Neo4jTaskUtils implements TaskUtils { + + protected final TaskType type; + + protected Neo4jTaskUtils(final TaskType type) { + this.type = type; + } + + @Override + public TaskType getType() { + return type; + } + + @Override + public > Class taskClass() { + Class result = null; + + switch (type) { + case PROPAGATION: + result = (Class) PropagationTask.class; + break; + + case SCHEDULED: + result = (Class) SchedTask.class; + break; + + case PULL: + result = (Class) PullTask.class; + break; + + case PUSH: + result = (Class) PushTask.class; + break; + + case MACRO: + result = (Class) MacroTask.class; + break; + + case NOTIFICATION: + result = (Class) NotificationTask.class; + break; + + default: + } + + return result; + } + + @Override + public > T newTask() { + T result = null; + + switch (type) { + case PROPAGATION: + result = (T) new Neo4jPropagationTask(); + break; + + case SCHEDULED: + result = (T) new Neo4jSchedTask(); + break; + + case PULL: + result = (T) new Neo4jPullTask(); + break; + + case PUSH: + result = (T) new Neo4jPushTask(); + break; + + case MACRO: + result = (T) new Neo4jMacroTask(); + break; + + case NOTIFICATION: + result = (T) new Neo4jNotificationTask(); + break; + + default: + } + + if (result != null) { + ((AbstractTask) result).setKey(SecureRandomUtils.generateRandomUUID().toString()); + } + + return result; + } + + @Override + public > E newTaskExec() { + E result; + + switch (type) { + case NOTIFICATION: + result = (E) new Neo4jNotificationTaskExec(); + break; + + case PROPAGATION: + result = (E) new Neo4jPropagationTaskExec(); + break; + + case PULL: + result = (E) new Neo4jPullTaskExec(); + break; + + case PUSH: + result = (E) new Neo4jPushTaskExec(); + break; + + case MACRO: + result = (E) new Neo4jMacroTaskExec(); + break; + + case SCHEDULED: + result = (E) new Neo4jSchedTaskExec(); + break; + + default: + result = null; + } + + if (result != null) { + ((AbstractTaskExec) result).setKey(SecureRandomUtils.generateRandomUUID().toString()); + } + + return result; + } + + @Override + public Class taskTOClass() { + Class result = null; + + switch (type) { + case PROPAGATION: + result = (Class) PropagationTaskTO.class; + break; + + case SCHEDULED: + result = (Class) SchedTaskTO.class; + break; + + case PULL: + result = (Class) PullTaskTO.class; + break; + + case PUSH: + result = (Class) PushTaskTO.class; + break; + + case MACRO: + result = (Class) MacroTaskTO.class; + break; + + case NOTIFICATION: + result = (Class) NotificationTaskTO.class; + break; + + default: + } + + return result; + } + + @Override + public T newTaskTO() { + Class taskClass = taskTOClass(); + try { + return taskClass == null ? null : taskClass.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + return null; + } + } + + @Override + public String getTaskTable() { + String result = null; + + switch (type) { + case NOTIFICATION: + result = Neo4jNotificationTask.NODE; + break; + + case PROPAGATION: + result = Neo4jPropagationTask.NODE; + break; + + case PUSH: + result = Neo4jPushTask.NODE; + break; + + case PULL: + result = Neo4jPullTask.NODE; + break; + + case MACRO: + result = Neo4jMacroTask.NODE; + break; + + case SCHEDULED: + result = Neo4jSchedTask.NODE; + break; + + default: + } + + return result; + } + + @Override + public Class> getTaskEntity() { + Class> result = null; + + switch (type) { + case NOTIFICATION: + result = Neo4jNotificationTask.class; + break; + + case PROPAGATION: + result = Neo4jPropagationTask.class; + break; + + case PUSH: + result = Neo4jPushTask.class; + break; + + case PULL: + result = Neo4jPullTask.class; + break; + + case MACRO: + result = Neo4jMacroTask.class; + break; + + case SCHEDULED: + result = Neo4jSchedTask.class; + break; + + default: + } + + return result; + } + + @Override + public String getTaskExecTable() { + String result = null; + + switch (type) { + case NOTIFICATION: + result = Neo4jNotificationTaskExec.NODE; + break; + + case PROPAGATION: + result = Neo4jPropagationTaskExec.NODE; + break; + + case SCHEDULED: + result = Neo4jSchedTaskExec.NODE; + break; + + case PUSH: + result = Neo4jPushTaskExec.NODE; + break; + + case PULL: + result = Neo4jPullTaskExec.NODE; + break; + + case MACRO: + result = Neo4jMacroTaskExec.NODE; + break; + + default: + } + + return result; + } + + @Override + public Class> getTaskExecEntity() { + Class> result = null; + + switch (type) { + case NOTIFICATION: + result = Neo4jNotificationTaskExec.class; + break; + + case PROPAGATION: + result = Neo4jPropagationTaskExec.class; + break; + + case SCHEDULED: + result = Neo4jSchedTaskExec.class; + break; + + case PUSH: + result = Neo4jPushTaskExec.class; + break; + + case PULL: + result = Neo4jPullTaskExec.class; + break; + + case MACRO: + result = Neo4jMacroTaskExec.class; + break; + + default: + } + + return result; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jTaskUtilsFactory.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jTaskUtilsFactory.java new file mode 100644 index 0000000000..c8f00f7a35 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jTaskUtilsFactory.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.persistence.neo4j.entity.task; + +import java.util.HashMap; +import java.util.Map; +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.apache.syncope.common.lib.to.TaskTO; +import org.apache.syncope.common.lib.types.TaskType; +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.PullTask; +import org.apache.syncope.core.persistence.api.entity.task.PushTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.api.entity.task.Task; +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.spring.ApplicationContextProvider; + +public class Neo4jTaskUtilsFactory implements TaskUtilsFactory { + + protected final Map instances = new HashMap<>(5); + + @Override + public TaskUtils getInstance(final TaskType type) { + TaskUtils instance; + synchronized (instances) { + instance = instances.get(type); + if (instance == null) { + instance = new Neo4jTaskUtils(type); + ApplicationContextProvider.getBeanFactory().autowireBean(instance); + instances.put(type, instance); + } + } + + return instance; + } + + @Override + public TaskUtils getInstance(final Task task) { + TaskType type; + if (task instanceof PullTask) { + type = TaskType.PULL; + } else if (task instanceof PushTask) { + type = TaskType.PUSH; + } else if (task instanceof MacroTask) { + type = TaskType.MACRO; + } else if (task instanceof SchedTask) { + type = TaskType.SCHEDULED; + } else if (task instanceof PropagationTask) { + type = TaskType.PROPAGATION; + } else if (task instanceof NotificationTask) { + type = TaskType.NOTIFICATION; + } else { + throw new IllegalArgumentException("Invalid task: " + task); + } + + return getInstance(type); + } + + @Override + public TaskUtils getInstance(final Class taskClass) { + TaskType type; + if (taskClass == PropagationTaskTO.class) { + type = TaskType.PROPAGATION; + } else if (taskClass == NotificationTaskTO.class) { + type = TaskType.NOTIFICATION; + } else if (taskClass == SchedTaskTO.class) { + type = TaskType.SCHEDULED; + } else if (taskClass == PullTaskTO.class) { + type = TaskType.PULL; + } else if (taskClass == PushTaskTO.class) { + type = TaskType.PUSH; + } else if (taskClass == MacroTaskTO.class) { + type = TaskType.MACRO; + } else { + throw new IllegalArgumentException("Invalid TaskTO class: " + taskClass.getName()); + } + + return getInstance(type); + } + + @Override + public TaskUtils getInstance(final TaskTO taskTO) { + return getInstance(taskTO.getClass()); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jLAPlainAttr.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jLAPlainAttr.java new file mode 100644 index 0000000000..4523e4ff62 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jLAPlainAttr.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity.user; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import java.util.List; +import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.user.LAPlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.LAPlainAttrUniqueValue; +import org.apache.syncope.core.persistence.api.entity.user.LAPlainAttrValue; +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.persistence.neo4j.entity.AbstractPlainAttr; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jPlainAttr; + +public class Neo4jLAPlainAttr extends AbstractPlainAttr implements LAPlainAttr, Neo4jPlainAttr { + + private static final long serialVersionUID = 7827533741035423694L; + + /** + * The owner of this attribute. + */ + @JsonIgnore + private Neo4jUser owner; + + @JsonIgnore + private Neo4jLinkedAccount account; + + /** + * Values of this attribute (if schema is not UNIQUE). + */ + private List values = new ArrayList<>(); + + /** + * Value of this attribute (if schema is UNIQUE). + */ + @JsonProperty + private Neo4jLAPlainAttrUniqueValue uniqueValue; + + @Override + public User getOwner() { + return owner; + } + + @Override + public void setOwner(final User owner) { + checkType(owner, Neo4jUser.class); + this.owner = (Neo4jUser) owner; + } + + @Override + public LinkedAccount getAccount() { + return account; + } + + @Override + public void setAccount(final LinkedAccount account) { + checkType(account, Neo4jLinkedAccount.class); + this.account = (Neo4jLinkedAccount) account; + } + + @Override + protected boolean addForMultiValue(final PlainAttrValue attrValue) { + checkType(attrValue, Neo4jLAPlainAttrValue.class); + return values.add((Neo4jLAPlainAttrValue) attrValue); + } + + @Override + public boolean add(final PlainAttrValue value) { + return addForMultiValue(value); + } + + @Override + public List getValues() { + return values; + } + + @Override + public LAPlainAttrUniqueValue getUniqueValue() { + return uniqueValue; + } + + @JsonIgnore + @Override + public void setUniqueValue(final PlainAttrUniqueValue uniqueValue) { + checkType(uniqueValue, Neo4jLAPlainAttrUniqueValue.class); + this.uniqueValue = (Neo4jLAPlainAttrUniqueValue) uniqueValue; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jLAPlainAttrUniqueValue.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jLAPlainAttrUniqueValue.java new file mode 100644 index 0000000000..e4ea06d912 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jLAPlainAttrUniqueValue.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.persistence.neo4j.entity.user; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.user.LAPlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.LAPlainAttrUniqueValue; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractPlainAttrValue; + +public class Neo4jLAPlainAttrUniqueValue extends AbstractPlainAttrValue implements LAPlainAttrUniqueValue { + + private static final long serialVersionUID = 1200617357906733442L; + + public static final String TABLE = "LAPlainAttrUniqueValue"; + + @JsonIgnore + @NotNull + private Neo4jLAPlainAttr attr; + + @Override + public LAPlainAttr getAttr() { + return attr; + } + + @Override + public void setAttr(final PlainAttr attr) { + checkType(attr, Neo4jLAPlainAttr.class); + this.attr = (Neo4jLAPlainAttr) attr; + } + + @JsonIgnore + @Override + public PlainSchema getSchema() { + return getAttr() == null ? null : getAttr().getSchema(); + } + + @Override + public void setSchema(final PlainSchema schema) { + // nothing to do + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jLAPlainAttrValue.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jLAPlainAttrValue.java new file mode 100644 index 0000000000..d5df4730f6 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jLAPlainAttrValue.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.persistence.neo4j.entity.user; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.LAPlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.LAPlainAttrValue; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractPlainAttrValue; + +public class Neo4jLAPlainAttrValue extends AbstractPlainAttrValue implements LAPlainAttrValue { + + private static final long serialVersionUID = 6237793413044604262L; + + @JsonIgnore + @NotNull + private Neo4jLAPlainAttr attr; + + @Override + public LAPlainAttr getAttr() { + return attr; + } + + @Override + public void setAttr(final PlainAttr attr) { + checkType(attr, Neo4jLAPlainAttr.class); + this.attr = (Neo4jLAPlainAttr) attr; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jLinkedAccount.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jLinkedAccount.java new file mode 100644 index 0000000000..52a7b37ea6 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jLinkedAccount.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.persistence.neo4j.entity.user; + +import jakarta.validation.constraints.NotNull; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import org.apache.syncope.common.keymaster.client.api.ConfParamOps; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.Privilege; +import org.apache.syncope.core.persistence.api.entity.user.LAPlainAttr; +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.persistence.neo4j.entity.AbstractGeneratedKeyNode; +import org.apache.syncope.core.persistence.neo4j.entity.AttributableCheck; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAttributable; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jPrivilege; +import org.apache.syncope.core.spring.ApplicationContextProvider; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.apache.syncope.core.spring.security.Encryptor; +import org.springframework.data.neo4j.core.schema.CompositeProperty; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.PostLoad; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jLinkedAccount.NODE) +@AttributableCheck +public class Neo4jLinkedAccount extends AbstractGeneratedKeyNode implements LinkedAccount, Neo4jAttributable { + + private static final long serialVersionUID = -5141654998687601522L; + + public static final String NODE = "LinkedAccount"; + + private static final Encryptor ENCRYPTOR = Encryptor.getInstance(); + + @NotNull + private String connObjectKeyValue; + + @NotNull + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jUser owner; + + @NotNull + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jExternalResource resource; + + private String username; + + private CipherAlgorithm cipherAlgorithm; + + private String password; + + private Boolean suspended = false; + + @CompositeProperty(converterRef = "laPlainAttrsConverter") + protected Map plainAttrs = new HashMap<>(); + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Set privileges = new HashSet<>(); + + @Override + public String getConnObjectKeyValue() { + return connObjectKeyValue; + } + + @Override + public void setConnObjectKeyValue(final String connObjectKeyValue) { + this.connObjectKeyValue = connObjectKeyValue; + } + + @Override + public User getOwner() { + return owner; + } + + @Override + public void setOwner(final User owner) { + checkType(owner, Neo4jUser.class); + this.owner = (Neo4jUser) owner; + } + + @Override + public ExternalResource getResource() { + return resource; + } + + @Override + public void setResource(final ExternalResource resource) { + checkType(resource, Neo4jExternalResource.class); + this.resource = (Neo4jExternalResource) resource; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public void setUsername(final String username) { + this.username = username; + } + + @Override + public CipherAlgorithm getCipherAlgorithm() { + return cipherAlgorithm; + } + + @Override + public void setCipherAlgorithm(final CipherAlgorithm cipherAlgorithm) { + if (this.cipherAlgorithm == null || cipherAlgorithm == null) { + this.cipherAlgorithm = cipherAlgorithm; + } else { + throw new IllegalArgumentException("Cannot override existing cipher algorithm"); + } + } + + @Override + public boolean canDecodeSecrets() { + return this.cipherAlgorithm != null && this.cipherAlgorithm.isInvertible(); + } + + @Override + public String getPassword() { + return password; + } + + @Override + public void setEncodedPassword(final String password, final CipherAlgorithm cipherAlgoritm) { + this.password = password; + this.cipherAlgorithm = cipherAlgoritm; + } + + @Override + public void setPassword(final String password) { + try { + this.password = ENCRYPTOR.encode(password, cipherAlgorithm == null + ? CipherAlgorithm.valueOf(ApplicationContextProvider.getBeanFactory().getBean(ConfParamOps.class). + get(AuthContextUtils.getDomain(), "password.cipher.algorithm", CipherAlgorithm.AES.name(), + String.class)) + : cipherAlgorithm); + } catch (Exception e) { + LOG.error("Could not encode password", e); + this.password = null; + } + } + + @Override + public void setSuspended(final Boolean suspended) { + this.suspended = suspended; + } + + @Override + public Boolean isSuspended() { + return suspended; + } + + @Override + public boolean add(final LAPlainAttr attr) { + checkType(attr, Neo4jLAPlainAttr.class); + return plainAttrs.put(attr.getSchema().getKey(), (Neo4jLAPlainAttr) attr) != null; + } + + @Override + public boolean remove(final LAPlainAttr attr) { + checkType(attr, Neo4jLAPlainAttr.class); + return plainAttrs.remove(attr.getSchema().getKey()) != null; + } + + @Override + public Optional getPlainAttr(final String plainSchema) { + return Optional.ofNullable(plainAttrs.get(plainSchema)); + } + + @Override + public List getPlainAttrs() { + return plainAttrs.entrySet().stream(). + sorted(Comparator.comparing(Map.Entry::getKey)). + map(Map.Entry::getValue).toList(); + } + + @Override + public boolean add(final Privilege privilege) { + checkType(privilege, Neo4jPrivilege.class); + return privileges.add((Neo4jPrivilege) privilege); + } + + @Override + public Set getPrivileges() { + return privileges; + } + + @PostLoad + public void completePlainAttrs() { + for (var itor = plainAttrs.entrySet().iterator(); itor.hasNext();) { + var entry = itor.next(); + String schema = entry.getKey(); + Neo4jLAPlainAttr attr = entry.getValue(); + + attr.setSchemaKey(schema); + if (attr.getSchema() == null) { + itor.remove(); + } else { + attr.setOwner(getOwner()); + attr.setAccount(this); + attr.getValues().forEach(value -> value.setAttr(attr)); + Optional.ofNullable(attr.getUniqueValue()).ifPresent(value -> value.setAttr(attr)); + } + } + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jSecurityQuestion.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jSecurityQuestion.java new file mode 100644 index 0000000000..af2e79db06 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jSecurityQuestion.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.persistence.neo4j.entity.user; + +import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractGeneratedKeyNode; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jSecurityQuestion.NODE) +public class Neo4jSecurityQuestion extends AbstractGeneratedKeyNode implements SecurityQuestion { + + private static final long serialVersionUID = 7675321820453579744L; + + public static final String NODE = "SecurityQuestion"; + + private String content; + + @Override + public String getContent() { + return content; + } + + @Override + public void setContent(final String content) { + this.content = content; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPADynRoleMembership.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUDynGroupMembership.java similarity index 53% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPADynRoleMembership.java rename to core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUDynGroupMembership.java index 4a84cd9a9a..1b0aa52829 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPADynRoleMembership.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUDynGroupMembership.java @@ -16,37 +16,32 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.entity.user; +package org.apache.syncope.core.persistence.neo4j.entity.user; -import jakarta.persistence.Entity; -import jakarta.persistence.OneToOne; -import jakarta.persistence.Table; -import org.apache.syncope.core.persistence.api.entity.Role; -import org.apache.syncope.core.persistence.api.entity.user.DynRoleMembership; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; import org.apache.syncope.core.persistence.api.entity.user.User; -import org.apache.syncope.core.persistence.jpa.entity.AbstractDynMembership; -import org.apache.syncope.core.persistence.jpa.entity.JPARole; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractDynMembership; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; +import org.springframework.data.neo4j.core.schema.Node; -@Entity -@Table(name = JPADynRoleMembership.TABLE) -public class JPADynRoleMembership extends AbstractDynMembership implements DynRoleMembership { +@Node(Neo4jUDynGroupMembership.NODE) +public class Neo4jUDynGroupMembership extends AbstractDynMembership implements UDynGroupMembership { private static final long serialVersionUID = -7336814163949640354L; - public static final String TABLE = "DynRoleMembership"; + public static final String NODE = "UDynGroupMembership"; - @OneToOne - private JPARole role; + private Neo4jGroup group; @Override - public Role getRole() { - return role; + public Group getGroup() { + return group; } @Override - public void setRole(final Role role) { - checkType(role, JPARole.class); - this.role = (JPARole) role; + public void setGroup(final Group group) { + checkType(group, Neo4jGroup.class); + this.group = (Neo4jGroup) group; } - } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUMembership.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUMembership.java new file mode 100644 index 0000000000..690340458c --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUMembership.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.persistence.neo4j.entity.user; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.entity.MembershipType; +import org.apache.syncope.core.persistence.api.entity.RelationshipType; +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.UPlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractMembership; +import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; +import org.springframework.data.neo4j.core.schema.CompositeProperty; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jUMembership.NODE) +public class Neo4jUMembership extends AbstractMembership implements UMembership { + + private static final long serialVersionUID = -14584450896965100L; + + public static final String NODE = "UMembership"; + + @Relationship(type = Neo4jUser.GROUP_MEMBERSHIP_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jUser leftEnd; + + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jGroup rightEnd; + + @CompositeProperty + protected Map plainAttrs = new HashMap<>(); + + @Override + public MembershipType getType() { + return MembershipType.getInstance(); + } + + @Override + public void setType(final RelationshipType type) { + // cannot be changed + } + + @Override + public User getLeftEnd() { + return leftEnd; + } + + @Override + public void setLeftEnd(final User leftEnd) { + checkType(leftEnd, Neo4jUser.class); + this.leftEnd = (Neo4jUser) leftEnd; + } + + @Override + public Group getRightEnd() { + return rightEnd; + } + + @Override + public void setRightEnd(final Group rightEnd) { + checkType(rightEnd, Neo4jGroup.class); + this.rightEnd = (Neo4jGroup) rightEnd; + } + + @Override + public List getPlainAttrs() { + return plainAttrs.entrySet().stream(). + sorted(Comparator.comparing(Map.Entry::getKey)). + map(Map.Entry::getValue).toList(); + } + + @Override + public Optional getPlainAttr(final String plainSchema) { + return Optional.ofNullable(plainAttrs.get(plainSchema)); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUPlainAttr.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUPlainAttr.java new file mode 100644 index 0000000000..7dafa9012c --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUPlainAttr.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity.user; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.user.UMembership; +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractPlainAttr; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jPlainAttr; +import org.apache.syncope.core.spring.ApplicationContextProvider; + +public class Neo4jUPlainAttr extends AbstractPlainAttr implements UPlainAttr, Neo4jPlainAttr { + + private static final long serialVersionUID = 806271775349587902L; + + /** + * The owner of this attribute. + */ + @JsonIgnore + private Neo4jUser owner; + + /** + * The membership of this attribute; might be {@code NULL} if this attribute is not related to a membership. + */ + @JsonProperty("membership") + private String membershipKey; + + /** + * Values of this attribute (if schema is not UNIQUE). + */ + private final List values = new ArrayList<>(); + + /** + * Value of this attribute (if schema is UNIQUE). + */ + @JsonProperty + private Neo4jUPlainAttrUniqueValue uniqueValue; + + @Override + public User getOwner() { + return owner; + } + + @Override + public void setOwner(final User owner) { + checkType(owner, Neo4jUser.class); + this.owner = (Neo4jUser) owner; + } + + public String getMembershipKey() { + return membershipKey; + } + + @JsonSetter("membership") + public void setMembershipKey(final String membershipKey) { + this.membershipKey = membershipKey; + } + + @JsonIgnore + @Override + public UMembership getMembership() { + return ApplicationContextProvider.getBeanFactory().getBean(UserDAO.class).findMembership(membershipKey); + } + + @JsonIgnore + @Override + public void setMembership(final UMembership membership) { + checkType(membership, Neo4jUMembership.class); + if (membership != null) { + this.membershipKey = membership.getKey(); + } + } + + @Override + protected boolean addForMultiValue(final PlainAttrValue attrValue) { + checkType(attrValue, Neo4jUPlainAttrValue.class); + return values.add((Neo4jUPlainAttrValue) attrValue); + } + + @Override + public boolean add(final PlainAttrValue value) { + return addForMultiValue(value); + } + + @Override + public List getValues() { + return values; + } + + @Override + public Neo4jUPlainAttrUniqueValue getUniqueValue() { + return uniqueValue; + } + + @JsonIgnore + @Override + public void setUniqueValue(final PlainAttrUniqueValue uniqueValue) { + checkType(uniqueValue, Neo4jUPlainAttrUniqueValue.class); + this.uniqueValue = (Neo4jUPlainAttrUniqueValue) uniqueValue; + } + + @Override + public int hashCode() { + return new HashCodeBuilder(). + append(schemaKey). + append(membershipKey). + append(values). + append(uniqueValue). + build(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Neo4jUPlainAttr other = (Neo4jUPlainAttr) obj; + return new EqualsBuilder(). + append(schemaKey, other.schemaKey). + append(membershipKey, other.membershipKey). + append(values, other.values). + append(uniqueValue, other.uniqueValue). + build(); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUPlainAttrUniqueValue.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUPlainAttrUniqueValue.java new file mode 100644 index 0000000000..a7c598b1bd --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUPlainAttrUniqueValue.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.persistence.neo4j.entity.user; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrUniqueValue; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractPlainAttrValue; + +public class Neo4jUPlainAttrUniqueValue extends AbstractPlainAttrValue implements UPlainAttrUniqueValue { + + private static final long serialVersionUID = -4053996864791245312L; + + @JsonIgnore + @NotNull + private Neo4jUPlainAttr attr; + + @Override + public UPlainAttr getAttr() { + return attr; + } + + @Override + public void setAttr(final PlainAttr attr) { + checkType(attr, Neo4jUPlainAttr.class); + this.attr = (Neo4jUPlainAttr) attr; + } + + @JsonIgnore + @Override + public PlainSchema getSchema() { + return getAttr() == null ? null : getAttr().getSchema(); + } + + @Override + public void setSchema(final PlainSchema schema) { + // nothing to do + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUPlainAttrValue.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUPlainAttrValue.java new file mode 100644 index 0000000000..dff3814ccd --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUPlainAttrValue.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.persistence.neo4j.entity.user; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.PlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrValue; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractPlainAttrValue; + +public class Neo4jUPlainAttrValue extends AbstractPlainAttrValue implements UPlainAttrValue { + + private static final long serialVersionUID = -8657212700294416428L; + + @JsonIgnore + @NotNull + private Neo4jUPlainAttr attr; + + @Override + public UPlainAttr getAttr() { + return attr; + } + + @Override + public void setAttr(final PlainAttr attr) { + checkType(attr, Neo4jUPlainAttr.class); + this.attr = (Neo4jUPlainAttr) attr; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jURelationship.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jURelationship.java new file mode 100644 index 0000000000..6eb9faf3b4 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jURelationship.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.persistence.neo4j.entity.user; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.MembershipType; +import org.apache.syncope.core.persistence.api.entity.RelationshipType; +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.persistence.api.entity.user.URelationship; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractGeneratedKeyNode; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRelationshipType; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAnyObject; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jURelationship.NODE) +public class Neo4jURelationship extends AbstractGeneratedKeyNode implements URelationship { + + private static final long serialVersionUID = 2778494939240083204L; + + public static final String NODE = "URelationship"; + + public static final String SOURCE_REL = "URELATIONSHIP_SOURCE"; + + public static final String DEST_REL = "URELATIONSHIP_DEST"; + + @NotNull + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jRelationshipType type; + + @Relationship(type = SOURCE_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jUser leftEnd; + + @Relationship(type = DEST_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jAnyObject rightEnd; + + @Override + public RelationshipType getType() { + return type; + } + + @Override + public void setType(final RelationshipType type) { + if (MembershipType.getInstance().getKey().equalsIgnoreCase(type.getKey())) { + throw new IllegalArgumentException("This is not a membership"); + } + checkType(type, Neo4jRelationshipType.class); + this.type = (Neo4jRelationshipType) type; + } + + @Override + public User getLeftEnd() { + return leftEnd; + } + + @Override + public void setLeftEnd(final User leftEnd) { + checkType(leftEnd, Neo4jUser.class); + this.leftEnd = (Neo4jUser) leftEnd; + } + + @Override + public AnyObject getRightEnd() { + return rightEnd; + } + + @Override + public void setRightEnd(final AnyObject rightEnd) { + checkType(rightEnd, Neo4jAnyObject.class); + this.rightEnd = (Neo4jAnyObject) rightEnd; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUser.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUser.java new file mode 100644 index 0000000000..6d1519bb0c --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUser.java @@ -0,0 +1,461 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.entity.user; + +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.apache.syncope.common.keymaster.client.api.ConfParamOps; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +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.AnyTypeClass; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +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.AnyObject; +import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount; +import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion; +import org.apache.syncope.core.persistence.api.entity.user.UMembership; +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.URelationship; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractGroupableRelatable; +import org.apache.syncope.core.persistence.neo4j.entity.AttributableCheck; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyTypeClass; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAttributable; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jPlainAttr; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRole; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.apache.syncope.core.spring.ApplicationContextProvider; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.apache.syncope.core.spring.security.Encryptor; +import org.apache.syncope.core.spring.security.SecureRandomUtils; +import org.springframework.data.neo4j.core.schema.CompositeProperty; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jUser.NODE) +@AttributableCheck +public class Neo4jUser + extends AbstractGroupableRelatable + implements User, Neo4jAttributable { + + private static final long serialVersionUID = -3905046855521446823L; + + public static final String NODE = "SyncopeUser"; + + public static final String ROLE_MEMBERSHIP_REL = "ROLE_MEMBERSHIP"; + + public static final String GROUP_MEMBERSHIP_REL = "GROUP_MEMBERSHIP"; + + protected static final Encryptor ENCRYPTOR = Encryptor.getInstance(); + + protected static final TypeReference> TYPEREF = new TypeReference>() { + }; + + protected String password; + + @Relationship(type = ROLE_MEMBERSHIP_REL, direction = Relationship.Direction.OUTGOING) + protected List roles = new ArrayList<>(); + + @CompositeProperty(converterRef = "uPlainAttrsConverter") + protected Map plainAttrs = new HashMap<>(); + + protected String token; + + protected OffsetDateTime tokenExpireTime; + + protected CipherAlgorithm cipherAlgorithm; + + protected String passwordHistory; + + /** + * Subsequent failed logins. + */ + protected Integer failedLogins; + + /** + * Username/Login. + */ + @NotNull(message = "Blank username") + protected String username; + + /** + * Last successful login date. + */ + protected OffsetDateTime lastLoginDate; + + /** + * Change password date. + */ + protected OffsetDateTime changePwdDate; + + protected Boolean suspended = false; + + protected Boolean mustChangePassword = false; + + /** + * Provisioning external resources. + */ + @Relationship(direction = Relationship.Direction.OUTGOING) + protected List resources = new ArrayList<>(); + + @Relationship(direction = Relationship.Direction.OUTGOING) + protected List auxClasses = new ArrayList<>(); + + @Relationship(type = Neo4jURelationship.SOURCE_REL, direction = Relationship.Direction.INCOMING) + protected List relationships = new ArrayList<>(); + + @Relationship(type = GROUP_MEMBERSHIP_REL, direction = Relationship.Direction.INCOMING) + protected List memberships = new ArrayList<>(); + + @Relationship(direction = Relationship.Direction.OUTGOING) + protected Neo4jSecurityQuestion securityQuestion; + + protected String securityAnswer; + + @Relationship(direction = Relationship.Direction.INCOMING) + @Valid + protected List linkedAccounts = new ArrayList<>(); + + @Override + protected Map>> plainAttrs() { + return plainAttrs; + } + + @Override + public AnyType getType() { + return ApplicationContextProvider.getBeanFactory().getBean(AnyTypeDAO.class).getUser(); + } + + @Override + public void setType(final AnyType type) { + // nothing to do + } + + @Override + public boolean add(final ExternalResource resource) { + checkType(resource, Neo4jExternalResource.class); + return resources.contains((Neo4jExternalResource) resource) || resources.add((Neo4jExternalResource) resource); + } + + @Override + public List getResources() { + return resources; + } + + @Override + public boolean add(final Role role) { + checkType(role, Neo4jRole.class); + return roles.contains((Neo4jRole) role) || roles.add((Neo4jRole) role); + } + + @Override + public List getRoles() { + return roles; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public void setEncodedPassword(final String password, final CipherAlgorithm cipherAlgorithm) { + this.password = password; + this.cipherAlgorithm = cipherAlgorithm; + setMustChangePassword(false); + } + + @Override + public void setPassword(final String password) { + try { + this.password = ENCRYPTOR.encode(password, cipherAlgorithm == null + ? CipherAlgorithm.valueOf(ApplicationContextProvider.getBeanFactory().getBean(ConfParamOps.class). + get(AuthContextUtils.getDomain(), "password.cipher.algorithm", CipherAlgorithm.AES.name(), + String.class)) + : cipherAlgorithm); + setMustChangePassword(false); + } catch (Exception e) { + LOG.error("Could not encode password", e); + this.password = null; + } + } + + @Override + public CipherAlgorithm getCipherAlgorithm() { + return cipherAlgorithm; + } + + @Override + public void setCipherAlgorithm(final CipherAlgorithm cipherAlgorithm) { + if (this.cipherAlgorithm == null || cipherAlgorithm == null) { + this.cipherAlgorithm = cipherAlgorithm; + } else { + throw new IllegalArgumentException("Cannot override existing cipher algorithm"); + } + } + + @Override + public boolean canDecodeSecrets() { + return this.cipherAlgorithm != null && this.cipherAlgorithm.isInvertible(); + } + + @Override + public boolean add(final UPlainAttr attr) { + checkType(attr, Neo4jUPlainAttr.class); + return plainAttrs.put(attr.getSchema().getKey(), (Neo4jUPlainAttr) attr) != null; + } + + @Override + protected Map internalGetPlainAttrs() { + return plainAttrs; + } + + @Override + public void generateToken(final int tokenLength, final int tokenExpireTime) { + this.token = SecureRandomUtils.generateRandomPassword(tokenLength); + this.tokenExpireTime = OffsetDateTime.now().plusMinutes(tokenExpireTime); + } + + @Override + public void removeToken() { + this.token = null; + this.tokenExpireTime = null; + } + + @Override + public String getToken() { + return token; + } + + @Override + public OffsetDateTime getTokenExpireTime() { + return tokenExpireTime; + } + + @Override + public boolean checkToken(final String token) { + return Optional.ofNullable(this.token). + map(s -> s.equals(token) && !hasTokenExpired()). + orElseGet(() -> token == null); + } + + @Override + public boolean hasTokenExpired() { + return Optional.ofNullable(tokenExpireTime). + filter(expireTime -> expireTime.isBefore(OffsetDateTime.now())). + isPresent(); + } + + @Override + public void addToPasswordHistory(final String password) { + List ph = getPasswordHistory(); + ph.add(password); + passwordHistory = POJOHelper.serialize(ph); + } + + @Override + public void removeOldestEntriesFromPasswordHistory(final int n) { + List ph = getPasswordHistory(); + ph.subList(n, ph.size()); + passwordHistory = POJOHelper.serialize(ph); + } + + @Override + public List getPasswordHistory() { + return passwordHistory == null + ? new ArrayList<>(0) + : POJOHelper.deserialize(passwordHistory, TYPEREF); + } + + @Override + public OffsetDateTime getChangePwdDate() { + return changePwdDate; + } + + @Override + public void setChangePwdDate(final OffsetDateTime changePwdDate) { + this.changePwdDate = changePwdDate; + } + + @Override + public Integer getFailedLogins() { + return failedLogins == null ? 0 : failedLogins; + } + + @Override + public void setFailedLogins(final Integer failedLogins) { + this.failedLogins = failedLogins; + } + + @Override + public OffsetDateTime getLastLoginDate() { + return lastLoginDate; + } + + @Override + public void setLastLoginDate(final OffsetDateTime lastLoginDate) { + this.lastLoginDate = lastLoginDate; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public void setUsername(final String username) { + this.username = username; + } + + @Override + public void setSuspended(final Boolean suspended) { + this.suspended = suspended; + } + + @Override + public Boolean isSuspended() { + return suspended; + } + + @Override + public void setMustChangePassword(final boolean mustChangePassword) { + this.mustChangePassword = mustChangePassword; + } + + @Override + public boolean isMustChangePassword() { + return mustChangePassword; + } + + @Override + public SecurityQuestion getSecurityQuestion() { + return securityQuestion; + } + + @Override + public void setSecurityQuestion(final SecurityQuestion securityQuestion) { + checkType(securityQuestion, Neo4jSecurityQuestion.class); + this.securityQuestion = (Neo4jSecurityQuestion) securityQuestion; + } + + @Override + public String getSecurityAnswer() { + return securityAnswer; + } + + @Override + public void setSecurityAnswer(final String securityAnswer) { + try { + this.securityAnswer = ENCRYPTOR.encode(securityAnswer, cipherAlgorithm == null + ? CipherAlgorithm.valueOf(ApplicationContextProvider.getBeanFactory().getBean(ConfParamOps.class). + get(AuthContextUtils.getDomain(), "password.cipher.algorithm", CipherAlgorithm.AES.name(), + String.class)) + : cipherAlgorithm); + } catch (Exception e) { + LOG.error("Could not encode security answer", e); + this.securityAnswer = null; + } + } + + @Override + public boolean add(final AnyTypeClass auxClass) { + checkType(auxClass, Neo4jAnyTypeClass.class); + return auxClasses.contains((Neo4jAnyTypeClass) auxClass) || auxClasses.add((Neo4jAnyTypeClass) auxClass); + } + + @Override + public List getAuxClasses() { + return auxClasses; + } + + @Override + public boolean add(final URelationship relationship) { + checkType(relationship, Neo4jURelationship.class); + return this.relationships.add((Neo4jURelationship) relationship); + } + + @Override + public Optional getRelationship( + final RelationshipType relationshipType, final String otherEndKey) { + + return getRelationships().stream().filter(relationship -> relationshipType.equals(relationship.getType()) + && otherEndKey != null && otherEndKey.equals(relationship.getRightEnd().getKey())). + findFirst(); + } + + @Override + public List getRelationships() { + return relationships; + } + + @Override + public boolean add(final UMembership membership) { + checkType(membership, Neo4jUMembership.class); + return this.memberships.add((Neo4jUMembership) membership); + } + + @Override + public boolean remove(final UMembership membership) { + checkType(membership, Neo4jUMembership.class); + return this.memberships.remove((Neo4jUMembership) membership); + } + + @Override + protected List internalGetMemberships() { + return memberships; + } + + @Override + public boolean add(final LinkedAccount account) { + checkType(account, Neo4jLinkedAccount.class); + return linkedAccounts.contains((Neo4jLinkedAccount) account) + || linkedAccounts.add((Neo4jLinkedAccount) account); + } + + @Override + public Optional getLinkedAccount(final String resource, final String connObjectKeyValue) { + return linkedAccounts.stream(). + filter(account -> account.getResource().getKey().equals(resource) + && account.getConnObjectKeyValue().equals(connObjectKeyValue)). + findFirst(); + } + + @Override + public List getLinkedAccounts(final String resource) { + return linkedAccounts.stream(). + filter(account -> account.getResource().getKey().equals(resource)). + toList(); + } + + @Override + public List getLinkedAccounts() { + return linkedAccounts; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/DomainRoutingNeo4jClient.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/DomainRoutingNeo4jClient.java new file mode 100644 index 0000000000..454456fccb --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/DomainRoutingNeo4jClient.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.persistence.neo4j.spring; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Supplier; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.neo4j.driver.QueryRunner; +import org.springframework.data.neo4j.core.DatabaseSelection; +import org.springframework.data.neo4j.core.DatabaseSelectionProvider; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.UserSelection; + +public class DomainRoutingNeo4jClient implements Neo4jClient { + + protected final Map delegates = new ConcurrentHashMap<>(); + + public void add(final String domain, final Neo4jClient neo4jClient) { + delegates.put(domain, neo4jClient); + } + + public void remove(final String domain) { + delegates.remove(domain); + } + + protected Neo4jClient delegate() { + return delegates.computeIfAbsent(AuthContextUtils.getDomain(), domain -> { + throw new IllegalStateException("Could not find Neo4jClient for domain " + domain); + }); + } + + @Override + public QueryRunner getQueryRunner(final DatabaseSelection databaseSelection, final UserSelection asUser) { + return delegate().getQueryRunner(databaseSelection, asUser); + } + + @Override + public UnboundRunnableSpec query(final String cypher) { + return delegate().query(cypher); + } + + @Override + public UnboundRunnableSpec query(final Supplier cypherSupplier) { + return delegate().query(cypherSupplier); + } + + @Override + public OngoingDelegation delegateTo(final Function> callback) { + return delegate().delegateTo(callback); + } + + @Override + public DatabaseSelectionProvider getDatabaseSelectionProvider() { + return delegate().getDatabaseSelectionProvider(); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/DomainRoutingNeo4jTransactionManager.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/DomainRoutingNeo4jTransactionManager.java new file mode 100644 index 0000000000..cecff40bad --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/DomainRoutingNeo4jTransactionManager.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.persistence.neo4j.spring; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionException; +import org.springframework.transaction.TransactionStatus; + +public class DomainRoutingNeo4jTransactionManager implements PlatformTransactionManager { + + protected final Map delegates = new ConcurrentHashMap<>(); + + public void add(final String domain, final Neo4jTransactionManager neo4jTransactionManager) { + delegates.put(domain, neo4jTransactionManager); + } + + public void remove(final String domain) { + delegates.remove(domain); + } + + protected Neo4jTransactionManager delegate() { + return delegates.computeIfAbsent(AuthContextUtils.getDomain(), domain -> { + throw new IllegalStateException("Could not find Neo4jTransactionManager for domain " + domain); + }); + } + + @Override + public TransactionStatus getTransaction(final TransactionDefinition definition) throws TransactionException { + return delegate().getTransaction(definition); + } + + @Override + public void commit(final TransactionStatus status) throws TransactionException { + delegate().commit(status); + } + + @Override + public void rollback(final TransactionStatus status) throws TransactionException { + delegate().rollback(status); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/NodeValidator.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/NodeValidator.java new file mode 100644 index 0000000000..1d2dc02243 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/NodeValidator.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.persistence.neo4j.spring; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validator; +import java.util.Set; +import org.apache.commons.lang3.ClassUtils; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.DynMembership; +import org.apache.syncope.core.persistence.api.entity.Entity; +import org.apache.syncope.core.persistence.api.entity.GroupableRelatable; +import org.apache.syncope.core.persistence.api.entity.ProvidedKeyEntity; +import org.apache.syncope.core.persistence.api.entity.Schema; +import org.apache.syncope.core.persistence.api.entity.policy.Policy; +import org.apache.syncope.core.persistence.api.entity.task.Task; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NodeValidator { + + protected static final Logger LOG = LoggerFactory.getLogger(NodeValidator.class); + + protected final Validator validator; + + public NodeValidator(final Validator validator) { + this.validator = validator; + } + + public T validate(final T node) { + Set> violations = validator.validate(node); + if (!violations.isEmpty()) { + LOG.warn("Bean validation errors found: {}", violations); + + Class entityInt = null; + for (Class interf : ClassUtils.getAllInterfaces(node.getClass())) { + if (!Entity.class.equals(interf) + && !ProvidedKeyEntity.class.equals(interf) + && !Schema.class.equals(interf) + && !Task.class.equals(interf) + && !Policy.class.equals(interf) + && !GroupableRelatable.class.equals(interf) + && !Any.class.equals(interf) + && !DynMembership.class.equals(interf) + && Entity.class.isAssignableFrom(interf)) { + + entityInt = interf; + } + } + + throw new InvalidEntityException(entityInt == null ? "Entity" : entityInt.getSimpleName(), violations); + } + + return node; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/PlainsAttrsConverter.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/PlainsAttrsConverter.java new file mode 100644 index 0000000000..fc2923d651 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/spring/PlainsAttrsConverter.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.persistence.neo4j.spring; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractPlainAttr; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.neo4j.driver.Value; +import org.neo4j.driver.internal.value.StringValue; +import org.springframework.data.neo4j.core.convert.Neo4jConversionService; +import org.springframework.data.neo4j.core.convert.Neo4jPersistentPropertyToMapConverter; + +public class PlainsAttrsConverter> + implements Neo4jPersistentPropertyToMapConverter> { + + protected final Class reference; + + public PlainsAttrsConverter(final Class reference) { + this.reference = reference; + } + + @Override + public Map decompose( + final Map property, + final Neo4jConversionService neo4jConversionService) { + + if (property == null) { + return Map.of(); + } + + Map decomposed = new HashMap<>(property.size()); + property.forEach((k, v) -> Optional.ofNullable(POJOHelper.serialize(v)). + ifPresent(s -> decomposed.put(k, new StringValue(s)))); + return decomposed; + } + + @Override + public Map compose( + final Map source, + final Neo4jConversionService neo4jConversionService) { + + Map composed = new HashMap<>(source.size()); + source.forEach((k, v) -> composed.put(k, POJOHelper.deserialize(v.asString(), reference))); + return composed; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/springframework/data/neo4j/repository/support/SyncopeNeo4jRepository.java b/core/persistence-neo4j/src/main/java/org/springframework/data/neo4j/repository/support/SyncopeNeo4jRepository.java new file mode 100644 index 0000000000..23cf0526d4 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/springframework/data/neo4j/repository/support/SyncopeNeo4jRepository.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.springframework.data.neo4j.repository.support; + +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.apache.syncope.core.spring.ApplicationContextProvider; +import org.springframework.data.neo4j.core.Neo4jOperations; + +public class SyncopeNeo4jRepository extends SimpleNeo4jRepository { + + public SyncopeNeo4jRepository( + final Neo4jOperations neo4jOperations, + final Neo4jEntityInformation entityInformation) { + + super(neo4jOperations, entityInformation); + } + + @Override + public S save(final S entity) { + return super.save( + ApplicationContextProvider.getApplicationContext().getBean(NodeValidator.class).validate(entity)); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/springframework/data/neo4j/repository/support/SyncopeNeo4jRepositoryFactory.java b/core/persistence-neo4j/src/main/java/org/springframework/data/neo4j/repository/support/SyncopeNeo4jRepositoryFactory.java new file mode 100644 index 0000000000..bea0c09b89 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/springframework/data/neo4j/repository/support/SyncopeNeo4jRepositoryFactory.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.springframework.data.neo4j.repository.support; + +import java.util.Optional; +import org.springframework.data.neo4j.core.Neo4jOperations; +import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; +import org.springframework.data.projection.ProjectionFactory; +import org.springframework.data.repository.core.EntityInformation; +import org.springframework.data.repository.core.RepositoryInformation; +import org.springframework.data.repository.core.RepositoryMetadata; +import org.springframework.data.repository.core.support.RepositoryComposition; +import org.springframework.data.repository.core.support.RepositoryFactorySupport; +import org.springframework.data.repository.query.QueryLookupStrategy; +import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; + +public class SyncopeNeo4jRepositoryFactory extends RepositoryFactorySupport { + + private final Neo4jRepositoryFactory delegate; + + public SyncopeNeo4jRepositoryFactory( + final Neo4jOperations neo4jOperations, + final Neo4jMappingContext mappingContext) { + + this.delegate = new Neo4jRepositoryFactory(neo4jOperations, mappingContext); + } + + @Override + public EntityInformation getEntityInformation(final Class domainClass) { + return delegate.getEntityInformation(domainClass); + } + + @Override + protected Object getTargetRepository(final RepositoryInformation metadata) { + return delegate.getTargetRepository(metadata); + } + + @Override + protected RepositoryComposition.RepositoryFragments getRepositoryFragments(final RepositoryMetadata metadata) { + return delegate.getRepositoryFragments(metadata); + } + + @Override + protected Class getRepositoryBaseClass(final RepositoryMetadata metadata) { + return SyncopeNeo4jRepository.class; + } + + @Override + protected Optional getQueryLookupStrategy( + final QueryLookupStrategy.Key key, + final QueryMethodEvaluationContextProvider evaluationContextProvider) { + + return delegate.getQueryLookupStrategy(key, evaluationContextProvider); + } + + @Override + protected ProjectionFactory getProjectionFactory() { + return delegate.getProjectionFactory(); + } +} diff --git a/core/persistence-neo4j/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/core/persistence-neo4j/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000000..1b92345821 --- /dev/null +++ b/core/persistence-neo4j/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -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. +org.apache.syncope.core.persistence.neo4j.PersistenceContext + diff --git a/core/persistence-neo4j/src/main/resources/domains/MasterContent.xml b/core/persistence-neo4j/src/main/resources/domains/MasterContent.xml new file mode 100644 index 0000000000..94a8d4510d --- /dev/null +++ b/core/persistence-neo4j/src/main/resources/domains/MasterContent.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/persistence-neo4j/src/main/resources/domains/MasterKeymasterConfParams.json b/core/persistence-neo4j/src/main/resources/domains/MasterKeymasterConfParams.json new file mode 100644 index 0000000000..8f2ce7d8b3 --- /dev/null +++ b/core/persistence-neo4j/src/main/resources/domains/MasterKeymasterConfParams.json @@ -0,0 +1,17 @@ +{ + "password.cipher.algorithm": "SSHA256", + "notificationjob.cronExpression": "", + "notification.maxRetries": 3, + "token.length": 256, + "token.expireTime": 60, + "selfRegistration.allowed": true, + "passwordReset.allowed": true, + "passwordReset.securityQuestion": true, + "authentication.attributes": ["username"], + "authentication.statuses": ["created", "active"], + "log.lastlogindate": true, + "return.password.value": false, + "jwt.lifetime.minutes": 120, + "connector.conf.history.size": 10, + "resource.conf.history.size": 10 +} diff --git a/core/persistence-neo4j/src/main/resources/indexes.xml b/core/persistence-neo4j/src/main/resources/indexes.xml new file mode 100644 index 0000000000..11cf8eac7c --- /dev/null +++ b/core/persistence-neo4j/src/main/resources/indexes.xml @@ -0,0 +1,56 @@ + + + + + Neo4j constraints and additional indexes + + CREATE CONSTRAINT AccessToken_owner FOR (n:AccessToken) REQUIRE n.owner IS UNIQUE + + CREATE CONSTRAINT AuthProfile_owner FOR (n:AuthProfile) REQUIRE n.owner IS UNIQUE + + CREATE CONSTRAINT AnyObject_name FOR (n:AnyObject) REQUIRE n.name IS UNIQUE + + CREATE CONSTRAINT ConnInstance_displayName FOR (n:ConnInstance) REQUIRE n.displayName IS UNIQUE + + CREATE CONSTRAINT SyncopeGroup_name FOR (n:SyncopeGroup) REQUIRE n.name IS UNIQUE + + CREATE CONSTRAINT Realm_fullPath FOR (n:Realm) REQUIRE n.fullPath IS UNIQUE + + CREATE CONSTRAINT Report_name FOR (n:Report) REQUIRE n.name IS UNIQUE + + CREATE CONSTRAINT CASSPClientApp_name FOR (n:CASSPClientApp) REQUIRE n.name IS UNIQUE + CREATE CONSTRAINT CASSPClientApp_clientAppId FOR (n:CASSPClientApp) REQUIRE n.clientAppId IS UNIQUE + CREATE CONSTRAINT CASSPClientApp_serviceId FOR (n:CASSPClientApp) REQUIRE n.serviceId IS UNIQUE + CREATE CONSTRAINT OIDCRPClientApp_name FOR (n:OIDCRPClientApp) REQUIRE n.name IS UNIQUE + CREATE CONSTRAINT OIDCRPClientApp_clientAppId FOR (n:OIDCRPClientApp) REQUIRE n.clientAppId IS UNIQUE + CREATE CONSTRAINT OIDCRPClientApp_clientId FOR (n:OIDCRPClientApp) REQUIRE n.clientId IS UNIQUE + CREATE CONSTRAINT SAML2SPClientApp_name FOR (n:SAML2SPClientApp) REQUIRE n.name IS UNIQUE + CREATE CONSTRAINT SAML2SPClientApp_clientAppId FOR (n:SAML2SPClientApp) REQUIRE n.clientAppId IS UNIQUE + CREATE CONSTRAINT SAML2SPClientApp_entityId FOR (n:SAML2SPClientApp) REQUIRE n.entityId IS UNIQUE + + CREATE CONSTRAINT SchedTask_name FOR (n:SchedTask) REQUIRE n.name IS UNIQUE + + CREATE CONSTRAINT Schema_id FOR (n:Schema) REQUIRE n.id IS UNIQUE + + CREATE CONSTRAINT SRARoute_name FOR (n:SRARoute) REQUIRE n.name IS UNIQUE + + CREATE CONSTRAINT SecurityQuestion_content FOR (n:SecurityQuestion) REQUIRE n.content IS UNIQUE + CREATE CONSTRAINT SyncopeUser_username FOR (n:SyncopeUser) REQUIRE n.username IS UNIQUE + diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/AbstractTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/AbstractTest.java new file mode 100644 index 0000000000..8a4788cd61 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/AbstractTest.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.core.persistence.neo4j; + +import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +@SpringJUnitConfig(classes = { MasterDomain.class, PersistenceTestContext.class }) +@DirtiesContext +public abstract class AbstractTest { + + @Autowired + protected EntityFactory entityFactory; + + @Autowired + protected AnyUtilsFactory anyUtilsFactory; + +} diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyConfParamOps.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyConfParamOps.java new file mode 100644 index 0000000000..c0f13ac802 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyConfParamOps.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.persistence.neo4j; + +import java.util.Map; +import org.apache.syncope.common.keymaster.client.api.ConfParamOps; + +public class DummyConfParamOps implements ConfParamOps { + + @Override + public Map list(final String domain) { + return Map.of(); + } + + @Override + public T get(final String domain, final String key, final T defaultValue, final Class reference) { + return defaultValue; + } + + @Override + public void set(final String domain, final String key, final T value) { + } + + @Override + public void remove(final String domain, final String key) { + } +} diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyConnectorManager.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyConnectorManager.java new file mode 100644 index 0000000000..af47491c6b --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyConnectorManager.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.neo4j; + +import java.util.Collection; +import java.util.Optional; +import org.apache.syncope.common.lib.to.ConnInstanceTO; +import org.apache.syncope.common.lib.types.ConnConfProperty; +import org.apache.syncope.common.lib.types.ConnectorCapability; +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; + +public class DummyConnectorManager implements ConnectorManager { + + @Override + public void registerConnector(final ExternalResource resource) { + } + + @Override + public void unregisterConnector(final ExternalResource resource) { + } + + @Override + public ConnInstance buildConnInstanceOverride( + final ConnInstanceTO connInstance, + final Collection confOverride, + final Optional> capabilitiesOverride) { + + return null; + } + + @Override + public Connector createConnector(final ConnInstance connInstance) { + return null; + } + + @Override + public Connector getConnector(final ExternalResource resource) { + return null; + } + + @Override + public Optional readConnector(final ExternalResource resource) { + return Optional.empty(); + } + + @Override + public void load() { + } + + @Override + public void unload() { + } +} diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyDomainOps.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyDomainOps.java new file mode 100644 index 0000000000..871547f59c --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyDomainOps.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.persistence.neo4j; + +import java.util.List; +import org.apache.syncope.common.keymaster.client.api.DomainOps; +import org.apache.syncope.common.keymaster.client.api.model.Domain; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.core.persistence.api.DomainRegistry; + +public class DummyDomainOps implements DomainOps { + + private final DomainRegistry domainRegistry; + + public DummyDomainOps(final DomainRegistry domainRegistry) { + this.domainRegistry = domainRegistry; + } + + @Override + public List list() { + return List.of(); + } + + @Override + public Domain read(final String key) { + return new Domain.Builder(key).build(); + } + + @Override + public void create(final Domain domain) { + domainRegistry.register(domain); + } + + @Override + public void changeAdminPassword(final String key, final String password, final CipherAlgorithm cipherAlgorithm) { + // nothing to do + } + + @Override + public void adjustPoolSize(final String key, final int maxPoolSize, final int minIdle) { + // nothing to do + } + + @Override + public void delete(final String key) { + // nothing to do + } +} diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyImplementationLookup.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyImplementationLookup.java new file mode 100644 index 0000000000..47d4659797 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyImplementationLookup.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.persistence.neo4j; + +import java.util.Set; +import org.apache.syncope.common.lib.policy.AccountRuleConf; +import org.apache.syncope.common.lib.policy.PasswordRuleConf; +import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf; +import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf; +import org.apache.syncope.common.lib.report.ReportConf; +import org.apache.syncope.core.provisioning.api.ImplementationLookup; +import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate; +import org.apache.syncope.core.provisioning.api.rules.AccountRule; +import org.apache.syncope.core.provisioning.api.rules.PasswordRule; +import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; +import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule; +import org.apache.syncope.core.spring.policy.DefaultAccountRule; +import org.apache.syncope.core.spring.policy.DefaultPasswordRule; + +public class DummyImplementationLookup implements ImplementationLookup { + + @Override + public int getOrder() { + return -1; + } + + @Override + public Set getClassNames(final String type) { + return Set.of(); + } + + @Override + public Class getReportClass(final Class reportConfClass) { + return null; + } + + @Override + public Class getAccountRuleClass( + final Class accountRuleConfClass) { + + return DefaultAccountRule.class; + } + + @Override + public Class getPasswordRuleClass( + final Class passwordRuleConfClass) { + + return DefaultPasswordRule.class; + } + + @Override + public Class getPullCorrelationRuleClass( + final Class pullCorrelationRuleConfClass) { + + return null; + } + + @Override + public Class getPushCorrelationRuleClass( + final Class pushCorrelationRuleConfClass) { + + return null; + } +} diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/PersistenceTestContext.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/PersistenceTestContext.java new file mode 100644 index 0000000000..132970878b --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/PersistenceTestContext.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.persistence.neo4j; + +import java.util.ArrayList; +import java.util.List; +import org.apache.syncope.common.keymaster.client.api.ConfParamOps; +import org.apache.syncope.common.keymaster.client.api.DomainOps; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.core.persistence.api.DomainHolder; +import org.apache.syncope.core.persistence.api.DomainRegistry; +import org.apache.syncope.core.persistence.api.content.ContentLoader; +import org.apache.syncope.core.persistence.neo4j.spring.DomainRoutingNeo4jClient; +import org.apache.syncope.core.provisioning.api.ConnectorManager; +import org.apache.syncope.core.provisioning.api.ImplementationLookup; +import org.apache.syncope.core.spring.security.DefaultPasswordGenerator; +import org.apache.syncope.core.spring.security.PasswordGenerator; +import org.apache.syncope.core.spring.security.SecurityProperties; +import org.neo4j.driver.Driver; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Primary; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.repository.config.Neo4jRepositoryConfigurationExtension; + +@Import(PersistenceContext.class) +@Configuration(proxyBeanMethods = false) +public class PersistenceTestContext { + + public static final ThreadLocal TEST_DOMAIN = ThreadLocal.withInitial(() -> SyncopeConstants.MASTER_DOMAIN); + + @Bean + public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { + PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer(); + + DefaultResourceLoader resourceLoader = new DefaultResourceLoader(); + + List locations = new ArrayList<>(); + for (String location : System.getProperty("CORE_PROPERTIES").split(",")) { + locations.add(resourceLoader.getResource(location)); + } + ppc.setLocations(locations.toArray(Resource[]::new)); + + return ppc; + } + + @Value("${security.adminUser}") + private String adminUser; + + @Value("${security.anonymousUser}") + private String anonymousUser; + + @Bean + public String adminUser() { + return adminUser; + } + + @Bean + public String anonymousUser() { + return anonymousUser; + } + + @Bean + public TestInitializer testInitializer( + final StartupDomainLoader domainLoader, + final DomainHolder domainHolder, + final ContentLoader contentLoader, + final ConfigurableApplicationContext ctx) { + + return new TestInitializer(domainLoader, domainHolder, contentLoader, ctx); + } + + @Bean + public SecurityProperties securityProperties() { + return new SecurityProperties(); + } + + @Bean + public PasswordGenerator passwordGenerator() { + return new DefaultPasswordGenerator(); + } + + @Bean + public ImplementationLookup implementationLookup() { + return new DummyImplementationLookup(); + } + + @Bean + public ConfParamOps confParamOps() { + return new DummyConfParamOps(); + } + + @Bean + public DomainOps domainOps(final DomainRegistry domainRegistry) { + return new DummyDomainOps(domainRegistry); + } + + @Bean + public ConnectorManager connectorManager() { + return new DummyConnectorManager(); + } + + @Primary + @Bean(Neo4jRepositoryConfigurationExtension.DEFAULT_NEO4J_CLIENT_BEAN_NAME) + public Neo4jClient neo4jClient( + @Qualifier("MasterNeo4jClient") + final Neo4jClient masterNeo4jClient) { + + DomainRoutingNeo4jClient neo4jClient = new DomainRoutingNeo4jClient() { + + @Override + protected Neo4jClient delegate() { + return delegates.getOrDefault( + TEST_DOMAIN.get(), + delegates.get(SyncopeConstants.MASTER_DOMAIN)); + } + }; + neo4jClient.add(SyncopeConstants.MASTER_DOMAIN, masterNeo4jClient); + return neo4jClient; + } +} diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/TestInitializer.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/TestInitializer.java new file mode 100644 index 0000000000..2b1583212a --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/TestInitializer.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.persistence.neo4j; + +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.core.persistence.api.DomainHolder; +import org.apache.syncope.core.persistence.api.content.ContentLoader; +import org.apache.syncope.core.spring.ApplicationContextProvider; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.neo4j.driver.Driver; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.context.ConfigurableApplicationContext; + +public class TestInitializer implements InitializingBean { + + private final StartupDomainLoader domainLoader; + + private final DomainHolder domainHolder; + + private final ContentLoader contentLoader; + + private final ConfigurableApplicationContext ctx; + + public TestInitializer( + final StartupDomainLoader domainLoader, + final DomainHolder domainHolder, + final ContentLoader contentLoader, + final ConfigurableApplicationContext ctx) { + + this.domainLoader = domainLoader; + this.domainHolder = domainHolder; + this.contentLoader = contentLoader; + this.ctx = ctx; + } + + @Override + public void afterPropertiesSet() throws Exception { + ApplicationContextProvider.setApplicationContext(ctx); + ApplicationContextProvider.setBeanFactory((DefaultListableBeanFactory) ctx.getBeanFactory()); + + domainLoader.load(); + + contentLoader.load(SyncopeConstants.MASTER_DOMAIN); + + if (domainHolder.getDomains().containsKey("Two")) { + AuthContextUtils.runAsAdmin("Two", () -> contentLoader.load("Two")); + } + } +} diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AbstractClientAppTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AbstractClientAppTest.java new file mode 100644 index 0000000000..cb29bcc9ca --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AbstractClientAppTest.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.persistence.neo4j.inner; + +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.core.persistence.api.dao.PolicyDAO; +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.persistence.neo4j.AbstractTest; +import org.springframework.beans.factory.annotation.Autowired; + +public class AbstractClientAppTest extends AbstractTest { + + @Autowired + protected PolicyDAO policyDAO; + + protected AttrReleasePolicy buildAndSaveAttrRelPolicy() { + AttrReleasePolicy attrRelPolicy = entityFactory.newEntity(AttrReleasePolicy.class); + attrRelPolicy.setName("AttrRelPolicyTest"); + attrRelPolicy.setStatus(Boolean.TRUE); + + DefaultAttrReleasePolicyConf conf = new DefaultAttrReleasePolicyConf(); + conf.getAllowedAttrs().addAll(List.of("cn", "givenName")); + conf.getIncludeOnlyAttrs().add("cn"); + + attrRelPolicy.setConf(conf); + + return policyDAO.save(attrRelPolicy); + + } + + protected AccessPolicy buildAndSaveAccessPolicy() { + AccessPolicy accessPolicy = entityFactory.newEntity(AccessPolicy.class); + accessPolicy.setName("AccessPolicyTest"); + + DefaultAccessPolicyConf conf = new DefaultAccessPolicyConf(); + conf.setEnabled(true); + conf.setSsoEnabled(false); + conf.getRequiredAttrs().put("attribute1", "value1,value2"); + accessPolicy.setConf(conf); + + return policyDAO.save(accessPolicy); + } + + protected AuthPolicy buildAndSaveAuthPolicy() { + AuthPolicy authPolicy = entityFactory.newEntity(AuthPolicy.class); + authPolicy.setName("AuthPolicyTest"); + + DefaultAuthPolicyConf conf = new DefaultAuthPolicyConf(); + conf.getAuthModules().addAll(List.of("LdapAuthentication1", "DatabaseAuthentication2")); + authPolicy.setConf(conf); + + return policyDAO.save(authPolicy); + } + + protected TicketExpirationPolicy buildAndSaveTicketExpirationPolicy() { + TicketExpirationPolicy ticketExpirationPolicy = entityFactory.newEntity(TicketExpirationPolicy.class); + ticketExpirationPolicy.setName("TicketExpirationPolicyTest"); + + DefaultTicketExpirationPolicyConf conf = new DefaultTicketExpirationPolicyConf(); + DefaultTicketExpirationPolicyConf.TGTConf tgtConf = new DefaultTicketExpirationPolicyConf.TGTConf(); + tgtConf.setMaxTimeToLiveInSeconds(110); + conf.setTgtConf(tgtConf); + DefaultTicketExpirationPolicyConf.STConf stConf = new DefaultTicketExpirationPolicyConf.STConf(); + stConf.setMaxTimeToLiveInSeconds(0); + stConf.setNumberOfUses(1); + conf.setStConf(stConf); + ticketExpirationPolicy.setConf(conf); + + return policyDAO.save(ticketExpirationPolicy); + } +} 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 new file mode 100644 index 0000000000..b8fe8920fd --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AccessTokenTest.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.core.persistence.neo4j.inner; + +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.fail; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.UUID; +import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; +import org.apache.syncope.core.persistence.api.entity.AccessToken; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class AccessTokenTest extends AbstractTest { + + @Autowired + private AccessTokenDAO accessTokenDAO; + + private void create(final String key, final long minusMinutes) { + AccessToken accessToken = entityFactory.newEntity(AccessToken.class); + accessToken.setKey(key); + accessToken.setBody("pointless body"); + accessToken.setExpirationTime(OffsetDateTime.now().minusMinutes(minusMinutes)); + accessToken.setOwner(UUID.randomUUID().toString()); + + accessTokenDAO.save(accessToken); + } + + @Test + public void findAll() { + for (long i = 0; i < 5; i++) { + create(String.valueOf(6 - i), i); + } + + assertEquals(5, accessTokenDAO.count()); + + Page page = + accessTokenDAO.findAll(PageRequest.of(0, 2, Sort.by("expirationTime").descending())); + assertEquals(5, page.getTotalElements()); + assertEquals(2, page.getNumberOfElements()); + + List list = page.get().toList(); + assertEquals(2, list.size()); + assertEquals("6", list.get(0).getKey()); + assertEquals("5", list.get(1).getKey()); + + page = accessTokenDAO.findAll(PageRequest.of(1, 2, Sort.by("expirationTime").descending())); + assertEquals(5, page.getTotalElements()); + assertEquals(2, page.getNumberOfElements()); + + list = page.get().toList(); + assertEquals(2, list.size()); + assertEquals("4", list.get(0).getKey()); + assertEquals("3", list.get(1).getKey()); + + page = accessTokenDAO.findAll(PageRequest.of(2, 2, Sort.by("expirationTime").descending())); + assertEquals(5, page.getTotalElements()); + assertEquals(1, page.getNumberOfElements()); + + list = page.get().toList(); + assertEquals(1, list.size()); + assertEquals("2", list.get(0).getKey()); + } + + @Test + public void crud() { + AccessToken accessToken = entityFactory.newEntity(AccessToken.class); + accessToken.setKey(UUID.randomUUID().toString()); + accessToken.setBody("pointless body"); + accessToken.setExpirationTime(OffsetDateTime.now()); + accessToken.setOwner("bellini"); + + accessToken = accessTokenDAO.save(accessToken); + assertNotNull(accessToken); + + accessToken = accessTokenDAO.findByOwner("bellini").orElse(null); + assertNotNull(accessToken); + assertEquals("bellini", accessToken.getOwner()); + + int deleted = accessTokenDAO.deleteExpired(OffsetDateTime.now()); + assertEquals(1, deleted); + + accessToken = accessTokenDAO.findByOwner("bellini").orElse(null); + assertNull(accessToken); + } + + @Test + public void unique() { + AccessToken accessToken = entityFactory.newEntity(AccessToken.class); + accessToken.setKey(UUID.randomUUID().toString()); + accessToken.setBody("pointless body"); + accessToken.setExpirationTime(OffsetDateTime.now()); + accessToken.setOwner("bellini"); + + accessTokenDAO.save(accessToken); + + try { + accessToken.setKey(UUID.randomUUID().toString()); + accessTokenDAO.save(accessToken); + fail(); + } catch (DataIntegrityViolationException e) { + assertNotNull(e); + } + } +} 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 new file mode 100644 index 0000000000..8a85d0aef6 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyMatchTest.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.inner; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO; +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.UserDAO; +import org.apache.syncope.core.persistence.api.dao.search.AnyCond; +import org.apache.syncope.core.persistence.api.dao.search.AttrCond; +import org.apache.syncope.core.persistence.api.dao.search.MembershipCond; +import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond; +import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond; +import org.apache.syncope.core.persistence.api.dao.search.ResourceCond; +import org.apache.syncope.core.persistence.api.dao.search.RoleCond; +import org.apache.syncope.core.persistence.api.dao.search.SearchCond; +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.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class AnyMatchTest extends AbstractTest { + + @Autowired + private UserDAO userDAO; + + @Autowired + private GroupDAO groupDAO; + + @Autowired + private AnyObjectDAO anyObjectDAO; + + @Autowired + private AnyMatchDAO anyMatcher; + + @Test + public void byResourceCond() { + User user = userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").orElseThrow(); + + ResourceCond resourceCond = new ResourceCond(); + resourceCond.setResource("resource-testdb2"); + assertTrue(anyMatcher.matches(user, SearchCond.getLeaf(resourceCond))); + + resourceCond.setResource("ws-target-resource-delete"); + assertFalse(anyMatcher.matches(user, SearchCond.getLeaf(resourceCond))); + } + + @Test + public void anyObjectMatch() { + AnyObject anyObject = anyObjectDAO.findById("fc6dbc3a-6c07-4965-8781-921e7401a4a5").orElseThrow(); + + RelationshipCond relationshipCond = new RelationshipCond(); + relationshipCond.setAnyObject("8559d14d-58c2-46eb-a2d4-a7d35161e8f8"); + assertTrue(anyMatcher.matches(anyObject, SearchCond.getLeaf(relationshipCond))); + + RelationshipTypeCond relationshipTypeCond = new RelationshipTypeCond(); + relationshipTypeCond.setRelationshipTypeKey("neighborhood"); + assertTrue(anyMatcher.matches(anyObject, SearchCond.getLeaf(relationshipTypeCond))); + } + + @Test + public void userMatch() { + User user = userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").orElseThrow(); + + MembershipCond groupCond = new MembershipCond(); + groupCond.setGroup("secretary"); + assertFalse(anyMatcher.matches(user, SearchCond.getLeaf(groupCond))); + + groupCond.setGroup("root"); + assertTrue(anyMatcher.matches(user, SearchCond.getLeaf(groupCond))); + + RoleCond roleCond = new RoleCond(); + roleCond.setRole("Other"); + assertTrue(anyMatcher.matches(user, SearchCond.getLeaf(roleCond))); + + user = userDAO.findById("c9b2dec2-00a7-4855-97c0-d854842b4b24").orElseThrow(); + + RelationshipCond relationshipCond = new RelationshipCond(); + relationshipCond.setAnyObject("fc6dbc3a-6c07-4965-8781-921e7401a4a5"); + assertTrue(anyMatcher.matches(user, SearchCond.getLeaf(relationshipCond))); + + RelationshipTypeCond relationshipTypeCond = new RelationshipTypeCond(); + relationshipTypeCond.setRelationshipTypeKey("neighborhood"); + assertTrue(anyMatcher.matches(user, SearchCond.getLeaf(relationshipTypeCond))); + } + + @Test + public void groupMatch() { + Group group = groupDAO.findById("37d15e4c-cdc1-460b-a591-8505c8133806").orElseThrow(); + + AnyCond anyCond = new AnyCond(); + anyCond.setSchema("name"); + anyCond.setExpression("root"); + anyCond.setType(AttrCond.Type.EQ); + assertTrue(anyMatcher.matches(group, SearchCond.getLeaf(anyCond))); + + AttrCond attrCond = new AttrCond(); + attrCond.setSchema("show"); + attrCond.setType(AttrCond.Type.ISNOTNULL); + assertTrue(anyMatcher.matches(group, SearchCond.getLeaf(attrCond))); + } +} 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 new file mode 100644 index 0000000000..752d300416 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyObjectTest.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.persistence.neo4j.inner; + +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.util.List; +import java.util.Map; +import org.apache.syncope.common.lib.SyncopeConstants; +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.RealmDAO; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttr; +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class AnyObjectTest extends AbstractTest { + + @Autowired + private AnyTypeDAO anyTypeDAO; + + @Autowired + private AnyObjectDAO anyObjectDAO; + + @Autowired + private RealmDAO realmDAO; + + @Test + public void findAll() { + List anyObjects = anyObjectDAO.findAll(); + assertEquals(3, anyObjects.size()); + } + + @Test + public void find() { + AnyObject anyObject = anyObjectDAO.findById("8559d14d-58c2-46eb-a2d4-a7d35161e8f8").orElseThrow(); + assertNotNull(anyObject.getType()); + assertFalse(anyObject.getType().getClasses().isEmpty()); + + APlainAttr model = anyObject.getPlainAttr("model").orElseThrow(); + assertEquals("HP Laserjet 1300n", model.getValuesAsStrings().get(0)); + + APlainAttr location = anyObject.getPlainAttr("location").orElseThrow(); + assertEquals("2nd floor", location.getValuesAsStrings().get(0)); + } + + @Test + public void findByName() { + AnyObject anyObject = anyObjectDAO.findByName("PRINTER", "HP LJ 1300n").orElseThrow(); + assertNotNull(anyObject); + assertEquals("fc6dbc3a-6c07-4965-8781-921e7401a4a5", anyObject.getKey()); + + assertEquals(1, anyObjectDAO.findByName("HP LJ 1300n").size()); + } + + @Test + public void findKey() { + assertEquals( + "fc6dbc3a-6c07-4965-8781-921e7401a4a5", + anyObjectDAO.findKey("PRINTER", "HP LJ 1300n").orElseThrow()); + + assertTrue(anyObjectDAO.findKey("PRINTER", "any").isEmpty()); + } + + @Test + public void findByKeys() { + List found = anyObjectDAO.findByKeys(List.of( + "8559d14d-58c2-46eb-a2d4-a7d35161e8f8", "9e1d130c-d6a3-48b1-98b3-182477ed0688", "none")); + assertEquals(2, found.size()); + assertTrue(found.contains(anyObjectDAO.findById("8559d14d-58c2-46eb-a2d4-a7d35161e8f8").orElseThrow())); + assertTrue(found.contains(anyObjectDAO.findById("9e1d130c-d6a3-48b1-98b3-182477ed0688").orElseThrow())); + } + + @Test + public void countByType() { + Map byType = anyObjectDAO.countByType(); + assertEquals(1, byType.size()); + Long count = byType.get(anyTypeDAO.findById("PRINTER").orElseThrow()); + assertEquals(3, count); + } + + @Test + public void countByRealm() { + Map byRealm = anyObjectDAO.countByRealm(anyTypeDAO.findById("PRINTER").orElseThrow()); + assertEquals(2, byRealm.size()); + Long count = byRealm.get(SyncopeConstants.ROOT_REALM); + assertEquals(2, count); + } + + @Test + public void save() { + AnyObject anyObject = entityFactory.newEntity(AnyObject.class); + anyObject.setName("a name"); + anyObject.setType(anyTypeDAO.findById("PRINTER").orElseThrow()); + anyObject.setRealm(realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); + + anyObject = anyObjectDAO.save(anyObject); + assertNotNull(anyObject); + } + + @Test + public void delete() { + AnyObject anyObject = anyObjectDAO.findById("8559d14d-58c2-46eb-a2d4-a7d35161e8f8").orElseThrow(); + anyObjectDAO.deleteById(anyObject.getKey()); + + AnyObject actual = anyObjectDAO.findById("8559d14d-58c2-46eb-a2d4-a7d35161e8f8").orElse(null); + assertNull(actual); + } +} 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 new file mode 100644 index 0000000000..fb8ba651ea --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnySearchTest.java @@ -0,0 +1,902 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.inner; + +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.text.ParseException; +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 java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +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.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.RealmDAO; +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.AnyCond; +import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond; +import org.apache.syncope.core.persistence.api.dao.search.AttrCond; +import org.apache.syncope.core.persistence.api.dao.search.AuxClassCond; +import org.apache.syncope.core.persistence.api.dao.search.MemberCond; +import org.apache.syncope.core.persistence.api.dao.search.MembershipCond; +import org.apache.syncope.core.persistence.api.dao.search.PrivilegeCond; +import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond; +import org.apache.syncope.core.persistence.api.dao.search.ResourceCond; +import org.apache.syncope.core.persistence.api.dao.search.RoleCond; +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.anyobject.AMembership; +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.UPlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.api.utils.FormatUtils; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.apache.syncope.core.spring.security.SyncopeAuthenticationDetails; +import org.apache.syncope.core.spring.security.SyncopeGrantedAuthority; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class AnySearchTest extends AbstractTest { + + private static final String LOGIN_DATE_VALUE = "2009-05-26"; + + @Autowired + private UserDAO userDAO; + + @Autowired + private AnyObjectDAO anyObjectDAO; + + @Autowired + private GroupDAO groupDAO; + + @Autowired + private AnySearchDAO searchDAO; + + @Autowired + private AnyTypeDAO anyTypeDAO; + + @Autowired + private RealmDAO realmDAO; + + @Autowired + private RoleDAO roleDAO; + + @BeforeEach + public void adjustLoginDateForLocalSystem() throws ParseException { + User rossini = userDAO.findByUsername("rossini").orElseThrow(); + + UPlainAttr loginDate = rossini.getPlainAttr("loginDate").get(); + loginDate.getValues().get(0).setDateValue(FormatUtils.parseDate(LOGIN_DATE_VALUE, "yyyy-MM-dd")); + + userDAO.save(rossini); + } + + @Test + public void searchWithLikeCondition() { + AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.LIKE); + fullnameLeafCond.setSchema("fullname"); + fullnameLeafCond.setExpression("%o%"); + + MembershipCond groupCond = new MembershipCond(); + groupCond.setGroup("root"); + + AttrCond loginDateCond = new AttrCond(AttrCond.Type.EQ); + loginDateCond.setSchema("loginDate"); + loginDateCond.setExpression(LOGIN_DATE_VALUE); + + SearchCond subCond = SearchCond.getAnd( + SearchCond.getLeaf(fullnameLeafCond), SearchCond.getLeaf(groupCond)); + + assertTrue(subCond.isValid()); + + SearchCond cond = SearchCond.getAnd(subCond, SearchCond.getLeaf(loginDateCond)); + + assertTrue(cond.isValid()); + + List users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + } + + @Test + public void searchCaseInsensitiveWithLikeCondition() { + AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.ILIKE); + fullnameLeafCond.setSchema("fullname"); + fullnameLeafCond.setExpression("%O%"); + + MembershipCond groupCond = new MembershipCond(); + groupCond.setGroup("root"); + + AttrCond loginDateCond = new AttrCond(AttrCond.Type.EQ); + loginDateCond.setSchema("loginDate"); + loginDateCond.setExpression(LOGIN_DATE_VALUE); + + SearchCond subCond = SearchCond.getAnd( + SearchCond.getLeaf(fullnameLeafCond), SearchCond.getLeaf(groupCond)); + + assertTrue(subCond.isValid()); + + SearchCond cond = SearchCond.getAnd(subCond, SearchCond.getLeaf(loginDateCond)); + + assertTrue(cond.isValid()); + + List users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + } + + @Test + public void searchWithNotAttrCond() { + AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.EQ); + fullnameLeafCond.setSchema("fullname"); + fullnameLeafCond.setExpression("Giuseppe Verdi"); + + SearchCond cond = SearchCond.getNotLeaf(fullnameLeafCond); + assertTrue(cond.isValid()); + + List users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(4, users.size()); + + Set ids = users.stream().map(User::getKey).collect(Collectors.toSet()); + assertTrue(ids.contains("1417acbe-cbf6-4277-9372-e75e04f97000")); + assertTrue(ids.contains("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee")); + } + + @Test + public void searchWithNotAnyCond() { + AnyCond usernameLeafCond = new AnyCond(AttrCond.Type.EQ); + usernameLeafCond.setSchema("username"); + usernameLeafCond.setExpression("verdi"); + + SearchCond cond = SearchCond.getNotLeaf(usernameLeafCond); + assertTrue(cond.isValid()); + + List users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(4, users.size()); + + assertTrue(users.stream().noneMatch(user -> "verdi".equals(user.getUsername()))); + } + + @Test + public void searchCaseInsensitiveWithNotCondition() { + AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.IEQ); + fullnameLeafCond.setSchema("fullname"); + fullnameLeafCond.setExpression("giuseppe verdi"); + + SearchCond cond = SearchCond.getNotLeaf(fullnameLeafCond); + assertTrue(cond.isValid()); + + List users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(4, users.size()); + + Set ids = users.stream().map(User::getKey).collect(Collectors.toSet()); + assertTrue(ids.contains("1417acbe-cbf6-4277-9372-e75e04f97000")); + assertTrue(ids.contains("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee")); + } + + @Test + public void searchByBoolean() { + AttrCond coolLeafCond = new AttrCond(AttrCond.Type.EQ); + coolLeafCond.setSchema("cool"); + coolLeafCond.setExpression("true"); + + SearchCond cond = SearchCond.getLeaf(coolLeafCond); + assertTrue(cond.isValid()); + + List users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + + assertEquals("c9b2dec2-00a7-4855-97c0-d854842b4b24", users.get(0).getKey()); + } + + @Test + public void searchByRealm() { + AnyCond anyCond = new AnyCond(AttrCond.Type.EQ); + anyCond.setSchema("realm"); + anyCond.setExpression("c5b75db1-fce7-470f-b780-3b9934d82a9d"); + + List users = searchDAO.search(SearchCond.getLeaf(anyCond), AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + assertEquals("rossini", users.get(0).getUsername()); + + anyCond.setExpression("/even"); + users = searchDAO.search(SearchCond.getLeaf(anyCond), AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + assertEquals("rossini", users.get(0).getUsername()); + } + + @Test + public void searchByPageAndSize() { + AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.LIKE); + fullnameLeafCond.setSchema("fullname"); + fullnameLeafCond.setExpression("%o%"); + + MembershipCond groupCond = new MembershipCond(); + groupCond.setGroup("root"); + + AttrCond loginDateCond = new AttrCond(AttrCond.Type.EQ); + loginDateCond.setSchema("loginDate"); + loginDateCond.setExpression(LOGIN_DATE_VALUE); + + SearchCond subCond = SearchCond.getAnd( + SearchCond.getLeaf(fullnameLeafCond), SearchCond.getLeaf(groupCond)); + + assertTrue(subCond.isValid()); + + SearchCond cond = SearchCond.getAnd(subCond, SearchCond.getLeaf(loginDateCond)); + + assertTrue(cond.isValid()); + + long count = searchDAO.count( + realmDAO.getRoot(), true, SyncopeConstants.FULL_ADMIN_REALMS, cond, AnyTypeKind.USER); + assertEquals(1, count); + + List users = searchDAO.search( + realmDAO.getRoot(), true, SyncopeConstants.FULL_ADMIN_REALMS, cond, + PageRequest.of(0, 2), AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + + users = searchDAO.search( + realmDAO.getRoot(), true, SyncopeConstants.FULL_ADMIN_REALMS, cond, + PageRequest.of(2, 2), AnyTypeKind.USER); + assertNotNull(users); + assertTrue(users.isEmpty()); + } + + @Test + public void searchByGroup() { + MembershipCond groupCond = new MembershipCond(); + groupCond.setGroup("child"); + + List matchingChild = searchDAO.search(SearchCond.getLeaf(groupCond), AnyTypeKind.USER); + assertNotNull(matchingChild); + assertTrue(matchingChild.stream().anyMatch(user -> "verdi".equals(user.getUsername()))); + + groupCond.setGroup("otherchild"); + + List matchingOtherChild = searchDAO.search(SearchCond.getLeaf(groupCond), AnyTypeKind.USER); + assertNotNull(matchingOtherChild); + assertTrue(matchingOtherChild.stream().anyMatch(user -> "rossini".equals(user.getUsername()))); + + Set union = Stream.concat( + matchingChild.stream().map(User::getUsername), + matchingOtherChild.stream().map(User::getUsername)). + collect(Collectors.toSet()); + + groupCond.setGroup("%child"); + + List matchingStar = searchDAO.search(SearchCond.getLeaf(groupCond), AnyTypeKind.USER); + assertNotNull(matchingStar); + assertTrue(matchingStar.stream().anyMatch(user -> "verdi".equals(user.getUsername()))); + assertTrue(matchingStar.stream().anyMatch(user -> "rossini".equals(user.getUsername()))); + assertEquals(union, matchingStar.stream().map(User::getUsername).collect(Collectors.toSet())); + + matchingStar = searchDAO.search(realmDAO.getRoot(), false, SyncopeConstants.FULL_ADMIN_REALMS, + SearchCond.getLeaf(groupCond), Pageable.unpaged(), AnyTypeKind.USER); + assertNotNull(matchingStar); + assertTrue(matchingStar.stream().anyMatch(user -> "verdi".equals(user.getUsername()))); + assertTrue(matchingStar.stream().noneMatch(user -> "rossini".equals(user.getUsername()))); + } + + @Test + public void searchByRole() { + RoleCond roleCond = new RoleCond(); + roleCond.setRole("Other"); + + List users = searchDAO.search(SearchCond.getLeaf(roleCond), AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + } + + @Test + public void searchByPrivilege() { + PrivilegeCond privilegeCond = new PrivilegeCond(); + privilegeCond.setPrivilege("postMighty"); + + List users = searchDAO.search(SearchCond.getLeaf(privilegeCond), AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + } + + @Test + public void searchByIsNull() { + AttrCond coolLeafCond = new AttrCond(AttrCond.Type.ISNULL); + coolLeafCond.setSchema("cool"); + + List users = searchDAO.search(SearchCond.getLeaf(coolLeafCond), AnyTypeKind.USER); + assertNotNull(users); + assertEquals(4, users.size()); + + coolLeafCond = new AttrCond(AttrCond.Type.ISNOTNULL); + coolLeafCond.setSchema("cool"); + + users = searchDAO.search(SearchCond.getLeaf(coolLeafCond), AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + } + + @Test + public void searchByAuxClass() { + AuxClassCond ac = new AuxClassCond(); + ac.setAuxClass("csv"); + + List groups = searchDAO.search(SearchCond.getLeaf(ac), AnyTypeKind.GROUP); + assertNotNull(groups); + assertEquals(2, groups.size()); + } + + @Test + public void searchByResource() { + ResourceCond ws2 = new ResourceCond(); + ws2.setResource("ws-target-resource-2"); + + ResourceCond ws1 = new ResourceCond(); + ws1.setResource("ws-target-resource-list-mappings-2"); + + SearchCond searchCondition = SearchCond.getAnd(SearchCond.getNotLeaf(ws2), SearchCond.getLeaf(ws1)); + assertTrue(searchCondition.isValid()); + + List users = searchDAO.search(searchCondition, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + } + + @Test + public void searchByBooleanAttrCond() { + AttrCond booleanCond = new AttrCond(AnyCond.Type.EQ); + booleanCond.setSchema("show"); + booleanCond.setExpression("true"); + + List matchingGroups = searchDAO.search(SearchCond.getLeaf(booleanCond), AnyTypeKind.GROUP); + assertNotNull(matchingGroups); + assertFalse(matchingGroups.isEmpty()); + } + + @Test + public void searchByUsernameAndKey() { + AnyCond usernameLeafCond = new AnyCond(AnyCond.Type.LIKE); + usernameLeafCond.setSchema("username"); + usernameLeafCond.setExpression("%ini"); + + AnyCond idRightCond = new AnyCond(AnyCond.Type.LT); + idRightCond.setSchema("key"); + idRightCond.setExpression("2"); + + SearchCond searchCondition = SearchCond.getAnd( + SearchCond.getLeaf(usernameLeafCond), + SearchCond.getLeaf(idRightCond)); + + List matching = searchDAO.search(searchCondition, AnyTypeKind.USER); + assertNotNull(matching); + assertEquals(1, matching.size()); + assertEquals("rossini", matching.iterator().next().getUsername()); + assertEquals("1417acbe-cbf6-4277-9372-e75e04f97000", matching.iterator().next().getKey()); + } + + @Test + public void searchByGroupNameAndKey() { + AnyCond groupNameLeafCond = new AnyCond(AnyCond.Type.EQ); + groupNameLeafCond.setSchema("name"); + groupNameLeafCond.setExpression("root"); + + AnyCond idRightCond = new AnyCond(AnyCond.Type.EQ); + idRightCond.setSchema("key"); + idRightCond.setExpression("37d15e4c-cdc1-460b-a591-8505c8133806"); + + SearchCond searchCondition = SearchCond.getAnd( + SearchCond.getLeaf(groupNameLeafCond), + SearchCond.getLeaf(idRightCond)); + + assertTrue(searchCondition.isValid()); + + List matching = searchDAO.search(searchCondition, AnyTypeKind.GROUP); + assertNotNull(matching); + assertEquals(1, matching.size()); + assertEquals("root", matching.iterator().next().getName()); + assertEquals("37d15e4c-cdc1-460b-a591-8505c8133806", matching.iterator().next().getKey()); + } + + @Test + public void searchByUsernameAndFullname() { + AnyCond usernameLeafCond = new AnyCond(AnyCond.Type.EQ); + usernameLeafCond.setSchema("username"); + usernameLeafCond.setExpression("rossini"); + + AttrCond idRightCond = new AttrCond(AttrCond.Type.LIKE); + idRightCond.setSchema("fullname"); + idRightCond.setExpression("Giuseppe V%"); + + SearchCond searchCondition = SearchCond.getOr( + SearchCond.getLeaf(usernameLeafCond), + SearchCond.getLeaf(idRightCond)); + + List matchingUsers = searchDAO.search(searchCondition, AnyTypeKind.USER); + assertNotNull(matchingUsers); + assertEquals(2, matchingUsers.size()); + } + + @Test + public void searchByUsernameAndFullnameIgnoreCase() { + AnyCond usernameLeafCond = new AnyCond(AnyCond.Type.IEQ); + usernameLeafCond.setSchema("username"); + usernameLeafCond.setExpression("RoSsini"); + + AttrCond idRightCond = new AttrCond(AttrCond.Type.ILIKE); + idRightCond.setSchema("fullname"); + idRightCond.setExpression("gIuseppe v%"); + + SearchCond searchCondition = SearchCond.getOr( + SearchCond.getLeaf(usernameLeafCond), + SearchCond.getLeaf(idRightCond)); + + List matchingUsers = searchDAO.search(searchCondition, AnyTypeKind.USER); + assertNotNull(matchingUsers); + assertEquals(2, matchingUsers.size()); + } + + @Test + public void searchByKey() { + AnyCond idLeafCond = new AnyCond(AnyCond.Type.EQ); + idLeafCond.setSchema("key"); + idLeafCond.setExpression("74cd8ece-715a-44a4-a736-e17b46c4e7e6"); + + SearchCond searchCondition = SearchCond.getLeaf(idLeafCond); + assertTrue(searchCondition.isValid()); + + List users = searchDAO.search(searchCondition, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + assertEquals("74cd8ece-715a-44a4-a736-e17b46c4e7e6", users.iterator().next().getKey()); + } + + @Test + public void searchByType() { + AnyTypeCond tcond = new AnyTypeCond(); + tcond.setAnyTypeKey("PRINTER"); + + SearchCond searchCondition = SearchCond.getLeaf(tcond); + assertTrue(searchCondition.isValid()); + + List printers = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT); + assertNotNull(printers); + assertEquals(3, printers.size()); + + tcond.setAnyTypeKey("UNEXISTING"); + printers = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT); + assertNotNull(printers); + assertTrue(printers.isEmpty()); + } + + @Test + public void searchByRelationshipType() { + // 1. first search for printers involved in "neighborhood" relationship + RelationshipTypeCond relationshipTypeCond = new RelationshipTypeCond(); + relationshipTypeCond.setRelationshipTypeKey("neighborhood"); + + AnyTypeCond tcond = new AnyTypeCond(); + tcond.setAnyTypeKey("PRINTER"); + + SearchCond searchCondition = SearchCond.getAnd( + SearchCond.getLeaf(relationshipTypeCond), SearchCond.getLeaf(tcond)); + assertTrue(searchCondition.isValid()); + + List anyObjects = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT); + assertNotNull(anyObjects); + assertEquals(2, anyObjects.size()); + assertTrue(anyObjects.stream().anyMatch(any -> "fc6dbc3a-6c07-4965-8781-921e7401a4a5".equals(any.getKey()))); + assertTrue(anyObjects.stream().anyMatch(any -> "8559d14d-58c2-46eb-a2d4-a7d35161e8f8".equals(any.getKey()))); + + // 2. search for users involved in "neighborhood" relationship + searchCondition = SearchCond.getLeaf(relationshipTypeCond); + List users = searchDAO.search(searchCondition, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + assertTrue(users.stream().anyMatch(any -> "c9b2dec2-00a7-4855-97c0-d854842b4b24".equals(any.getKey()))); + } + + @Test + public void searchByAnyCondDate() { + AnyCond creationDateCond = new AnyCond(AnyCond.Type.EQ); + creationDateCond.setSchema("creationDate"); + creationDateCond.setExpression("2021-04-15 12:45:00"); + + SearchCond searchCondition = SearchCond.getLeaf(creationDateCond); + assertTrue(searchCondition.isValid()); + + List anyObjects = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT); + assertNotNull(anyObjects); + assertEquals(1, anyObjects.size()); + assertEquals("9e1d130c-d6a3-48b1-98b3-182477ed0688", anyObjects.iterator().next().getKey()); + } + + @Test + public void searchByAttrCondDate() { + AttrCond loginDateCond = new AttrCond(AnyCond.Type.LT); + loginDateCond.setSchema("loginDate"); + loginDateCond.setExpression("2009-05-27"); + + SearchCond searchCondition = SearchCond.getLeaf(loginDateCond); + assertTrue(searchCondition.isValid()); + + List users = searchDAO.search(searchCondition, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + assertEquals("1417acbe-cbf6-4277-9372-e75e04f97000", users.iterator().next().getKey()); + } + + @Test + public void userOrderBy() { + AnyCond usernameLeafCond = new AnyCond(AnyCond.Type.EQ); + usernameLeafCond.setSchema("username"); + usernameLeafCond.setExpression("rossini"); + AttrCond idRightCond = new AttrCond(AttrCond.Type.LIKE); + idRightCond.setSchema("fullname"); + idRightCond.setExpression("Giuseppe V%"); + SearchCond searchCondition = SearchCond.getOr( + SearchCond.getLeaf(usernameLeafCond), SearchCond.getLeaf(idRightCond)); + + List orderByClauses = new ArrayList<>(); + orderByClauses.add(new Sort.Order(Sort.Direction.DESC, "username")); + orderByClauses.add(new Sort.Order(Sort.Direction.ASC, "fullname")); + + List users = searchDAO.search(searchCondition, orderByClauses, AnyTypeKind.USER); + assertEquals( + searchDAO.count( + realmDAO.getRoot(), true, + SyncopeConstants.FULL_ADMIN_REALMS, searchCondition, AnyTypeKind.USER), + users.size()); + } + + @Test + public void groupOrderBy() { + AnyCond idLeafCond = new AnyCond(AnyCond.Type.LIKE); + idLeafCond.setSchema("name"); + idLeafCond.setExpression("%r"); + SearchCond searchCondition = SearchCond.getLeaf(idLeafCond); + assertTrue(searchCondition.isValid()); + + Sort.Order orderByClause = new Sort.Order(Sort.DEFAULT_DIRECTION, "name"); + + List groups = searchDAO.search( + searchCondition, List.of(orderByClause), AnyTypeKind.GROUP); + assertEquals( + searchDAO.count( + realmDAO.getRoot(), true, + SyncopeConstants.FULL_ADMIN_REALMS, searchCondition, AnyTypeKind.GROUP), + groups.size()); + } + + @Test + public void member() { + MemberCond memberCond = new MemberCond(); + memberCond.setMember("1417acbe-cbf6-4277-9372-e75e04f97000"); + SearchCond searchCondition = SearchCond.getLeaf(memberCond); + assertTrue(searchCondition.isValid()); + + List groups = searchDAO.search(searchCondition, AnyTypeKind.GROUP); + assertEquals(2, groups.size()); + assertTrue(groups.contains(groupDAO.findByName("root").orElseThrow())); + assertTrue(groups.contains(groupDAO.findByName("otherchild").orElseThrow())); + } + + @Test + public void asGroupOwner() { + // prepare authentication + Map> entForRealms = new HashMap<>(); + roleDAO.findById(RoleDAO.GROUP_OWNER_ROLE).orElseThrow().getEntitlements().forEach(entitlement -> { + Set realms = Optional.ofNullable(entForRealms.get(entitlement)).orElseGet(() -> { + Set r = new HashSet<>(); + entForRealms.put(entitlement, r); + return r; + }); + + realms.add(RealmUtils.getGroupOwnerRealm( + SyncopeConstants.ROOT_REALM, "37d15e4c-cdc1-460b-a591-8505c8133806")); + }); + + Set authorities = new HashSet<>(); + entForRealms.forEach((key, value) -> { + SyncopeGrantedAuthority authority = new SyncopeGrantedAuthority(key); + authority.addRealms(value); + authorities.add(authority); + }); + + UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken( + new org.springframework.security.core.userdetails.User( + "poorGroupOwner", "FAKE_PASSWORD", authorities), "FAKE_PASSWORD", authorities); + auth.setDetails(new SyncopeAuthenticationDetails(SyncopeConstants.MASTER_DOMAIN, null)); + + SecurityContextHolder.getContext().setAuthentication(auth); + try { + // test count() and search() + Set authRealms = RealmUtils.getEffective( + AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.GROUP_SEARCH), + SyncopeConstants.ROOT_REALM); + + assertEquals( + 1, + searchDAO.count( + realmDAO.getRoot(), true, authRealms, groupDAO.getAllMatchingCond(), AnyTypeKind.GROUP)); + + List groups = searchDAO.search( + realmDAO.getRoot(), + true, + authRealms, + groupDAO.getAllMatchingCond(), + PageRequest.of(0, 10), + AnyTypeKind.GROUP); + assertEquals(1, groups.size()); + assertEquals("37d15e4c-cdc1-460b-a591-8505c8133806", groups.get(0).getKey()); + } finally { + SecurityContextHolder.getContext().setAuthentication(null); + } + } + + @Test + public void changePwdDate() { + AnyCond statusCond = new AnyCond(AttrCond.Type.IEQ); + statusCond.setSchema("status"); + statusCond.setExpression("suspended"); + + AnyCond changePwdDateCond = new AnyCond(AttrCond.Type.ISNULL); + changePwdDateCond.setSchema("changePwdDate"); + + SearchCond cond = SearchCond.getAnd(SearchCond.getNotLeaf(statusCond), SearchCond.getLeaf(changePwdDateCond)); + assertTrue(cond.isValid()); + + List users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(5, users.size()); + } + + @Test + public void issue202() { + ResourceCond ws2 = new ResourceCond(); + ws2.setResource("ws-target-resource-2"); + + ResourceCond ws1 = new ResourceCond(); + ws1.setResource("ws-target-resource-list-mappings-1"); + + SearchCond searchCondition = + SearchCond.getAnd(SearchCond.getNotLeaf(ws2), SearchCond.getNotLeaf(ws1)); + assertTrue(searchCondition.isValid()); + + List users = searchDAO.search(searchCondition, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(2, users.size()); + assertTrue(users.stream().anyMatch(user -> "c9b2dec2-00a7-4855-97c0-d854842b4b24".equals(user.getKey()))); + } + + @Test + public void issue242() { + AnyCond cond = new AnyCond(AttrCond.Type.LIKE); + cond.setSchema("key"); + cond.setExpression("test%"); + + SearchCond searchCondition = SearchCond.getLeaf(cond); + assertTrue(searchCondition.isValid()); + + List users = searchDAO.search(searchCondition, AnyTypeKind.USER); + assertNotNull(users); + assertTrue(users.isEmpty()); + } + + @Test + public void issueSYNCOPE46() { + AnyCond cond = new AnyCond(AttrCond.Type.LIKE); + cond.setSchema("username"); + cond.setExpression("%ossin%"); + + SearchCond searchCondition = SearchCond.getLeaf(cond); + assertTrue(searchCondition.isValid()); + + List users = searchDAO.search(searchCondition, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + } + + @Test + public void issueSYNCOPE433() { + AttrCond isNullCond = new AttrCond(AttrCond.Type.ISNULL); + isNullCond.setSchema("loginDate"); + + AnyCond likeCond = new AnyCond(AttrCond.Type.LIKE); + likeCond.setSchema("username"); + likeCond.setExpression("%ossin%"); + + SearchCond searchCond = SearchCond.getOr( + SearchCond.getLeaf(isNullCond), SearchCond.getLeaf(likeCond)); + + long count = searchDAO.count( + realmDAO.getRoot(), true, SyncopeConstants.FULL_ADMIN_REALMS, searchCond, AnyTypeKind.USER); + assertTrue(count > 0); + } + + @Test + public void issueSYNCOPE929() { + AttrCond rossiniCond = new AttrCond(AttrCond.Type.EQ); + rossiniCond.setSchema("surname"); + rossiniCond.setExpression("Rossini"); + + AttrCond genderCond = new AttrCond(AttrCond.Type.EQ); + genderCond.setSchema("gender"); + genderCond.setExpression("M"); + + SearchCond orCond = SearchCond.getOr(SearchCond.getLeaf(rossiniCond), SearchCond.getLeaf(genderCond)); + + AttrCond belliniCond = new AttrCond(AttrCond.Type.EQ); + belliniCond.setSchema("surname"); + belliniCond.setExpression("Bellini"); + + SearchCond searchCond = SearchCond.getAnd(orCond, SearchCond.getLeaf(belliniCond)); + + List users = searchDAO.search(searchCond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + } + + @Test + public void issueSYNCOPE980() { + AnyType service = entityFactory.newEntity(AnyType.class); + service.setKey("SERVICE"); + service.setKind(AnyTypeKind.ANY_OBJECT); + service = anyTypeDAO.save(service); + + Group citizen = groupDAO.findByName("citizen").orElseThrow(); + assertNotNull(citizen); + + AnyObject anyObject = entityFactory.newEntity(AnyObject.class); + anyObject.setName("one"); + anyObject.setType(service); + anyObject.setRealm(realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); + + AMembership membership = entityFactory.newEntity(AMembership.class); + membership.setRightEnd(citizen); + membership.setLeftEnd(anyObject); + + anyObject.add(membership); + anyObjectDAO.save(anyObject); + + anyObject = anyObjectDAO.findById("fc6dbc3a-6c07-4965-8781-921e7401a4a5").orElseThrow(); + membership = entityFactory.newEntity(AMembership.class); + membership.setRightEnd(citizen); + membership.setLeftEnd(anyObject); + anyObject.add(membership); + anyObjectDAO.save(anyObject); + + MembershipCond groupCond = new MembershipCond(); + groupCond.setGroup("citizen"); + + SearchCond searchCondition = SearchCond.getLeaf(groupCond); + + List matching = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT); + assertEquals(2, matching.size()); + + AnyTypeCond anyTypeCond = new AnyTypeCond(); + anyTypeCond.setAnyTypeKey(service.getKey()); + + searchCondition = SearchCond.getAnd(SearchCond.getLeaf(groupCond), SearchCond.getLeaf(anyTypeCond)); + + matching = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT); + assertEquals(1, matching.size()); + } + + @Test + public void issueSYNCOPE983() { + AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.LIKE); + fullnameLeafCond.setSchema("surname"); + fullnameLeafCond.setExpression("%o%"); + + List orderByClauses = new ArrayList<>(); + orderByClauses.add(new Sort.Order(Sort.Direction.ASC, "surname")); + orderByClauses.add(new Sort.Order(Sort.Direction.DESC, "username")); + + List users = searchDAO.search( + realmDAO.getRoot(), + true, + SyncopeConstants.FULL_ADMIN_REALMS, + SearchCond.getLeaf(fullnameLeafCond), + Pageable.unpaged(Sort.by(orderByClauses)), + AnyTypeKind.USER); + assertFalse(users.isEmpty()); + } + + @Test + public void issueSYNCOPE1416() { + AttrCond idLeftCond = new AttrCond(AttrCond.Type.ISNOTNULL); + idLeftCond.setSchema("surname"); + + AttrCond idRightCond = new AttrCond(AttrCond.Type.ISNOTNULL); + idRightCond.setSchema("firstname"); + + SearchCond searchCondition = SearchCond.getAnd(SearchCond.getLeaf(idLeftCond), SearchCond.getLeaf(idRightCond)); + + List orderByClauses = List.of(new Sort.Order(Sort.Direction.ASC, "ctype")); + + List users = searchDAO.search(searchCondition, orderByClauses, AnyTypeKind.USER); + assertEquals(searchDAO.count( + realmDAO.getRoot(), true, SyncopeConstants.FULL_ADMIN_REALMS, searchCondition, AnyTypeKind.USER), + users.size()); + + // search by attribute with unique constraint + AttrCond fullnameCond = new AttrCond(AttrCond.Type.ISNOTNULL); + fullnameCond.setSchema("fullname"); + + SearchCond cond = SearchCond.getLeaf(fullnameCond); + assertTrue(cond.isValid()); + + users = searchDAO.search(cond, AnyTypeKind.USER); + assertEquals(5, users.size()); + + fullnameCond = new AttrCond(AttrCond.Type.ISNULL); + fullnameCond.setSchema("fullname"); + + cond = SearchCond.getLeaf(fullnameCond); + assertTrue(cond.isValid()); + + users = searchDAO.search(cond, AnyTypeKind.USER); + assertTrue(users.isEmpty()); + } + + @Test + public void issueSYNCOPE1419() { + AttrCond loginDateCond = new AttrCond(AttrCond.Type.EQ); + loginDateCond.setSchema("loginDate"); + loginDateCond.setExpression(LOGIN_DATE_VALUE); + + SearchCond cond = SearchCond.getNotLeaf(loginDateCond); + assertTrue(cond.isValid()); + + List users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(4, users.size()); + } +} 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 new file mode 100644 index 0000000000..5feea372e6 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyTypeClassTest.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.persistence.neo4j.inner; + +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.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.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class AnyTypeClassTest extends AbstractTest { + + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + @Autowired + private AnyTypeClassDAO anyTypeClassDAO; + + @Test + public void find() { + AnyTypeClass minimalGroup = anyTypeClassDAO.findById("minimal group").orElseThrow(); + + assertFalse(minimalGroup.getPlainSchemas().isEmpty()); + assertFalse(minimalGroup.getDerSchemas().isEmpty()); + assertFalse(minimalGroup.getVirSchemas().isEmpty()); + } + + @Test + public void findAll() { + List list = anyTypeClassDAO.findAll(); + assertFalse(list.isEmpty()); + } + + @Test + public void save() { + PlainSchema firstname = plainSchemaDAO.findById("firstname").orElseThrow(); + + AnyTypeClass newClass = entityFactory.newEntity(AnyTypeClass.class); + newClass.setKey("new class"); + newClass.add(firstname); + + firstname.setAnyTypeClass(newClass); + plainSchemaDAO.save(firstname); + + newClass = anyTypeClassDAO.save(newClass); + assertNotNull(newClass); + assertFalse(newClass.getPlainSchemas().isEmpty()); + assertTrue(newClass.getDerSchemas().isEmpty()); + assertTrue(newClass.getVirSchemas().isEmpty()); + + firstname = plainSchemaDAO.findById("firstname").orElseThrow(); + assertEquals(newClass, firstname.getAnyTypeClass()); + } + + @Test + public void delete() { + AnyTypeClass minimalUser = anyTypeClassDAO.findById("minimal user").orElseThrow(); + assertNotNull(minimalUser); + + anyTypeClassDAO.deleteById(minimalUser.getKey()); + assertTrue(anyTypeClassDAO.findById("minimal user").isEmpty()); + } +} 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 new file mode 100644 index 0000000000..c66dfa3c21 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyTypeTest.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.persistence.neo4j.inner; + +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 org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class AnyTypeTest extends AbstractTest { + + @Autowired + private AnyTypeDAO anyTypeDAO; + + @Autowired + private AnyTypeClassDAO anyTypeClassDAO; + + @Test + public void find() { + AnyType userType = anyTypeDAO.getUser(); + assertNotNull(userType); + assertEquals(AnyTypeKind.USER, userType.getKind()); + assertEquals(AnyTypeKind.USER.name(), userType.getKey()); + assertFalse(userType.getClasses().isEmpty()); + + AnyType groupType = anyTypeDAO.getGroup(); + assertNotNull(groupType); + assertEquals(AnyTypeKind.GROUP, groupType.getKind()); + assertEquals(AnyTypeKind.GROUP.name(), groupType.getKey()); + assertFalse(groupType.getClasses().isEmpty()); + + AnyType otherType = anyTypeDAO.findById("PRINTER").orElseThrow(); + assertEquals(AnyTypeKind.ANY_OBJECT, otherType.getKind()); + assertEquals("PRINTER", otherType.getKey()); + } + + @Test + public void findAll() { + List list = anyTypeDAO.findAll(); + assertFalse(list.isEmpty()); + } + + @Test + public void save() { + AnyType newType = entityFactory.newEntity(AnyType.class); + newType.setKey("new type"); + newType.setKind(AnyTypeKind.ANY_OBJECT); + newType.add(anyTypeClassDAO.findById("generic membership").orElseThrow()); + newType.add(anyTypeClassDAO.findById("csv").orElseThrow()); + + newType = anyTypeDAO.save(newType); + assertNotNull(newType); + assertFalse(newType.getClasses().isEmpty()); + } + + @Test + public void saveInvalidKind() { + assertThrows(InvalidEntityException.class, () -> { + AnyType newType = entityFactory.newEntity(AnyType.class); + newType.setKey("new type"); + newType.setKind(AnyTypeKind.USER); + anyTypeDAO.save(newType); + }); + } + + @Test + public void saveInvalidName() { + assertThrows(InvalidEntityException.class, () -> { + AnyType newType = entityFactory.newEntity(AnyType.class); + newType.setKey("group"); + newType.setKind(AnyTypeKind.ANY_OBJECT); + anyTypeDAO.save(newType); + }); + } + + @Test + public void delete() { + AnyType otherType = anyTypeDAO.findById("PRINTER").orElseThrow(); + assertNotNull(otherType); + + anyTypeDAO.deleteById(otherType.getKey()); + assertTrue(anyTypeDAO.findById("PRINTER").isEmpty()); + } + + @Test + public 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/ApplicationTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ApplicationTest.java new file mode 100644 index 0000000000..afb71ace8b --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ApplicationTest.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.inner; + +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.util.List; +import java.util.UUID; +import org.apache.syncope.core.persistence.api.dao.ApplicationDAO; +import org.apache.syncope.core.persistence.api.entity.Application; +import org.apache.syncope.core.persistence.api.entity.Privilege; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class ApplicationTest extends AbstractTest { + + @Autowired + private ApplicationDAO applicationDAO; + + @Test + public void findAll() { + List applications = applicationDAO.findAll(); + assertFalse(applications.isEmpty()); + assertEquals(1, applications.size()); + } + + @Test + public void find() { + Application mightyApp = applicationDAO.findById("mightyApp").orElseThrow(); + assertEquals(2, mightyApp.getPrivileges().size()); + + Privilege getMighty = applicationDAO.findPrivilege("getMighty").orElseThrow(); + assertEquals(getMighty, mightyApp.getPrivilege("getMighty").get()); + + } + + @Test + public void crud() { + // 1. create application + Application application = entityFactory.newEntity(Application.class); + application.setKey(UUID.randomUUID().toString()); + + String privilege1Key = UUID.randomUUID().toString(); + Privilege privilege = entityFactory.newEntity(Privilege.class); + privilege.setKey(privilege1Key); + privilege.setSpec("{ \"one\": true }"); + application.add(privilege); + + String privilege2Key = UUID.randomUUID().toString(); + privilege = entityFactory.newEntity(Privilege.class); + privilege.setKey(privilege2Key); + privilege.setSpec("{ \"two\": true }"); + application.add(privilege); + + String privilege3Key = UUID.randomUUID().toString(); + privilege = entityFactory.newEntity(Privilege.class); + privilege.setKey(privilege3Key); + privilege.setSpec("{ \"three\": true }"); + application.add(privilege); + + application = applicationDAO.save(application); + assertNotNull(application); + assertNull(application.getDescription()); + assertEquals(3, application.getPrivileges().size()); + + // 2. update application + application.setDescription("A description"); + + Privilege priv3 = applicationDAO.findPrivilege(privilege3Key).orElseThrow(); + priv3.setApplication(null); + application.getPrivileges().remove(priv3); + assertEquals(2, application.getPrivileges().size()); + + applicationDAO.save(application); + + application = applicationDAO.findById(application.getKey()).orElseThrow(); + assertNotNull(application.getDescription()); + assertEquals(2, application.getPrivileges().size()); + + // 3. delete application + applicationDAO.delete(application); + + assertTrue(applicationDAO.findById(application.getKey()).isEmpty()); + assertTrue(applicationDAO.findPrivilege(privilege1Key).isEmpty()); + assertTrue(applicationDAO.findPrivilege(privilege2Key).isEmpty()); + } +} 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 new file mode 100644 index 0000000000..b10328594c --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AttrRepoTest.java @@ -0,0 +1,227 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.inner; + +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 java.util.UUID; +import org.apache.commons.lang3.ClassUtils; +import org.apache.syncope.common.lib.attr.AttrRepoConf; +import org.apache.syncope.common.lib.attr.JDBCAttrRepoConf; +import org.apache.syncope.common.lib.attr.LDAPAttrRepoConf; +import org.apache.syncope.common.lib.attr.StubAttrRepoConf; +import org.apache.syncope.common.lib.attr.SyncopeAttrRepoConf; +import org.apache.syncope.common.lib.to.Item; +import org.apache.syncope.common.lib.types.AttrRepoState; +import org.apache.syncope.core.persistence.api.dao.AttrRepoDAO; +import org.apache.syncope.core.persistence.api.entity.am.AttrRepo; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class AttrRepoTest extends AbstractTest { + + private static boolean isSpecificConf(final AttrRepoConf conf, final Class clazz) { + return ClassUtils.isAssignable(clazz, conf.getClass()); + } + + @Autowired + private AttrRepoDAO attrRepoDAO; + + @Test + public void findAll() { + List modules = attrRepoDAO.findAll(); + assertNotNull(modules); + assertFalse(modules.isEmpty()); + assertTrue(modules.size() >= 4); + } + + @Test + public void find() { + AttrRepo attrRepo = attrRepoDAO.findById("DefaultLDAPAttrRepo").orElseThrow(); + assertTrue(attrRepo.getConf() instanceof LDAPAttrRepoConf); + + attrRepo = attrRepoDAO.findById("DefaultJDBCAttrRepo").orElseThrow(); + assertTrue(attrRepo.getConf() instanceof JDBCAttrRepoConf); + + attrRepo = attrRepoDAO.findById("DefaultStubAttrRepo").orElseThrow(); + assertTrue(attrRepo.getConf() instanceof StubAttrRepoConf); + assertEquals(1, attrRepo.getItems().size()); + + attrRepo = attrRepoDAO.findById("DefaultSyncopeAttrRepo").orElseThrow(); + assertTrue(attrRepo.getConf() instanceof SyncopeAttrRepoConf); + } + + @Test + public void findByType() { + List attrRepos = attrRepoDAO.findAll(); + assertTrue(attrRepos.stream().anyMatch( + attrRepo -> isSpecificConf(attrRepo.getConf(), LDAPAttrRepoConf.class) + && attrRepo.getKey().equals("DefaultLDAPAttrRepo"))); + assertTrue(attrRepos.stream().anyMatch( + attrRepo -> isSpecificConf(attrRepo.getConf(), JDBCAttrRepoConf.class) + && attrRepo.getKey().equals("DefaultJDBCAttrRepo"))); + assertTrue(attrRepos.stream().anyMatch( + attrRepo -> isSpecificConf(attrRepo.getConf(), SyncopeAttrRepoConf.class) + && attrRepo.getKey().equals("DefaultSyncopeAttrRepo"))); + assertTrue(attrRepos.stream().anyMatch( + attrRepo -> isSpecificConf(attrRepo.getConf(), StubAttrRepoConf.class) + && attrRepo.getKey().equals("DefaultStubAttrRepo"))); + } + + private void saveAttrRepo(final String key, final AttrRepoConf conf) { + AttrRepo attrRepo = entityFactory.newEntity(AttrRepo.class); + attrRepo.setKey(key); + attrRepo.setDescription("An attr repo"); + attrRepo.setState(AttrRepoState.ACTIVE); + attrRepo.setConf(conf); + + Item keyMapping = new Item(); + keyMapping.setIntAttrName("uid"); + keyMapping.setExtAttrName("username"); + attrRepo.getItems().add(keyMapping); + + Item fullnameMapping = new Item(); + fullnameMapping.setIntAttrName("cn"); + fullnameMapping.setExtAttrName("fullname"); + attrRepo.getItems().add(fullnameMapping); + + attrRepo = attrRepoDAO.save(attrRepo); + + assertNotNull(attrRepo); + assertNotNull(attrRepo.getKey()); + assertEquals(attrRepo, attrRepoDAO.findById(attrRepo.getKey()).orElseThrow()); + assertEquals(2, attrRepo.getItems().size()); + } + + @Test + public void saveWithStubRepo() { + StubAttrRepoConf conf = new StubAttrRepoConf(); + conf.getAttributes().put("attr1", UUID.randomUUID().toString()); + conf.getAttributes().put("attr2", UUID.randomUUID().toString()); + + saveAttrRepo("StaticAttrRepoTest", conf); + } + + @Test + public void saveWithLdapRepo() { + LDAPAttrRepoConf conf = new LDAPAttrRepoConf(); + conf.setBaseDn("dc=example,dc=org"); + conf.setSearchFilter("cn={user}"); + conf.setSubtreeSearch(true); + conf.setLdapUrl("ldap://localhost:1389"); + conf.setBindCredential("Password"); + + saveAttrRepo("LDAPAttrRepoTest", conf); + } + + @Test + public void saveWithJDBCRepo() { + JDBCAttrRepoConf conf = new JDBCAttrRepoConf(); + conf.setSql("SELECT * FROM table WHERE name=?"); + conf.setUrl("jdbc:h2:mem:syncopedb;DB_CLOSE_DELAY=-1"); + conf.setUser("username"); + conf.setPassword("password"); + + saveAttrRepo("JDBCAttrRepoTest", conf); + } + + @Test + public void saveWithSyncopeRepo() { + SyncopeAttrRepoConf conf = new SyncopeAttrRepoConf(); + conf.setDomain("Master"); + + saveAttrRepo("SyncopeAttrRepoTest", conf); + } + + @Test + public void updateWithLDAPRepo() { + AttrRepo module = attrRepoDAO.findById("DefaultLDAPAttrRepo").orElseThrow(); + AttrRepoConf conf = module.getConf(); + LDAPAttrRepoConf.class.cast(conf).setBaseDn("dc=example2,dc=org"); + LDAPAttrRepoConf.class.cast(conf).setSearchFilter("cn={user2}"); + module.setConf(conf); + + module = attrRepoDAO.save(module); + assertNotNull(module); + assertNotNull(module.getKey()); + + AttrRepo found = attrRepoDAO.findById(module.getKey()).orElseThrow(); + assertEquals("dc=example2,dc=org", LDAPAttrRepoConf.class.cast(found.getConf()).getBaseDn()); + assertEquals("cn={user2}", LDAPAttrRepoConf.class.cast(found.getConf()).getSearchFilter()); + } + + @Test + public void updateWithJDBCRepo() { + AttrRepo module = attrRepoDAO.findById("DefaultJDBCAttrRepo").orElseThrow(); + AttrRepoConf conf = module.getConf(); + JDBCAttrRepoConf.class.cast(conf).setSql("SELECT * FROM otherTable WHERE name=?"); + module.setConf(conf); + + module = attrRepoDAO.save(module); + assertNotNull(module); + assertNotNull(module.getKey()); + AttrRepo found = attrRepoDAO.findById(module.getKey()).orElseThrow(); + assertEquals("SELECT * FROM otherTable WHERE name=?", JDBCAttrRepoConf.class.cast(found.getConf()).getSql()); + } + + @Test + public void updateWithStubRepo() { + AttrRepo module = attrRepoDAO.findById("DefaultStubAttrRepo").orElseThrow(); + assertEquals(1, StubAttrRepoConf.class.cast(module.getConf()).getAttributes().size()); + AttrRepoConf conf = module.getConf(); + StubAttrRepoConf.class.cast(conf).getAttributes().put("attr3", UUID.randomUUID().toString()); + module.setConf(conf); + + module = attrRepoDAO.save(module); + assertNotNull(module.getKey()); + AttrRepo found = attrRepoDAO.findById(module.getKey()).orElseThrow(); + assertEquals(2, StubAttrRepoConf.class.cast(found.getConf()).getAttributes().size()); + } + + @Test + public void updateWithSyncopeRepo() { + AttrRepo module = attrRepoDAO.findById("DefaultSyncopeAttrRepo").orElseThrow(); + + AttrRepoConf conf = module.getConf(); + SyncopeAttrRepoConf.class.cast(conf).setDomain("Two"); + module.setConf(conf); + + module = attrRepoDAO.save(module); + assertNotNull(module); + assertNotNull(module.getKey()); + AttrRepo found = attrRepoDAO.findById(module.getKey()).orElseThrow(); + assertEquals("Two", SyncopeAttrRepoConf.class.cast(found.getConf()).getDomain()); + } + + @Test + public void delete() { + assertTrue(attrRepoDAO.findById("DefaultSyncopeAttrRepo").isPresent()); + + attrRepoDAO.deleteById("DefaultSyncopeAttrRepo"); + + assertTrue(attrRepoDAO.findById("DefaultSyncopeAttrRepo").isEmpty()); + } +} 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 new file mode 100644 index 0000000000..8443eaa69b --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthModuleTest.java @@ -0,0 +1,399 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.inner; + +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 java.util.UUID; +import org.apache.commons.lang3.ClassUtils; +import org.apache.syncope.common.lib.auth.AuthModuleConf; +import org.apache.syncope.common.lib.auth.DuoMfaAuthModuleConf; +import org.apache.syncope.common.lib.auth.GoogleMfaAuthModuleConf; +import org.apache.syncope.common.lib.auth.JDBCAuthModuleConf; +import org.apache.syncope.common.lib.auth.JaasAuthModuleConf; +import org.apache.syncope.common.lib.auth.LDAPAuthModuleConf; +import org.apache.syncope.common.lib.auth.OIDCAuthModuleConf; +import org.apache.syncope.common.lib.auth.SAML2IdPAuthModuleConf; +import org.apache.syncope.common.lib.auth.SimpleMfaAuthModuleConf; +import org.apache.syncope.common.lib.auth.StaticAuthModuleConf; +import org.apache.syncope.common.lib.auth.SyncopeAuthModuleConf; +import org.apache.syncope.common.lib.to.Item; +import org.apache.syncope.common.lib.types.AuthModuleState; +import org.apache.syncope.core.persistence.api.dao.AuthModuleDAO; +import org.apache.syncope.core.persistence.api.entity.am.AuthModule; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class AuthModuleTest extends AbstractTest { + + private static boolean isSpecificConf(final AuthModuleConf conf, final Class clazz) { + return ClassUtils.isAssignable(clazz, conf.getClass()); + } + + @Autowired + private AuthModuleDAO authModuleDAO; + + @Test + public void findAll() { + List modules = authModuleDAO.findAll(); + assertNotNull(modules); + assertFalse(modules.isEmpty()); + assertTrue(modules.size() >= 10); + } + + @Test + public void find() { + AuthModule authModule = authModuleDAO.findById("DefaultLDAPAuthModule").orElseThrow(); + assertTrue(authModule.getConf() instanceof LDAPAuthModuleConf); + + authModule = authModuleDAO.findById("DefaultSimpleMfaAuthModule").orElseThrow(); + assertTrue(authModule.getConf() instanceof SimpleMfaAuthModuleConf); + + authModule = authModuleDAO.findById("DefaultJDBCAuthModule").orElseThrow(); + assertTrue(authModule.getConf() instanceof JDBCAuthModuleConf); + + authModule = authModuleDAO.findById("DefaultGoogleMfaAuthModule").orElseThrow(); + assertTrue(authModule.getConf() instanceof GoogleMfaAuthModuleConf); + + authModule = authModuleDAO.findById("DefaultDuoMfaAuthModule").orElseThrow(); + assertTrue(authModule.getConf() instanceof DuoMfaAuthModuleConf); + + authModule = authModuleDAO.findById("DefaultOIDCAuthModule").orElseThrow(); + assertTrue(authModule.getConf() instanceof OIDCAuthModuleConf); + + authModule = authModuleDAO.findById("DefaultSAML2IdPAuthModule").orElseThrow(); + assertTrue(authModule.getConf() instanceof SAML2IdPAuthModuleConf); + + authModule = authModuleDAO.findById("DefaultJaasAuthModule").orElseThrow(); + assertTrue(authModule.getConf() instanceof JaasAuthModuleConf); + + authModule = authModuleDAO.findById("DefaultStaticAuthModule").orElseThrow(); + assertTrue(authModule.getConf() instanceof StaticAuthModuleConf); + + authModule = authModuleDAO.findById("DefaultSyncopeAuthModule").orElseThrow(); + assertTrue(authModule.getConf() instanceof SyncopeAuthModuleConf); + } + + @Test + public void findByType() { + List authModules = authModuleDAO.findAll(); + assertTrue(authModules.stream().anyMatch( + authModule -> isSpecificConf(authModule.getConf(), LDAPAuthModuleConf.class) + && authModule.getKey().equals("DefaultLDAPAuthModule"))); + assertTrue(authModules.stream().anyMatch( + authModule -> isSpecificConf(authModule.getConf(), JDBCAuthModuleConf.class) + && authModule.getKey().equals("DefaultJDBCAuthModule"))); + assertTrue(authModules.stream().anyMatch( + authModule -> isSpecificConf(authModule.getConf(), GoogleMfaAuthModuleConf.class) + && authModule.getKey().equals("DefaultGoogleMfaAuthModule"))); + assertTrue(authModules.stream().anyMatch( + authModule -> isSpecificConf(authModule.getConf(), DuoMfaAuthModuleConf.class) + && authModule.getKey().equals("DefaultDuoMfaAuthModule"))); + assertTrue(authModules.stream().anyMatch( + authModule -> isSpecificConf(authModule.getConf(), OIDCAuthModuleConf.class) + && authModule.getKey().equals("DefaultOIDCAuthModule"))); + assertTrue(authModules.stream().anyMatch( + authModule -> isSpecificConf(authModule.getConf(), SAML2IdPAuthModuleConf.class) + && authModule.getKey().equals("DefaultSAML2IdPAuthModule"))); + assertTrue(authModules.stream().anyMatch( + authModule -> isSpecificConf(authModule.getConf(), JaasAuthModuleConf.class) + && authModule.getKey().equals("DefaultJaasAuthModule"))); + assertTrue(authModules.stream().anyMatch( + authModule -> isSpecificConf(authModule.getConf(), StaticAuthModuleConf.class) + && authModule.getKey().equals("DefaultStaticAuthModule"))); + assertTrue(authModules.stream().anyMatch( + authModule -> isSpecificConf(authModule.getConf(), SyncopeAuthModuleConf.class) + && authModule.getKey().equals("DefaultSyncopeAuthModule"))); + } + + private void saveAuthModule(final String key, final AuthModuleConf conf) { + AuthModule module = entityFactory.newEntity(AuthModule.class); + module.setKey(key); + module.setDescription("An authentication module"); + module.setState(AuthModuleState.ACTIVE); + module.setConf(conf); + + Item keyMapping = new Item(); + keyMapping.setIntAttrName("uid"); + keyMapping.setExtAttrName("username"); + module.getItems().add(keyMapping); + + Item fullnameMapping = new Item(); + fullnameMapping.setIntAttrName("cn"); + fullnameMapping.setExtAttrName("fullname"); + module.getItems().add(fullnameMapping); + + module = authModuleDAO.save(module); + + assertNotNull(module); + assertNotNull(module.getKey()); + assertEquals(module, authModuleDAO.findById(module.getKey()).orElseThrow()); + assertEquals(2, module.getItems().size()); + } + + @Test + public void saveWithStaticModule() { + StaticAuthModuleConf conf = new StaticAuthModuleConf(); + conf.getUsers().put("user1", UUID.randomUUID().toString()); + conf.getUsers().put("user2", "user2Password123"); + + saveAuthModule("StaticAuthModuleTest", conf); + } + + @Test + public void saveWithJaasModule() { + JaasAuthModuleConf conf = new JaasAuthModuleConf(); + conf.setKerberosKdcSystemProperty("sample-value"); + conf.setKerberosRealmSystemProperty("sample-value"); + conf.setLoginConfigType("JavaLoginConfig"); + conf.setRealm("SYNCOPE"); + conf.setLoginConfigurationFile("/opt/jaas/login.conf"); + + saveAuthModule("JaasAuthModuleTest", conf); + } + + @Test + public void saveWithLdapModule() { + LDAPAuthModuleConf conf = new LDAPAuthModuleConf(); + conf.setBaseDn("dc=example,dc=org"); + conf.setSearchFilter("cn={user}"); + conf.setSubtreeSearch(true); + conf.setLdapUrl("ldap://localhost:1389"); + conf.setPrincipalAttributeId("uid"); + conf.setBindCredential("Password"); + + saveAuthModule("LDAPAuthModuleTest", conf); + } + + @Test + public void saveWithGoogleAuthenticatorModule() { + GoogleMfaAuthModuleConf conf = new GoogleMfaAuthModuleConf(); + conf.setCodeDigits(6); + conf.setIssuer("SyncopeTest"); + conf.setLabel("Syncope"); + conf.setTimeStepSize(30); + conf.setWindowSize(3); + + saveAuthModule("GoogleMfaAuthModuleTest", conf); + } + + @Test + public void saveWithDuoAuthenticatorModule() { + DuoMfaAuthModuleConf conf = new DuoMfaAuthModuleConf(); + conf.setSecretKey("Q2IU2i6BFNd6VYflZT8Evl6lF7oPlj4PM15BmRU7"); + conf.setIntegrationKey("DIOXVRZD1UMZ8XXMNFQ6"); + conf.setApiHost("theapi.duosecurity.com"); + saveAuthModule("DuoMfaAuthModuleTest", conf); + } + + @Test + public void saveWithOIDCAuthModule() { + OIDCAuthModuleConf conf = new OIDCAuthModuleConf(); + conf.setClientId("OIDCTestId"); + conf.setDiscoveryUri("www.testurl.com"); + conf.setUserIdAttribute("username"); + conf.setResponseType("code"); + conf.setScope("openid email profile"); + + saveAuthModule("OIDCAuthModuleTest", conf); + } + + @Test + public void saveWithJDBCModule() { + JDBCAuthModuleConf conf = new JDBCAuthModuleConf(); + conf.setSql("SELECT * FROM table WHERE name=?"); + conf.setFieldPassword("password"); + + saveAuthModule("JDBCAuthModuleTest", conf); + } + + @Test + public void saveWithSyncopeModule() { + SyncopeAuthModuleConf conf = new SyncopeAuthModuleConf(); + conf.setDomain("Master"); + + saveAuthModule("SyncopeAuthModuleTest", conf); + } + + @Test + public void saveWithSAML2IdPModule() { + SAML2IdPAuthModuleConf conf = new SAML2IdPAuthModuleConf(); + conf.setServiceProviderEntityId("testEntityId"); + conf.setProviderName("testProviderName"); + saveAuthModule("SAML2IdPAuthModuleTest", conf); + } + + @Test + public void saveWithSimpleMfaModule() { + SimpleMfaAuthModuleConf conf = new SimpleMfaAuthModuleConf(); + conf.setTokenLength(9); + conf.setTimeToKillInSeconds(120); + saveAuthModule("SimpleMfaAuthModuleConf", conf); + } + + @Test + public void updateWithLDAPModule() { + AuthModule module = authModuleDAO.findById("DefaultLDAPAuthModule").orElseThrow(); + AuthModuleConf conf = module.getConf(); + LDAPAuthModuleConf.class.cast(conf).setBaseDn("dc=example2,dc=org"); + LDAPAuthModuleConf.class.cast(conf).setSearchFilter("cn={user2}"); + module.setConf(conf); + + module = authModuleDAO.save(module); + assertNotNull(module); + assertNotNull(module.getKey()); + + AuthModule found = authModuleDAO.findById(module.getKey()).orElseThrow(); + assertEquals("dc=example2,dc=org", LDAPAuthModuleConf.class.cast(found.getConf()).getBaseDn()); + assertEquals("cn={user2}", LDAPAuthModuleConf.class.cast(found.getConf()).getSearchFilter()); + } + + @Test + public void updateWithJDBCModule() { + AuthModule module = authModuleDAO.findById("DefaultJDBCAuthModule").orElseThrow(); + AuthModuleConf conf = module.getConf(); + JDBCAuthModuleConf.class.cast(conf).setSql("SELECT * FROM otherTable WHERE name=?"); + module.setConf(conf); + + module = authModuleDAO.save(module); + assertNotNull(module); + assertNotNull(module.getKey()); + AuthModule found = authModuleDAO.findById(module.getKey()).orElseThrow(); + assertEquals("SELECT * FROM otherTable WHERE name=?", JDBCAuthModuleConf.class.cast(found.getConf()).getSql()); + } + + @Test + public void updateWithGoogleMfaModule() { + AuthModule module = authModuleDAO.findById("DefaultGoogleMfaAuthModule").orElseThrow(); + AuthModuleConf conf = module.getConf(); + GoogleMfaAuthModuleConf.class.cast(conf).setLabel("newLabel"); + module.setConf(conf); + + module = authModuleDAO.save(module); + assertNotNull(module); + assertNotNull(module.getKey()); + AuthModule found = authModuleDAO.findById(module.getKey()).orElseThrow(); + assertEquals("newLabel", GoogleMfaAuthModuleConf.class.cast(found.getConf()).getLabel()); + } + + @Test + public void updateWithDuoMfaModule() { + AuthModule module = authModuleDAO.findById("DefaultDuoMfaAuthModule").orElseThrow(); + AuthModuleConf conf = module.getConf(); + String secretKey = UUID.randomUUID().toString(); + DuoMfaAuthModuleConf.class.cast(conf).setSecretKey(secretKey); + module.setConf(conf); + + module = authModuleDAO.save(module); + assertNotNull(module); + assertNotNull(module.getKey()); + AuthModule found = authModuleDAO.findById(module.getKey()).orElseThrow(); + assertEquals(secretKey, DuoMfaAuthModuleConf.class.cast(found.getConf()).getSecretKey()); + } + + @Test + public void updateWithSAML2IdPModule() { + AuthModule module = authModuleDAO.findById("DefaultSAML2IdPAuthModule").orElseThrow(); + AuthModuleConf conf = module.getConf(); + SAML2IdPAuthModuleConf.class.cast(conf).setServiceProviderEntityId("newEntityId"); + module.setConf(conf); + + module = authModuleDAO.save(module); + assertNotNull(module); + assertNotNull(module.getKey()); + AuthModule found = authModuleDAO.findById(module.getKey()).orElseThrow(); + assertNotNull(found); + assertEquals("newEntityId", SAML2IdPAuthModuleConf.class.cast(found.getConf()).getServiceProviderEntityId()); + } + + @Test + public void updateWithOIDCModule() { + AuthModule module = authModuleDAO.findById("DefaultOIDCAuthModule").orElseThrow(); + AuthModuleConf conf = module.getConf(); + OIDCAuthModuleConf.class.cast(conf).setResponseType("newCode"); + module.setConf(conf); + + module = authModuleDAO.save(module); + assertNotNull(module); + assertNotNull(module.getKey()); + AuthModule found = authModuleDAO.findById(module.getKey()).orElseThrow(); + assertEquals("newCode", OIDCAuthModuleConf.class.cast(found.getConf()).getResponseType()); + } + + @Test + public void updateWithJaasModule() { + AuthModule module = authModuleDAO.findById("DefaultJaasAuthModule").orElseThrow(); + AuthModuleConf conf = module.getConf(); + JaasAuthModuleConf.class.cast(conf).setRealm("SYNCOPE_NEW"); + module.setConf(conf); + + module = authModuleDAO.save(module); + assertNotNull(module); + assertNotNull(module.getKey()); + AuthModule found = authModuleDAO.findById(module.getKey()).orElseThrow(); + assertEquals("SYNCOPE_NEW", JaasAuthModuleConf.class.cast(found.getConf()).getRealm()); + } + + @Test + public void updateWithStaticModule() { + AuthModule module = authModuleDAO.findById("DefaultStaticAuthModule").orElseThrow(); + assertNotNull(module); + assertEquals(1, StaticAuthModuleConf.class.cast(module.getConf()).getUsers().size()); + AuthModuleConf conf = module.getConf(); + StaticAuthModuleConf.class.cast(conf).getUsers().put("user3", "user3Password123"); + module.setConf(conf); + + module = authModuleDAO.save(module); + assertNotNull(module); + assertNotNull(module.getKey()); + AuthModule found = authModuleDAO.findById(module.getKey()).orElseThrow(); + assertEquals(2, StaticAuthModuleConf.class.cast(found.getConf()).getUsers().size()); + } + + @Test + public void updateWithSyncopeModule() { + AuthModule module = authModuleDAO.findById("DefaultSyncopeAuthModule").orElseThrow(); + + AuthModuleConf conf = module.getConf(); + SyncopeAuthModuleConf.class.cast(conf).setDomain("Two"); + module.setConf(conf); + + module = authModuleDAO.save(module); + assertNotNull(module); + assertNotNull(module.getKey()); + AuthModule found = authModuleDAO.findById(module.getKey()).orElseThrow(); + assertEquals("Two", SyncopeAuthModuleConf.class.cast(found.getConf()).getDomain()); + } + + @Test + public void delete() { + assertTrue(authModuleDAO.findById("DefaultSyncopeAuthModule").isPresent()); + + authModuleDAO.deleteById("DefaultSyncopeAuthModule"); + + assertTrue(authModuleDAO.findById("DefaultSyncopeAuthModule").isEmpty()); + } +} 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 new file mode 100644 index 0000000000..c5f308f78c --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AuthProfileTest.java @@ -0,0 +1,198 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.inner; + +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.LocalDateTime; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Optional; +import java.util.stream.IntStream; +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.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.persistence.neo4j.AbstractTest; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jAuthProfile; +import org.apache.syncope.core.spring.security.SecureRandomUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class AuthProfileTest extends AbstractTest { + + @Autowired + private AuthProfileDAO authProfileDAO; + + @Autowired + private Neo4jTemplate neo4jTemplate; + + @BeforeEach + public void beforeEach() { + neo4jTemplate.deleteAll(Neo4jAuthProfile.class); + } + + @Test + public void googleMfaToken() { + String id = SecureRandomUtils.generateRandomUUID().toString(); + + createAuthProfileWithToken(id, 123456); + + Optional result = authProfileDAO.findByOwner(id); + assertTrue(result.isPresent()); + + assertFalse(authProfileDAO.findAll(PageRequest.of(0, 100)).isEmpty()); + + AuthProfile authProfile = result.get(); + result = authProfileDAO.findById(authProfile.getKey()); + assertTrue(result.isPresent()); + + authProfile.setOwner("SyncopeCreate-New"); + authProfile.setGoogleMfaAuthTokens(List.of()); + authProfileDAO.save(authProfile); + + assertFalse(authProfileDAO.findByOwner(id).isPresent()); + } + + @Test + public void webAuthnRegisteredDevice() { + String id = SecureRandomUtils.generateRandomUUID().toString(); + String record = "[ {" + + " \"userIdentity\" : {" + + " \"name\" : \"casuser\"," + + " \"displayName\" : \"casuser\"" + + " }," + + " \"credential\" : {" + + " \"credentialId\" : \"fFGyV3K5x1\"" + + " }," + + " \"username\" : \"casuser\"" + + " } ]"; + + WebAuthnDeviceCredential credential = new WebAuthnDeviceCredential.Builder(). + json(record). + identifier("fFGyV3K5x1"). + build(); + + createAuthProfileWithWebAuthnDevice(id, List.of(credential)); + + Optional result = authProfileDAO.findByOwner(id); + assertTrue(result.isPresent()); + + assertFalse(authProfileDAO.findAll(PageRequest.of(0, 100)).isEmpty()); + + AuthProfile authProfile = result.get(); + result = authProfileDAO.findById(authProfile.getKey()); + assertTrue(result.isPresent()); + + authProfile.setOwner("newowner"); + authProfile.setWebAuthnDeviceCredentials(List.of()); + authProfileDAO.save(authProfile); + + assertFalse(authProfileDAO.findByOwner(id).isPresent()); + } + + @Test + public void googleMfaAccount() { + String id = SecureRandomUtils.generateRandomUUID().toString(); + + createAuthProfileWithAccount(id); + + Optional result = authProfileDAO.findByOwner(id); + assertTrue(result.isPresent()); + + assertFalse(authProfileDAO.findAll(PageRequest.of(0, 100)).isEmpty()); + + AuthProfile authProfile = result.get(); + result = authProfileDAO.findById(authProfile.getKey()); + assertTrue(result.isPresent()); + + String secret = SecureRandomUtils.generateRandomUUID().toString(); + List googleMfaAuthAccounts = authProfile.getGoogleMfaAuthAccounts(); + assertFalse(googleMfaAuthAccounts.isEmpty()); + GoogleMfaAuthAccount googleMfaAuthAccount = googleMfaAuthAccounts.get(0); + googleMfaAuthAccount.setSecretKey(secret); + + authProfile.setGoogleMfaAuthAccounts(googleMfaAuthAccounts); + authProfile = authProfileDAO.save(authProfile); + assertEquals(secret, authProfile.getGoogleMfaAuthAccounts().get(0).getSecretKey()); + } + + @Test + public void impersonationAccounts() { + String id = SecureRandomUtils.generateRandomUUID().toString(); + + createAuthProfileWithAccount(id); + + Optional result = authProfileDAO.findByOwner(id); + assertTrue(result.isPresent()); + + AuthProfile authProfile = result.get(); + result = authProfileDAO.findById(authProfile.getKey()); + assertTrue(result.isPresent()); + + List accounts = IntStream.range(1, 10). + mapToObj(i -> new ImpersonationAccount.Builder().impersonated("impersonatee" + i).build()). + toList(); + + authProfile.setImpersonationAccounts(accounts); + authProfile = authProfileDAO.save(authProfile); + assertEquals(accounts.size(), authProfile.getImpersonationAccounts().size()); + } + + private AuthProfile createAuthProfileWithToken(final String owner, final Integer otp) { + AuthProfile profile = entityFactory.newEntity(AuthProfile.class); + profile.setOwner(owner); + GoogleMfaAuthToken token = new GoogleMfaAuthToken.Builder().issueDate(LocalDateTime.now()).token(otp).build(); + profile.setGoogleMfaAuthTokens(List.of(token)); + return authProfileDAO.save(profile); + } + + private AuthProfile createAuthProfileWithWebAuthnDevice( + final String owner, + final List credentials) { + + AuthProfile profile = entityFactory.newEntity(AuthProfile.class); + profile.setOwner(owner); + profile.setWebAuthnDeviceCredentials(credentials); + return authProfileDAO.save(profile); + } + + private AuthProfile createAuthProfileWithAccount(final String owner) { + AuthProfile profile = entityFactory.newEntity(AuthProfile.class); + profile.setOwner(owner); + GoogleMfaAuthAccount account = new GoogleMfaAuthAccount.Builder() + .registrationDate(OffsetDateTime.now()) + .scratchCodes(List.of(1, 2, 3, 4, 5)) + .secretKey(SecureRandomUtils.generateRandomUUID().toString()) + .validationCode(123456) + .name(SecureRandomUtils.generateRandomUUID().toString()) + .build(); + profile.setGoogleMfaAuthAccounts(List.of(account)); + return authProfileDAO.save(profile); + } +} 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 new file mode 100644 index 0000000000..78243876f3 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/CASSPTest.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.persistence.neo4j.inner; + +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.UUID; +import org.apache.syncope.core.persistence.api.dao.CASSPClientAppDAO; +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.AuthPolicy; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class CASSPTest extends AbstractClientAppTest { + + @Autowired + private CASSPClientAppDAO casspDAO; + + @Test + public void find() { + long beforeCount = casspDAO.count(); + + CASSPClientApp rp = entityFactory.newEntity(CASSPClientApp.class); + rp.setName("CAS"); + rp.setClientAppId(UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE); + rp.setDescription("This is a sample CAS RP"); + rp.setServiceId("https://syncope.apache.org/.*"); + + AccessPolicy accessPolicy = buildAndSaveAccessPolicy(); + rp.setAccessPolicy(accessPolicy); + + AuthPolicy authPolicy = buildAndSaveAuthPolicy(); + rp.setAuthPolicy(authPolicy); + + casspDAO.save(rp); + + assertNotNull(rp); + assertNotNull(rp.getKey()); + + long afterCount = casspDAO.count(); + assertEquals(afterCount, beforeCount + 1); + + rp = casspDAO.findByName("CAS").orElseThrow(); + assertNotNull(rp); + + rp = casspDAO.findByClientAppId(rp.getClientAppId()).orElseThrow(); + assertNotNull(rp); + + casspDAO.delete(rp); + assertTrue(casspDAO.findByName("CAS").isEmpty()); + } +} 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 new file mode 100644 index 0000000000..b7f426f472 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ConnInstanceTest.java @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.inner; + +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.junit.jupiter.api.Assertions.fail; + +import java.io.File; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.ConnConfPropSchema; +import org.apache.syncope.common.lib.types.ConnConfProperty; +import org.apache.syncope.common.lib.types.IdMEntitlement; +import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO; +import org.apache.syncope.core.persistence.api.entity.ConnInstance; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.apache.syncope.core.spring.security.DelegatedAdministrationException; +import org.apache.syncope.core.spring.security.SyncopeAuthenticationDetails; +import org.apache.syncope.core.spring.security.SyncopeGrantedAuthority; +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 ConnInstanceTest extends AbstractTest { + + @Autowired + private ConnInstanceDAO connInstanceDAO; + + @Test + public void findAll() { + 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); + + try { + List connectors = connInstanceDAO.findAll(); + assertNotNull(connectors); + assertFalse(connectors.isEmpty()); + } finally { + SecurityContextHolder.getContext().setAuthentication(null); + } + } + + @Test + public void findById() { + ConnInstance connInstance = connInstanceDAO.findById("88a7a819-dab5-46b4-9b90-0b9769eabdb8").orElseThrow(); + assertNotNull(connInstance); + assertEquals("net.tirasa.connid.bundles.soap.WebServiceConnector", connInstance.getConnectorName()); + assertEquals("net.tirasa.connid.bundles.soap", connInstance.getBundleName()); + + try { + connInstanceDAO.authFind("88a7a819-dab5-46b4-9b90-0b9769eabdb8"); + fail("This should not happen"); + } catch (DelegatedAdministrationException e) { + assertNotNull(e); + } + } + + @Test + public void save() throws ClassNotFoundException { + ConnInstance connInstance = entityFactory.newEntity(ConnInstance.class); + + connInstance.setLocation(new File(System.getProperty("java.io.tmpdir")).toURI().toString()); + + // set connector version + connInstance.setVersion("1.0"); + + // set connector name + connInstance.setConnectorName("WebService"); + + // set bundle name + connInstance.setBundleName("org.apache.syncope.core.persistence.test.util"); + + connInstance.setDisplayName("New"); + + connInstance.setConnRequestTimeout(60); + + // set the connector configuration using PropertyTO + Set conf = new HashSet<>(); + + ConnConfPropSchema endpointSchema = new ConnConfPropSchema(); + endpointSchema.setName("endpoint"); + endpointSchema.setType(String.class.getName()); + endpointSchema.setRequired(true); + ConnConfProperty endpoint = new ConnConfProperty(); + endpoint.setSchema(endpointSchema); + endpoint.getValues().add("http://host.domain"); + + ConnConfPropSchema servicenameSchema = new ConnConfPropSchema(); + servicenameSchema.setName("servicename"); + servicenameSchema.setType(String.class.getName()); + servicenameSchema.setRequired(true); + ConnConfProperty servicename = new ConnConfProperty(); + servicename.setSchema(servicenameSchema); + endpoint.getValues().add("Provisioning"); + + conf.add(endpoint); + conf.add(servicename); + + // set connector configuration + connInstance.setConf(conf); + assertFalse(connInstance.getConf().isEmpty()); + + // perform save operation + ConnInstance actual = connInstanceDAO.save(connInstance); + + assertNotNull("save did not work", actual::getKey); + + assertEquals("WebService", actual.getConnectorName()); + + assertEquals("org.apache.syncope.core.persistence.test.util", actual.getBundleName()); + + assertEquals("1.0", connInstance.getVersion()); + + assertEquals("New", actual.getDisplayName()); + + assertEquals(60, actual.getConnRequestTimeout().intValue()); + + conf = connInstance.getConf(); + assertFalse(conf.isEmpty()); + + assertNotNull(conf); + assertEquals(2, conf.size()); + } + + @Test + public void delete() { + ConnInstance connInstance = connInstanceDAO.findById("88a7a819-dab5-46b4-9b90-0b9769eabdb8").orElseThrow(); + + connInstanceDAO.deleteById(connInstance.getKey()); + + assertTrue(connInstanceDAO.findById("88a7a819-dab5-46b4-9b90-0b9769eabdb8").isEmpty()); + } +} 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 new file mode 100644 index 0000000000..d1c3f32556 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/DerSchemaTest.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.core.persistence.neo4j.inner; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.List; +import org.apache.syncope.common.lib.types.EntityViolationType; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; +import org.apache.syncope.core.persistence.api.entity.DerSchema; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class DerSchemaTest extends AbstractTest { + + @Autowired + private DerSchemaDAO derSchemaDAO; + + @Test + public void findAll() { + List list = derSchemaDAO.findAll(); + assertEquals(10, list.size()); + } + + @Test + public void findByIdLike() { + List schemas = derSchemaDAO.findByIdLike("mderivedd*"); + assertEquals(1, schemas.size()); + } + + @Test + public void findByName() { + assertTrue(derSchemaDAO.findById("cn").isPresent()); + } + + @Test + public void save() { + DerSchema derivedAttributeSchema = entityFactory.newEntity(DerSchema.class); + derivedAttributeSchema.setKey("cn2"); + derivedAttributeSchema.setExpression("firstname surname"); + + derSchemaDAO.save(derivedAttributeSchema); + + DerSchema actual = derSchemaDAO.findById("cn2").orElseThrow(); + assertEquals(derivedAttributeSchema, actual); + } + + @Test + public void delete() { + DerSchema cn = derSchemaDAO.findById("cn").orElseThrow(); + + derSchemaDAO.deleteById(cn.getKey()); + + assertTrue(derSchemaDAO.findById("cn").isEmpty()); + + // ------------- // + DerSchema rderiveddata = derSchemaDAO.findById("rderiveddata").orElseThrow(); + + derSchemaDAO.deleteById(rderiveddata.getKey()); + + assertTrue(derSchemaDAO.findById("rderiveddata").isEmpty()); + } + + @Test + public void issueSYNCOPE418() { + DerSchema schema = entityFactory.newEntity(DerSchema.class); + schema.setKey("http://schemas.examples.org/security/authorization/organizationUnit"); + + try { + derSchemaDAO.save(schema); + fail("This should not happen"); + } catch (InvalidEntityException e) { + assertTrue(e.hasViolation(EntityViolationType.InvalidKey)); + } + } +} 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 new file mode 100644 index 0000000000..7c418d0877 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/GroupTest.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.persistence.neo4j.inner; + +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.List; +import org.apache.syncope.common.lib.SyncopeConstants; +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.RealmDAO; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class GroupTest extends AbstractTest { + + @Autowired + private GroupDAO groupDAO; + + @Autowired + private RealmDAO realmDAO; + + @Autowired + private AnyTypeDAO anyTypeDAO; + + @Test + public void findAll() { + List groups = groupDAO.findAll(); + assertEquals(16, groups.size()); + } + + @Test + public void find() { + Group group = groupDAO.findByName("additional").orElseThrow(); + assertNotNull(group); + assertEquals(1, group.getTypeExtensions().size()); + assertEquals(2, group.getTypeExtension(anyTypeDAO.getUser()).get().getAuxClasses().size()); + } + + @Test + public void findKeysByNamePattern() { + List groups = groupDAO.findKeysByNamePattern(".*child"); + assertEquals(2, groups.size()); + assertTrue(groups.contains("b1f7c12d-ec83-441f-a50e-1691daaedf3b")); + assertTrue(groups.contains("f779c0d4-633b-4be5-8f57-32eb478a3ca5")); + } + + @Test + public void save() { + Group group = entityFactory.newEntity(Group.class); + group.setName("secondChild"); + group.setRealm(realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); + + group = groupDAO.save(group); + + assertTrue(groupDAO.findById(group.getKey()).isPresent()); + } + + @Test + public void delete() { + Group group = groupDAO.findById("8fb2d51e-c605-4e80-a72b-13ffecf1aa9a").orElseThrow(); + groupDAO.deleteById(group.getKey()); + + assertTrue(groupDAO.findById("8fb2d51e-c605-4e80-a72b-13ffecf1aa9a").isEmpty()); + } +} 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 new file mode 100644 index 0000000000..ef2a1b73b9 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ImplementationTest.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.persistence.neo4j.inner; + +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 java.util.List; +import org.apache.syncope.common.lib.types.IdMImplementationType; +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.entity.Implementation; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class ImplementationTest extends AbstractTest { + + @Autowired + private ImplementationDAO implementationDAO; + + @Test + public void findById() { + Implementation impl = implementationDAO.findById("ExpiredBatchCleanup").orElseThrow(); + assertEquals(IdRepoImplementationType.TASKJOB_DELEGATE, impl.getType()); + assertEquals(ImplementationEngine.JAVA, impl.getEngine()); + } + + @Test + public void findAll() { + List implementations = implementationDAO.findAll(); + assertFalse(implementations.isEmpty()); + + assertEquals(19, implementations.size()); + + implementations = implementationDAO.findByType(IdMImplementationType.PULL_ACTIONS); + assertEquals(1, implementations.size()); + + implementations = implementationDAO.findByType(IdMImplementationType.PROPAGATION_ACTIONS); + assertEquals(2, implementations.size()); + + implementations = implementationDAO.findByType(IdRepoImplementationType.TASKJOB_DELEGATE); + assertEquals(6, implementations.size()); + + implementations = implementationDAO.findByType(IdRepoImplementationType.REPORT_DELEGATE); + assertEquals(1, implementations.size()); + + implementations = implementationDAO.findByType(IdRepoImplementationType.ACCOUNT_RULE); + assertEquals(2, implementations.size()); + + implementations = implementationDAO.findByType(IdRepoImplementationType.PASSWORD_RULE); + assertEquals(3, implementations.size()); + + implementations = implementationDAO.findByType(IdRepoImplementationType.VALIDATOR); + assertEquals(2, implementations.size()); + + implementations = implementationDAO.findByType(IdMImplementationType.PULL_CORRELATION_RULE); + assertEquals(1, implementations.size()); + + implementations = implementationDAO.findByType(IdMImplementationType.PUSH_CORRELATION_RULE); + assertEquals(1, implementations.size()); + } + + @Test + public void create() { + Implementation impl = entityFactory.newEntity(Implementation.class); + impl.setKey("new"); + impl.setEngine(ImplementationEngine.GROOVY); + impl.setType(IdRepoImplementationType.VALIDATOR); + impl.setBody(""); + + Implementation actual = implementationDAO.save(impl); + assertNotNull(actual); + assertEquals(impl, actual); + } +} 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 new file mode 100644 index 0000000000..13f898a921 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/MailTemplateTest.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.persistence.neo4j.inner; + +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.util.List; +import org.apache.syncope.core.persistence.api.dao.MailTemplateDAO; +import org.apache.syncope.core.persistence.api.entity.MailTemplate; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class MailTemplateTest extends AbstractTest { + + @Autowired + private MailTemplateDAO mailTemplateDAO; + + @Test + public void find() { + MailTemplate optin = mailTemplateDAO.findById("optin").orElseThrow(); + assertNotNull(optin.getTextTemplate()); + assertNotNull(optin.getHTMLTemplate()); + } + + @Test + public void findAll() { + List templates = mailTemplateDAO.findAll(); + assertNotNull(templates); + assertFalse(templates.isEmpty()); + } + + @Test + public void save() { + MailTemplate template = entityFactory.newEntity(MailTemplate.class); + template.setKey("new"); + template.setTextTemplate("Text template"); + + MailTemplate actual = mailTemplateDAO.save(template); + assertNotNull(actual); + assertNotNull(actual.getKey()); + assertNotNull(actual.getTextTemplate()); + assertNull(actual.getHTMLTemplate()); + + actual.setHTMLTemplate("

HTML template

"); + actual = mailTemplateDAO.save(actual); + assertNotNull(actual.getTextTemplate()); + assertNotNull(actual.getHTMLTemplate()); + } + + @Test + public 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 new file mode 100644 index 0000000000..d2815106fe --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/MultitenancyTest.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.persistence.neo4j.inner; + +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.SyncopeConstants; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +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.user.User; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.apache.syncope.core.persistence.neo4j.PersistenceTestContext; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class MultitenancyTest extends AbstractTest { + + static { + PersistenceTestContext.TEST_DOMAIN.set("Two"); + } + + @AfterAll + public static void restoreDomain() { + PersistenceTestContext.TEST_DOMAIN.set(SyncopeConstants.MASTER_DOMAIN); + } + + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + @Autowired + private RealmDAO realmDAO; + + @Autowired + private UserDAO userDAO; + + @Test + public void readPlainSchemas() { + assertEquals(1, plainSchemaDAO.findAll().size()); + } + + @Test + public void readRealm() { + assertEquals(1, realmDAO.findDescendants(realmDAO.getRoot().getFullPath(), null, Pageable.unpaged()).size()); + assertEquals( + realmDAO.getRoot(), + realmDAO.findDescendants(realmDAO.getRoot().getFullPath(), null, Pageable.unpaged()).get(0)); + } + + @Test + public void createUser() { + assertNull(realmDAO.getRoot().getPasswordPolicy()); + + User user = entityFactory.newEntity(User.class); + user.setRealm(realmDAO.getRoot()); + user.setPassword("password"); + user.setCipherAlgorithm(CipherAlgorithm.SHA256); + user.setUsername("username"); + + User actual = userDAO.save(user); + assertNotNull(actual); + assertEquals(0, actual.getPasswordHistory().size()); + } +} 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 new file mode 100644 index 0000000000..0528692ca6 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/NotificationTest.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.inner; + +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.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.dao.MailTemplateDAO; +import org.apache.syncope.core.persistence.api.dao.NotificationDAO; +import org.apache.syncope.core.persistence.api.entity.AnyAbout; +import org.apache.syncope.core.persistence.api.entity.Notification; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class NotificationTest extends AbstractTest { + + @Autowired + private AnyTypeDAO anyTypeDAO; + + @Autowired + private NotificationDAO notificationDAO; + + @Autowired + private MailTemplateDAO mailTemplateDAO; + + @Test + public void find() { + Notification notification = notificationDAO.findById("9e2b911c-25de-4c77-bcea-b86ed9451050").orElseThrow(); + assertNotNull(notification.getEvents()); + assertFalse(notification.getEvents().isEmpty()); + assertNotNull(notification.getAbout(anyTypeDAO.getUser())); + assertNotNull(notification.getRecipientsFIQL()); + } + + @Test + public void findAll() { + List notifications = notificationDAO.findAll(); + assertNotNull(notifications); + assertFalse(notifications.isEmpty()); + } + + @Test + public void save() { + Notification notification = entityFactory.newEntity(Notification.class); + notification.getEvents().add("save"); + + AnyAbout about = entityFactory.newEntity(AnyAbout.class); + about.setNotification(notification); + notification.add(about); + about.setAnyType(anyTypeDAO.getUser()); + about.set("fake search condition"); + + notification.setRecipientsFIQL("fake recipients"); + + notification.setRecipientAttrName("email"); + + notification.setSender("syncope@syncope.apache.org"); + notification.setSubject("Test notification"); + notification.setTemplate(mailTemplateDAO.findById("test").orElseThrow()); + + Notification actual = notificationDAO.save(notification); + assertNotNull(actual); + assertNotNull(actual.getKey()); + } + + @Test + public void delete() { + notificationDAO.deleteById("9e2b911c-25de-4c77-bcea-b86ed9451050"); + assertTrue(notificationDAO.findById("9e2b911c-25de-4c77-bcea-b86ed9451050").isEmpty()); + } + + @Test + public void issueSYNCOPE445() { + Notification notification = entityFactory.newEntity(Notification.class); + notification.getEvents().add("save"); + + AnyAbout about = entityFactory.newEntity(AnyAbout.class); + about.setNotification(notification); + notification.add(about); + about.setAnyType(anyTypeDAO.getUser()); + about.set("fake search condition"); + + notification.setRecipientsFIQL("fake search condition"); + + notification.setRecipientAttrName("email"); + + notification.getStaticRecipients().add("syncope445@syncope.apache.org"); + + notification.setSender("syncope@syncope.apache.org"); + notification.setSubject("Test notification"); + notification.setTemplate(mailTemplateDAO.findById("test").orElseThrow()); + + notification = notificationDAO.save(notification); + + Notification actual = notificationDAO.findById(notification.getKey()).orElseThrow(); + assertNotNull(actual.getKey()); + assertNotNull(actual.getStaticRecipients()); + assertFalse(actual.getStaticRecipients().isEmpty()); + } + + @Test + public void issueSYNCOPE446() { + Notification notification = entityFactory.newEntity(Notification.class); + notification.getEvents().add("[LOGIC]:[GroupLogic]:[]:[create]:[SUCCESS]"); + + AnyAbout about = entityFactory.newEntity(AnyAbout.class); + about.setNotification(notification); + notification.add(about); + about.setAnyType(anyTypeDAO.getUser()); + about.set("fake search condition"); + + notification.setRecipientAttrName("email"); + + notification.getStaticRecipients().add("syncope446@syncope.apache.org"); + + notification.setSender("syncope@syncope.apache.org"); + notification.setSubject("Test notification"); + notification.setTemplate(mailTemplateDAO.findById("test").orElseThrow()); + + notification = notificationDAO.save(notification); + + Notification actual = notificationDAO.findById(notification.getKey()).orElseThrow(); + assertNotNull(actual.getKey()); + assertNotNull(actual.getStaticRecipients()); + assertFalse(actual.getStaticRecipients().isEmpty()); + } +} diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/OIDCJWKSTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/OIDCJWKSTest.java new file mode 100644 index 0000000000..ff467e93d8 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/OIDCJWKSTest.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.core.persistence.neo4j.inner; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jose.jwk.KeyUse; +import com.nimbusds.jose.jwk.RSAKey; +import com.nimbusds.jose.jwk.gen.RSAKeyGenerator; +import java.util.UUID; +import org.apache.syncope.core.persistence.api.dao.OIDCJWKSDAO; +import org.apache.syncope.core.persistence.api.entity.am.OIDCJWKS; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class OIDCJWKSTest extends AbstractTest { + + @Autowired + private OIDCJWKSDAO jwksDAO; + + @Test + public void save() throws Exception { + OIDCJWKS jwks = entityFactory.newEntity(OIDCJWKS.class); + + RSAKey jwk = new RSAKeyGenerator(2048) + .keyUse(KeyUse.SIGNATURE) + .keyID(UUID.randomUUID().toString()) + .generate(); + + String json = new JWKSet(jwk).toString(); + jwks.setJson(json); + jwks = jwksDAO.save(jwks); + assertNotNull(jwks); + assertNotNull(jwks.getKey()); + } +} 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 new file mode 100644 index 0000000000..174cc9cb5d --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/OIDCRPTest.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.persistence.neo4j.inner; + +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.UUID; +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.core.persistence.api.dao.OIDCRPClientAppDAO; +import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp; +import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class OIDCRPTest extends AbstractClientAppTest { + + @Autowired + private OIDCRPClientAppDAO oidcrpDAO; + + @Test + public void find() { + int beforeCount = oidcrpDAO.findAll().size(); + + OIDCRPClientApp rp = entityFactory.newEntity(OIDCRPClientApp.class); + rp.setName("OIDC"); + rp.setClientAppId(UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE); + rp.setDescription("This is a sample OIDC RP"); + rp.setClientId("clientid"); + rp.setClientSecret("secret"); + rp.setSubjectType(OIDCSubjectType.PUBLIC); + rp.getSupportedGrantTypes().add(OIDCGrantType.password); + rp.getSupportedResponseTypes().add(OIDCResponseType.CODE); + + AccessPolicy accessPolicy = buildAndSaveAccessPolicy(); + rp.setAccessPolicy(accessPolicy); + + AuthPolicy authPolicy = buildAndSaveAuthPolicy(); + rp.setAuthPolicy(authPolicy); + + oidcrpDAO.save(rp); + + assertNotNull(rp); + assertNotNull(rp.getKey()); + + int afterCount = oidcrpDAO.findAll().size(); + assertEquals(afterCount, beforeCount + 1); + + rp = oidcrpDAO.findByClientId("clientid").orElseThrow(); + assertNotNull(rp.getAuthPolicy()); + + rp = oidcrpDAO.findByName("OIDC").orElseThrow(); + + assertTrue(oidcrpDAO.findByClientAppId(rp.getClientAppId()).isPresent()); + + oidcrpDAO.deleteByClientId("clientid"); + + assertTrue(oidcrpDAO.findByName("OIDC").isEmpty()); + } +} 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 new file mode 100644 index 0000000000..c90dbc75e0 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PlainAttrTest.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.neo4j.inner; + +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.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import jakarta.validation.ValidationException; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.Base64; +import java.util.Random; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.common.lib.types.EntityViolationType; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; +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.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.apache.syncope.core.spring.security.Encryptor; +import org.apache.syncope.core.spring.security.SecureRandomUtils; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class PlainAttrTest extends AbstractTest { + + @Autowired + private UserDAO userDAO; + + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + @Autowired + private AnyTypeClassDAO anyTypeClassDAO; + + @Autowired + private PlainAttrValidationManager validator; + + @Test + public void save() throws ClassNotFoundException { + User user = userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").orElseThrow(); + + PlainSchema emailSchema = plainSchemaDAO.findById("email").orElseThrow(); + + UPlainAttr attr = entityFactory.newEntity(UPlainAttr.class); + attr.setOwner(user); + attr.setSchema(emailSchema); + + Exception thrown = null; + try { + attr.add(validator, "john.doe@gmail.com", anyUtilsFactory.getInstance(AnyTypeKind.USER)); + attr.add(validator, "mario.rossi@gmail.com", anyUtilsFactory.getInstance(AnyTypeKind.USER)); + } catch (ValidationException e) { + thrown = e; + } + assertNull(thrown); + + try { + attr.add(validator, "http://www.apache.org", anyUtilsFactory.getInstance(AnyTypeKind.USER)); + } catch (ValidationException e) { + thrown = e; + } + assertNotNull(thrown); + } + + @Test + public void saveWithEnum() throws ClassNotFoundException { + User user = userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").orElseThrow(); + + PlainSchema gender = plainSchemaDAO.findById("gender").orElseThrow(); + assertNotNull(gender.getType()); + assertNotNull(gender.getEnumerationValues()); + + UPlainAttr attribute = entityFactory.newEntity(UPlainAttr.class); + attribute.setOwner(user); + attribute.setSchema(gender); + user.add(attribute); + + Exception thrown = null; + try { + attribute.add(validator, "A", anyUtilsFactory.getInstance(AnyTypeKind.USER)); + } catch (ValidationException e) { + thrown = e; + } + assertNotNull(thrown); + + attribute.add(validator, "M", anyUtilsFactory.getInstance(AnyTypeKind.USER)); + + InvalidEntityException iee = null; + try { + userDAO.save(user); + } catch (InvalidEntityException e) { + iee = e; + } + assertNull(iee); + } + + @Test + public void invalidValueList() { + User user = userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").orElseThrow(); + + PlainSchema emailSchema = plainSchemaDAO.findById("email").orElseThrow(); + + UPlainAttr attr = entityFactory.newEntity(UPlainAttr.class); + attr.setOwner(user); + attr.setSchema(emailSchema); + + user.add(attr); + + InvalidEntityException iee = null; + try { + userDAO.save(user); + fail("This should not happen"); + } catch (InvalidEntityException e) { + iee = e; + } + assertNotNull(iee); + // for attr because no values are set + assertTrue(iee.hasViolation(EntityViolationType.InvalidValueList)); + } + + @Test + public void saveWithEncrypted() throws Exception { + User user = userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").orElseThrow(); + + PlainSchema obscureSchema = plainSchemaDAO.findById("obscure").orElseThrow(); + assertNotNull(obscureSchema.getSecretKey()); + assertNotNull(obscureSchema.getCipherAlgorithm()); + + UPlainAttr attr = entityFactory.newEntity(UPlainAttr.class); + attr.setOwner(user); + attr.setSchema(obscureSchema); + attr.add(validator, "testvalue", anyUtilsFactory.getInstance(AnyTypeKind.USER)); + user.add(attr); + + userDAO.save(user); + + UPlainAttr obscure = user.getPlainAttr("obscure").get(); + assertNotNull(obscure); + assertEquals(1, obscure.getValues().size()); + assertEquals(Encryptor.getInstance(obscureSchema.getSecretKey()). + encode("testvalue", obscureSchema.getCipherAlgorithm()), obscure.getValues().get(0).getStringValue()); + } + + @Test + public void encryptedWithKeyAsSysProp() throws Exception { + PlainSchema obscureSchema = plainSchemaDAO.findById("obscure").orElseThrow(); + + PlainSchema obscureWithKeyAsSysprop = entityFactory.newEntity(PlainSchema.class); + obscureWithKeyAsSysprop.setKey("obscureWithKeyAsSysprop"); + obscureWithKeyAsSysprop.setAnyTypeClass(obscureSchema.getAnyTypeClass()); + obscureWithKeyAsSysprop.setType(AttrSchemaType.Encrypted); + obscureWithKeyAsSysprop.setCipherAlgorithm(obscureSchema.getCipherAlgorithm()); + obscureWithKeyAsSysprop.setSecretKey("${obscureSecretKey}"); + + obscureWithKeyAsSysprop = plainSchemaDAO.save(obscureWithKeyAsSysprop); + + System.setProperty("obscureSecretKey", obscureSchema.getSecretKey()); + + UPlainAttr attr = entityFactory.newEntity(UPlainAttr.class); + attr.setSchema(obscureWithKeyAsSysprop); + attr.add(validator, "testvalue", anyUtilsFactory.getInstance(AnyTypeKind.USER)); + + assertEquals(Encryptor.getInstance(obscureSchema.getSecretKey()). + encode("testvalue", obscureSchema.getCipherAlgorithm()), attr.getValues().get(0).getStringValue()); + } + + @Test + public void encryptedWithDecodeConversionPattern() throws Exception { + PlainSchema obscureWithDecodeConversionPattern = entityFactory.newEntity(PlainSchema.class); + obscureWithDecodeConversionPattern.setKey("obscureWithDecodeConversionPattern"); + obscureWithDecodeConversionPattern.setAnyTypeClass(anyTypeClassDAO.findById("other").orElseThrow()); + obscureWithDecodeConversionPattern.setType(AttrSchemaType.Encrypted); + obscureWithDecodeConversionPattern.setCipherAlgorithm(CipherAlgorithm.AES); + obscureWithDecodeConversionPattern.setSecretKey(SecureRandomUtils.generateRandomUUID().toString()); + + obscureWithDecodeConversionPattern = plainSchemaDAO.save(obscureWithDecodeConversionPattern); + + UPlainAttr attr = entityFactory.newEntity(UPlainAttr.class); + attr.setSchema(obscureWithDecodeConversionPattern); + attr.add(validator, "testvalue", anyUtilsFactory.getInstance(AnyTypeKind.USER)); + + assertEquals(Encryptor.getInstance(obscureWithDecodeConversionPattern.getSecretKey()). + encode("testvalue", obscureWithDecodeConversionPattern.getCipherAlgorithm()), + attr.getValues().get(0).getStringValue()); + + obscureWithDecodeConversionPattern.setConversionPattern(SyncopeConstants.ENCRYPTED_DECODE_CONVERSION_PATTERN); + plainSchemaDAO.save(obscureWithDecodeConversionPattern); + + assertNotEquals("testvalue", attr.getValues().get(0).getStringValue()); + assertEquals("testvalue", attr.getValuesAsStrings().get(0)); + } + + @Test + public void saveWithBinary() throws UnsupportedEncodingException { + User user = userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").orElseThrow(); + + PlainSchema photoSchema = plainSchemaDAO.findById("photo").orElseThrow(); + assertNotNull(photoSchema.getMimeType()); + + byte[] bytes = new byte[20]; + new Random().nextBytes(bytes); + String photoB64Value = Base64.getEncoder().encodeToString(bytes); + + UPlainAttr attr = entityFactory.newEntity(UPlainAttr.class); + attr.setOwner(user); + attr.setSchema(photoSchema); + attr.add(validator, photoB64Value, anyUtilsFactory.getInstance(AnyTypeKind.USER)); + user.add(attr); + + userDAO.save(user); + + UPlainAttr photo = user.getPlainAttr("photo").get(); + assertNotNull(photo); + assertEquals(1, photo.getValues().size()); + assertTrue(Arrays.equals(bytes, photo.getValues().get(0).getBinaryValue())); + } +} 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 new file mode 100644 index 0000000000..d0191cb28b --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PlainSchemaTest.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.persistence.neo4j.inner; + +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 static org.junit.jupiter.api.Assertions.fail; + +import java.util.List; +import java.util.Locale; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.common.lib.types.EntityViolationType; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +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.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class PlainSchemaTest extends AbstractTest { + + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + @Autowired + private ImplementationDAO implementationDAO; + + @Test + public void findAll() { + List schemas = plainSchemaDAO.findAll(); + assertEquals(27, schemas.size()); + } + + @Test + public void findByIdLike() { + List schemas = plainSchemaDAO.findByIdLike("fullna*"); + assertEquals(1, schemas.size()); + assertEquals(0, schemas.get(0).getLabels().size()); + } + + @Test + public void findByName() { + PlainSchema schema = plainSchemaDAO.findById("firstname").orElseThrow(); + + assertEquals(3, schema.getLabels().size()); + assertTrue(schema.getLabel(Locale.ITALIAN).isPresent()); + assertFalse(schema.getLabel(Locale.KOREAN).isPresent()); + } + + @Test + public void hasAttrs() { + PlainSchema schema = plainSchemaDAO.findById("icon").orElseThrow(); + assertTrue(plainSchemaDAO.hasAttrs(schema, GPlainAttr.class)); + + schema = plainSchemaDAO.findById("aLong").orElseThrow(); + assertFalse(plainSchemaDAO.hasAttrs(schema, UPlainAttr.class)); + } + + @Test + public void save() { + PlainSchema schema = entityFactory.newEntity(PlainSchema.class); + schema.setKey("secondaryEmail"); + schema.setType(AttrSchemaType.String); + schema.setValidator(implementationDAO.findById("EmailAddressValidator").orElseThrow()); + schema.setMandatoryCondition("false"); + schema.setMultivalue(true); + schema.getLabels().put(Locale.ENGLISH, "Secondary email"); + + plainSchemaDAO.save(schema); + + PlainSchema actual = plainSchemaDAO.findById("secondaryEmail").orElseThrow(); + assertEquals(schema, actual); + assertEquals("Secondary email", actual.getLabel(Locale.ENGLISH).orElseThrow()); + assertTrue(actual.getLabel(Locale.ITALIAN).isEmpty()); + } + + @Test + public void saveNonValid() { + assertThrows(InvalidEntityException.class, () -> { + PlainSchema schema = entityFactory.newEntity(PlainSchema.class); + schema.setKey("secondaryEmail"); + schema.setType(AttrSchemaType.String); + schema.setValidator(implementationDAO.findById("EmailAddressValidator").orElseThrow()); + schema.setMandatoryCondition("false"); + schema.setMultivalue(true); + schema.setUniqueConstraint(true); + + plainSchemaDAO.save(schema); + }); + } + + @Test + public void checkForEnumType() { + PlainSchema schema = entityFactory.newEntity(PlainSchema.class); + schema.setType(AttrSchemaType.Enum); + schema.setKey("color"); + + try { + plainSchemaDAO.save(schema); + fail("This should not happen"); + } catch (Exception e) { + assertNotNull(e); + } + + schema.setEnumerationValues("red" + SyncopeConstants.ENUM_VALUES_SEPARATOR + "yellow"); + schema.setEnumerationKeys('1' + SyncopeConstants.ENUM_VALUES_SEPARATOR + '2'); + + plainSchemaDAO.save(schema); + + PlainSchema actual = plainSchemaDAO.findById(schema.getKey()).orElseThrow(); + assertNotNull(actual.getEnumerationKeys()); + assertFalse(actual.getEnumerationKeys().isEmpty()); + } + + @Test + public void saveInvalidSchema() { + assertThrows(InvalidEntityException.class, () -> { + PlainSchema schema = entityFactory.newEntity(PlainSchema.class); + schema.setKey("username"); + plainSchemaDAO.save(schema); + }); + } + + @Test + public void delete() { + PlainSchema firstname = plainSchemaDAO.findById("firstname").orElseThrow(); + + plainSchemaDAO.deleteById(firstname.getKey()); + + assertTrue(plainSchemaDAO.findById("firstname").isEmpty()); + } + + @Test + public void issueSYNCOPE418() { + PlainSchema schema = entityFactory.newEntity(PlainSchema.class); + schema.setKey("http://schemas.examples.org/security/authorization/organizationUnit"); + + try { + plainSchemaDAO.save(schema); + fail("This should not happen"); + } catch (InvalidEntityException e) { + assertTrue(e.hasViolation(EntityViolationType.InvalidKey)); + } + } +} 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 new file mode 100644 index 0000000000..e536514eae --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PolicyTest.java @@ -0,0 +1,388 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.inner; + +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.List; +import java.util.UUID; +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.DefaultPasswordRuleConf; +import org.apache.syncope.common.lib.policy.DefaultPullCorrelationRuleConf; +import org.apache.syncope.common.lib.policy.DefaultPushCorrelationRuleConf; +import org.apache.syncope.common.lib.policy.DefaultTicketExpirationPolicyConf; +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.common.lib.types.IdMImplementationType; +import org.apache.syncope.common.lib.types.IdRepoImplementationType; +import org.apache.syncope.common.lib.types.ImplementationEngine; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.dao.ImplementationDAO; +import org.apache.syncope.core.persistence.api.dao.PolicyDAO; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy; +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.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.PullCorrelationRuleEntity; +import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.PushCorrelationRuleEntity; +import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; +import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class PolicyTest extends AbstractTest { + + @Autowired + private AnyTypeDAO anyTypeDAO; + + @Autowired + private PolicyDAO policyDAO; + + @Autowired + private ImplementationDAO implementationDAO; + + @Test + public void findAll() { + assertEquals(16, policyDAO.count()); + assertEquals(16, policyDAO.findAll().size()); + + assertEquals(1, policyDAO.findAll(AccessPolicy.class).size()); + assertEquals(2, policyDAO.findAll(AccountPolicy.class).size()); + assertEquals(2, policyDAO.findAll(AttrReleasePolicy.class).size()); + assertEquals(2, policyDAO.findAll(AuthPolicy.class).size()); + assertEquals(3, policyDAO.findAll(PasswordPolicy.class).size()); + assertEquals(1, policyDAO.findAll(PropagationPolicy.class).size()); + assertEquals(4, policyDAO.findAll(PullPolicy.class).size()); + assertEquals(1, policyDAO.findAll(PushPolicy.class).size()); + } + + @Test + public void findByKey() { + PropagationPolicy propagationPolicy = policyDAO.findById( + "89d322db-9878-420c-b49c-67be13df9a12", PropagationPolicy.class).orElseThrow(); + assertEquals(BackOffStrategy.FIXED, propagationPolicy.getBackOffStrategy()); + assertEquals("10000", propagationPolicy.getBackOffParams()); + assertEquals(5, propagationPolicy.getMaxAttempts()); + + PullPolicy pullPolicy = policyDAO.findById( + "880f8553-069b-4aed-9930-2cd53873f544", PullPolicy.class).orElseThrow(); + + PullCorrelationRuleEntity pullCR = pullPolicy.getCorrelationRule(AnyTypeKind.USER.name()).orElseThrow(); + DefaultPullCorrelationRuleConf pullCRConf = + POJOHelper.deserialize(pullCR.getImplementation().getBody(), DefaultPullCorrelationRuleConf.class); + assertNotNull(pullCRConf); + assertEquals(2, pullCRConf.getSchemas().size()); + assertTrue(pullCRConf.getSchemas().contains("username")); + assertTrue(pullCRConf.getSchemas().contains("firstname")); + + PushPolicy pushPolicy = policyDAO.findById( + "fb6530e5-892d-4f47-a46b-180c5b6c5c83", PushPolicy.class).orElseThrow(); + + PushCorrelationRuleEntity pushCR = pushPolicy.getCorrelationRule(AnyTypeKind.USER.name()).orElseThrow(); + DefaultPushCorrelationRuleConf pushCRConf = + POJOHelper.deserialize(pushCR.getImplementation().getBody(), DefaultPushCorrelationRuleConf.class); + assertNotNull(pushCRConf); + assertEquals(1, pushCRConf.getSchemas().size()); + assertTrue(pushCRConf.getSchemas().contains("surname")); + + assertTrue(policyDAO.findById("617735c7-deb3-40b3-8a9a-683037e523a2", AccessPolicy.class).isEmpty()); + assertTrue(policyDAO.findById("419935c7-deb3-40b3-8a9a-683037e523a2", AccessPolicy.class).isPresent()); + assertTrue(policyDAO.findById(UUID.randomUUID().toString(), AccessPolicy.class).isEmpty()); + + assertTrue(policyDAO.findById("b912a0d4-a890-416f-9ab8-84ab077eb028", AuthPolicy.class).isPresent()); + assertTrue(policyDAO.findById("659b9906-4b6e-4bc0-aca0-6809dff346d4", AuthPolicy.class).isPresent()); + assertTrue(policyDAO.findById(UUID.randomUUID().toString(), AuthPolicy.class).isEmpty()); + + assertTrue(policyDAO.findById("019935c7-deb3-40b3-8a9a-683037e523a2", AttrReleasePolicy.class).isEmpty()); + assertTrue(policyDAO.findById("319935c7-deb3-40b3-8a9a-683037e523a2", AttrReleasePolicy.class).isPresent()); + assertTrue(policyDAO.findById(UUID.randomUUID().toString(), AttrReleasePolicy.class).isEmpty()); + } + + @Test + public void createPropagation() { + long beforeCount = policyDAO.count(); + + PropagationPolicy propagationPolicy = entityFactory.newEntity(PropagationPolicy.class); + propagationPolicy.setName("Propagation policy"); + propagationPolicy.setMaxAttempts(5); + propagationPolicy.setBackOffStrategy(BackOffStrategy.EXPONENTIAL); + propagationPolicy.setBackOffParams(propagationPolicy.getBackOffStrategy().getDefaultBackOffParams()); + + propagationPolicy = policyDAO.save(propagationPolicy); + assertNotNull(propagationPolicy); + assertEquals(5, propagationPolicy.getMaxAttempts()); + assertEquals(BackOffStrategy.EXPONENTIAL, propagationPolicy.getBackOffStrategy()); + assertEquals(BackOffStrategy.EXPONENTIAL.getDefaultBackOffParams(), propagationPolicy.getBackOffParams()); + + long afterCount = policyDAO.count(); + assertEquals(afterCount, beforeCount + 1); + } + + @Test + public void createPull() { + PullPolicy pullPolicy = entityFactory.newEntity(PullPolicy.class); + pullPolicy.setConflictResolutionAction(ConflictResolutionAction.IGNORE); + pullPolicy.setName("Pull policy"); + + final String pullURuleName = "net.tirasa.pull.correlation.TirasaURule"; + final String pullGRuleName = "net.tirasa.pull.correlation.TirasaGRule"; + + Implementation impl1 = entityFactory.newEntity(Implementation.class); + impl1.setKey(pullURuleName); + impl1.setEngine(ImplementationEngine.JAVA); + impl1.setType(IdMImplementationType.PULL_CORRELATION_RULE); + impl1.setBody(PullCorrelationRule.class.getName()); + impl1 = implementationDAO.save(impl1); + + PullCorrelationRuleEntity rule1 = entityFactory.newEntity(PullCorrelationRuleEntity.class); + rule1.setAnyType(anyTypeDAO.getUser()); + rule1.setPullPolicy(pullPolicy); + rule1.setImplementation(impl1); + pullPolicy.add(rule1); + + Implementation impl2 = entityFactory.newEntity(Implementation.class); + impl2.setKey(pullGRuleName); + impl2.setEngine(ImplementationEngine.JAVA); + impl2.setType(IdMImplementationType.PULL_CORRELATION_RULE); + impl2.setBody(PullCorrelationRule.class.getName()); + impl2 = implementationDAO.save(impl2); + + PullCorrelationRuleEntity rule2 = entityFactory.newEntity(PullCorrelationRuleEntity.class); + rule2.setAnyType(anyTypeDAO.getGroup()); + rule2.setPullPolicy(pullPolicy); + rule2.setImplementation(impl2); + pullPolicy.add(rule2); + + pullPolicy = policyDAO.save(pullPolicy); + + assertNotNull(pullPolicy); + assertEquals(pullURuleName, + pullPolicy.getCorrelationRule(AnyTypeKind.USER.name()).get().getImplementation().getKey()); + assertEquals(pullGRuleName, + pullPolicy.getCorrelationRule(AnyTypeKind.GROUP.name()).get().getImplementation().getKey()); + } + + @Test + public void createPush() { + PushPolicy pushPolicy = entityFactory.newEntity(PushPolicy.class); + pushPolicy.setName("Push policy"); + pushPolicy.setConflictResolutionAction(ConflictResolutionAction.IGNORE); + + final String pushURuleName = "net.tirasa.push.correlation.TirasaURule"; + final String pushGRuleName = "net.tirasa.push.correlation.TirasaGRule"; + + Implementation impl1 = entityFactory.newEntity(Implementation.class); + impl1.setKey(pushURuleName); + impl1.setEngine(ImplementationEngine.JAVA); + impl1.setType(IdMImplementationType.PUSH_CORRELATION_RULE); + impl1.setBody(PushCorrelationRule.class.getName()); + impl1 = implementationDAO.save(impl1); + + PushCorrelationRuleEntity rule1 = entityFactory.newEntity(PushCorrelationRuleEntity.class); + rule1.setAnyType(anyTypeDAO.getUser()); + rule1.setPushPolicy(pushPolicy); + rule1.setImplementation(impl1); + pushPolicy.add(rule1); + + Implementation impl2 = entityFactory.newEntity(Implementation.class); + impl2.setKey(pushGRuleName); + impl2.setEngine(ImplementationEngine.JAVA); + impl2.setType(IdMImplementationType.PUSH_CORRELATION_RULE); + impl2.setBody(PushCorrelationRule.class.getName()); + impl2 = implementationDAO.save(impl2); + + PushCorrelationRuleEntity rule2 = entityFactory.newEntity(PushCorrelationRuleEntity.class); + rule2.setAnyType(anyTypeDAO.getGroup()); + rule2.setPushPolicy(pushPolicy); + rule2.setImplementation(impl2); + pushPolicy.add(rule2); + + pushPolicy = policyDAO.save(pushPolicy); + + assertNotNull(pushPolicy); + assertEquals(pushURuleName, + pushPolicy.getCorrelationRule(AnyTypeKind.USER.name()).get().getImplementation().getKey()); + assertEquals(pushGRuleName, + pushPolicy.getCorrelationRule(AnyTypeKind.GROUP.name()).get().getImplementation().getKey()); + } + + @Test + public void createAccess() { + long beforeCount = policyDAO.count(); + + AccessPolicy accessPolicy = entityFactory.newEntity(AccessPolicy.class); + accessPolicy.setName("AttrReleasePolicyAllowEverything"); + + DefaultAccessPolicyConf conf = new DefaultAccessPolicyConf(); + conf.getRequiredAttrs().put("cn", "syncope"); + accessPolicy.setConf(conf); + + accessPolicy = policyDAO.save(accessPolicy); + + assertNotNull(accessPolicy); + assertNotNull(accessPolicy.getKey()); + + long afterCount = policyDAO.count(); + assertEquals(afterCount, beforeCount + 1); + } + + @Test + public void createAuth() { + long beforeCount = policyDAO.count(); + + AuthPolicy authPolicy = entityFactory.newEntity(AuthPolicy.class); + authPolicy.setName("AuthPolicyTest"); + + DefaultAuthPolicyConf authPolicyConf = new DefaultAuthPolicyConf(); + authPolicyConf.getAuthModules().addAll(List.of("LdapAuthentication1", "DatabaseAuthentication2")); + authPolicyConf.setTryAll(true); + authPolicy.setConf(authPolicyConf); + + authPolicy = policyDAO.save(authPolicy); + + assertNotNull(authPolicy); + assertNotNull(authPolicy.getKey()); + + long afterCount = policyDAO.count(); + assertEquals(afterCount, beforeCount + 1); + } + + @Test + public void createAttrRelease() { + long beforeCount = policyDAO.count(); + + AttrReleasePolicy attrReleasePolicy = entityFactory.newEntity(AttrReleasePolicy.class); + attrReleasePolicy.setName("AttrReleasePolicyAllowEverything"); + attrReleasePolicy.setStatus(Boolean.TRUE); + + DefaultAttrReleasePolicyConf attrReleasePolicyConf = new DefaultAttrReleasePolicyConf(); + attrReleasePolicyConf.getAllowedAttrs().add("*"); + attrReleasePolicyConf.getIncludeOnlyAttrs().add("cn"); + attrReleasePolicy.setConf(attrReleasePolicyConf); + + attrReleasePolicy = policyDAO.save(attrReleasePolicy); + + assertNotNull(attrReleasePolicy); + assertNotNull(attrReleasePolicy.getKey()); + assertNotNull(attrReleasePolicy.getStatus()); + assertNotNull(((DefaultAttrReleasePolicyConf) attrReleasePolicy.getConf()).getAllowedAttrs()); + + long afterCount = policyDAO.count(); + assertEquals(afterCount, beforeCount + 1); + } + + @Test + public void createTicketExpiration() { + long beforeCount = policyDAO.count(); + + TicketExpirationPolicy ticketExpirationPolicy = entityFactory.newEntity(TicketExpirationPolicy.class); + ticketExpirationPolicy.setName("TicketExpirationPolicyTest"); + + DefaultTicketExpirationPolicyConf ticketExpirationPolicyConf = new DefaultTicketExpirationPolicyConf(); + DefaultTicketExpirationPolicyConf.TGTConf tgtConf = new DefaultTicketExpirationPolicyConf.TGTConf(); + tgtConf.setMaxTimeToLiveInSeconds(110); + ticketExpirationPolicyConf.setTgtConf(tgtConf); + DefaultTicketExpirationPolicyConf.STConf stConf = new DefaultTicketExpirationPolicyConf.STConf(); + stConf.setMaxTimeToLiveInSeconds(0); + stConf.setNumberOfUses(1); + ticketExpirationPolicyConf.setStConf(stConf); + ticketExpirationPolicy.setConf(ticketExpirationPolicyConf); + + ticketExpirationPolicy = policyDAO.save(ticketExpirationPolicy); + + assertNotNull(ticketExpirationPolicy); + assertNotNull(ticketExpirationPolicy.getKey()); + assertNotNull(((DefaultTicketExpirationPolicyConf) ticketExpirationPolicy.getConf()).getTgtConf()); + assertNotNull(((DefaultTicketExpirationPolicyConf) ticketExpirationPolicy.getConf()).getStConf()); + + long afterCount = policyDAO.count(); + assertEquals(afterCount, beforeCount + 1); + } + + @Test + public void update() { + PasswordPolicy policy = policyDAO.findById( + "ce93fcda-dc3a-4369-a7b0-a6108c261c85", PasswordPolicy.class).orElseThrow(); + assertEquals(1, policy.getRules().size()); + + DefaultPasswordRuleConf ruleConf = new DefaultPasswordRuleConf(); + ruleConf.setMaxLength(8); + ruleConf.setMinLength(6); + + Implementation rule = entityFactory.newEntity(Implementation.class); + rule.setKey("PasswordRule" + UUID.randomUUID().toString()); + rule.setEngine(ImplementationEngine.JAVA); + rule.setType(IdRepoImplementationType.PASSWORD_RULE); + rule.setBody(POJOHelper.serialize(ruleConf)); + rule = implementationDAO.save(rule); + + policy.add(rule); + + policy = policyDAO.save(policy); + + assertNotNull(policy); + + rule = policy.getRules().get(1); + + DefaultPasswordRuleConf actual = POJOHelper.deserialize(rule.getBody(), DefaultPasswordRuleConf.class); + assertEquals(actual.getMaxLength(), 8); + assertEquals(actual.getMinLength(), 6); + } + + @Test + public void delete() { + Policy policy = policyDAO.findById("66691e96-285f-4464-bc19-e68384ea4c85").orElseThrow(); + policyDAO.delete(policy); + assertTrue(policyDAO.findById("66691e96-285f-4464-bc19-e68384ea4c85").isEmpty()); + + AccessPolicy accessPolicy = policyDAO.findById( + "419935c7-deb3-40b3-8a9a-683037e523a2", AccessPolicy.class).orElseThrow(); + policyDAO.delete(accessPolicy); + assertTrue(policyDAO.findById("419935c7-deb3-40b3-8a9a-683037e523a2").isEmpty()); + assertTrue(policyDAO.findById("419935c7-deb3-40b3-8a9a-683037e523a2", AccessPolicy.class).isEmpty()); + + AuthPolicy authPolicy = policyDAO.findById( + "b912a0d4-a890-416f-9ab8-84ab077eb028", AuthPolicy.class).orElseThrow(); + policyDAO.delete(authPolicy); + assertTrue(policyDAO.findById("b912a0d4-a890-416f-9ab8-84ab077eb028").isEmpty()); + assertTrue(policyDAO.findById("b912a0d4-a890-416f-9ab8-84ab077eb028", AuthPolicy.class).isEmpty()); + + AttrReleasePolicy attrReleasepolicy = policyDAO.findById( + "319935c7-deb3-40b3-8a9a-683037e523a2", AttrReleasePolicy.class).orElseThrow(); + policyDAO.delete(attrReleasepolicy); + assertTrue(policyDAO.findById("319935c7-deb3-40b3-8a9a-683037e523a2").isEmpty()); + assertTrue(policyDAO.findById("319935c7-deb3-40b3-8a9a-683037e523a2", AttrReleasePolicy.class).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 new file mode 100644 index 0000000000..efe69a9b79 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RealmTest.java @@ -0,0 +1,184 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.inner; + +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 static org.junit.jupiter.api.Assertions.fail; + +import java.util.List; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.EntityViolationType; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.dao.MalformedPathException; +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.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.neo4j.AbstractTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class RealmTest extends AbstractTest { + + @Autowired + private RealmDAO realmDAO; + + @Autowired + private PolicyDAO policyDAO; + + @Test + public void getRoot() { + assertNotNull(realmDAO.getRoot()); + } + + @Test + public void find() { + Realm realm = realmDAO.findById("e4c28e7a-9dbf-4ee7-9441-93812a0d4a28").orElseThrow(); + assertEquals(SyncopeConstants.ROOT_REALM, realm.getName()); + assertEquals(SyncopeConstants.ROOT_REALM, realm.getFullPath()); + + realm = realmDAO.findById("c5b75db1-fce7-470f-b780-3b9934d82a9d").orElseThrow(); + assertEquals("even", realm.getName()); + assertEquals("/even", realm.getFullPath()); + assertEquals("e4c28e7a-9dbf-4ee7-9441-93812a0d4a28", realm.getParent().getKey()); + assertEquals(realmDAO.getRoot(), realm.getParent()); + + realm = realmDAO.findByFullPath("/even/two").orElseThrow(); + assertEquals("0679e069-7355-4b20-bd11-a5a0a5453c7c", realm.getKey()); + assertEquals("two", realm.getName()); + assertEquals("/even/two", realm.getFullPath()); + } + + @Test + public void findInvalidPath() { + assertThrows(MalformedPathException.class, () -> realmDAO.findByFullPath("even/two")); + } + + @Test + public void findDescendants() { + List found = realmDAO.findDescendants(SyncopeConstants.ROOT_REALM, SyncopeConstants.ROOT_REALM); + assertEquals(4, found.size()); + assertTrue(found.stream().allMatch(f -> SyncopeConstants.UUID_PATTERN.matcher(f).matches())); + + assertEquals( + found, + realmDAO.findDescendants(SyncopeConstants.ROOT_REALM, null, Pageable.unpaged()).stream(). + map(Realm::getKey).toList()); + } + + @Test + public void findChildren() { + List children = realmDAO.findChildren( + realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); + assertEquals(2, children.size()); + assertTrue(children.contains(realmDAO.findByFullPath("/odd").orElseThrow())); + assertTrue(children.contains(realmDAO.findByFullPath("/even").orElseThrow())); + + children = realmDAO.findChildren(realmDAO.findByFullPath("/odd").orElseThrow()); + assertTrue(children.isEmpty()); + } + + @Test + public void findAll() { + List list = realmDAO.findDescendants(realmDAO.getRoot().getFullPath(), null, Pageable.unpaged()); + assertNotNull(list); + assertFalse(list.isEmpty()); + list.forEach(Assertions::assertNotNull); + + assertEquals(4, realmDAO.findAll(Pageable.ofSize(100)).stream().count()); + } + + @Test + public void save() { + Realm realm = entityFactory.newEntity(Realm.class); + realm.setName("last"); + realm.setParent(realmDAO.findByFullPath("/even/two").orElseThrow()); + + Realm actual = realmDAO.save(realm); + assertNotNull(actual.getKey()); + assertEquals("last", actual.getName()); + assertEquals("/even/two/last", actual.getFullPath()); + assertEquals(realmDAO.findByFullPath("/even/two").orElseThrow(), actual.getParent()); + assertEquals("20ab5a8c-4b0c-432c-b957-f7fb9784d9f7", realm.getAccountPolicy().getKey()); + assertEquals("ce93fcda-dc3a-4369-a7b0-a6108c261c85", realm.getPasswordPolicy().getKey()); + + realm = actual; + realm.setAccountPolicy(policyDAO.findById( + "06e2ed52-6966-44aa-a177-a0ca7434201f", AccountPolicy.class).orElseThrow()); + realm.setPasswordPolicy(policyDAO.findById( + "986d1236-3ac5-4a19-810c-5ab21d79cba1", PasswordPolicy.class).orElseThrow()); + + actual = realmDAO.save(realm); + assertEquals("06e2ed52-6966-44aa-a177-a0ca7434201f", actual.getAccountPolicy().getKey()); + assertEquals("986d1236-3ac5-4a19-810c-5ab21d79cba1", actual.getPasswordPolicy().getKey()); + } + + @Test + public void saveInvalidName() { + Realm realm = entityFactory.newEntity(Realm.class); + realm.setName(" a name"); + realm.setParent(realmDAO.getRoot()); + + try { + realmDAO.save(realm); + fail("This should not happen"); + } catch (InvalidEntityException e) { + assertTrue(e.hasViolation(EntityViolationType.InvalidRealm)); + } + } + + @Test + public void saveNullParent() { + Realm realm = entityFactory.newEntity(Realm.class); + realm.setName("name"); + realm.setParent(null); + + try { + realmDAO.save(realm); + fail("This should not happen"); + } catch (InvalidEntityException e) { + assertTrue(e.hasViolation(EntityViolationType.InvalidRealm)); + } + } + + @Test + public void delete() { + Realm realm = entityFactory.newEntity(Realm.class); + realm.setName("name"); + realm.setParent(realmDAO.getRoot()); + + Realm actual = realmDAO.save(realm); + assertNotNull(actual); + + actual = realmDAO.findById(actual.getKey()).orElseThrow(); + assertNotNull(actual); + + realmDAO.delete(actual); + assertTrue(realmDAO.findById(actual.getKey()).isEmpty()); + } +} 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 new file mode 100644 index 0000000000..0fd460c74c --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RelationshipTypeTest.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.persistence.neo4j.inner; + +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 org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; +import org.apache.syncope.core.persistence.api.entity.RelationshipType; +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class RelationshipTypeTest extends AbstractTest { + + @Autowired + private RelationshipTypeDAO relationshipTypeDAO; + + @Autowired + private AnyObjectDAO anyObjectDAO; + + @Test + public void find() { + RelationshipType inclusion = relationshipTypeDAO.findById("inclusion").orElseThrow(); + assertEquals("inclusion", inclusion.getKey()); + } + + @Test + public void findAll() { + List list = relationshipTypeDAO.findAll(); + assertFalse(list.isEmpty()); + } + + @Test + public void save() { + RelationshipType newType = entityFactory.newEntity(RelationshipType.class); + newType.setKey("new type"); + newType.setDescription("description"); + + newType = relationshipTypeDAO.save(newType); + assertNotNull(newType); + assertEquals("description", newType.getDescription()); + } + + @Test + public void saveInvalidName() { + assertThrows(InvalidEntityException.class, () -> { + RelationshipType newType = entityFactory.newEntity(RelationshipType.class); + newType.setKey("membership"); + relationshipTypeDAO.save(newType); + }); + } + + @Test + public void delete() { + RelationshipType type = relationshipTypeDAO.findById("neighborhood").orElseThrow(); + + relationshipTypeDAO.deleteById(type.getKey()); + + assertTrue(relationshipTypeDAO.findById("neighborhood").isEmpty()); + } + + @Test + public void deleteOnAnyObject() { + RelationshipType neighborhood = relationshipTypeDAO.findById("neighborhood").orElseThrow(); + + AnyObject anyObject = anyObjectDAO.findById("fc6dbc3a-6c07-4965-8781-921e7401a4a5").orElseThrow(); + assertNotNull(anyObject.getRelationships(neighborhood)); + assertFalse(anyObject.getRelationships(neighborhood).isEmpty()); + + relationshipTypeDAO.deleteById("neighborhood"); + + anyObject = anyObjectDAO.findById("fc6dbc3a-6c07-4965-8781-921e7401a4a5").orElseThrow(); + assertNotNull(anyObject); + assertTrue(anyObject.getRelationships().isEmpty()); + } +} 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 new file mode 100644 index 0000000000..2c9b8c21a8 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RemediationTest.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.neo4j.inner; + +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 static org.junit.jupiter.api.Assertions.fail; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import org.apache.syncope.common.lib.types.EntityViolationType; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.common.lib.types.TaskType; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +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.Remediation; +import org.apache.syncope.core.persistence.api.entity.task.PullTask; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class RemediationTest extends AbstractTest { + + @Autowired + private RemediationDAO remediationDAO; + + @Autowired + private AnyTypeDAO anyTypeDAO; + + @Autowired + private TaskDAO taskDAO; + + @Test + public void findAll() { + List remediations = remediationDAO.findAll(null, null, Pageable.unpaged()); + assertTrue(remediations.isEmpty()); + } + + @Test + public void createMissingPayload() { + Remediation remediation = entityFactory.newEntity(Remediation.class); + remediation.setAnyType(anyTypeDAO.findById("PRINTER").orElseThrow()); + remediation.setOperation(ResourceOperation.CREATE); + remediation.setError("Error"); + remediation.setInstant(OffsetDateTime.now()); + remediation.setRemoteName("remote"); + remediation.setPullTask((PullTask) taskDAO.findById( + TaskType.PULL, "38abbf9e-a1a3-40a1-a15f-7d0ac02f47f1").orElseThrow()); + + // missing payload + try { + remediationDAO.save(remediation); + fail("This should not happen"); + } catch (InvalidEntityException e) { + Set violations = e.getViolations().values().iterator().next(); + assertEquals(2, violations.size()); + assertTrue(violations.stream().allMatch(violation -> violation.getPropertyPath().equals("payload"))); + } + } + + @Test + public void createWrongPayload() { + Remediation remediation = entityFactory.newEntity(Remediation.class); + remediation.setAnyType(anyTypeDAO.findById("PRINTER").orElseThrow()); + remediation.setOperation(ResourceOperation.CREATE); + remediation.setError("Error"); + remediation.setInstant(OffsetDateTime.now()); + remediation.setRemoteName("remote"); + remediation.setPullTask((PullTask) taskDAO.findById( + TaskType.PULL, "38abbf9e-a1a3-40a1-a15f-7d0ac02f47f1").orElseThrow()); + remediation.setPayload(UUID.randomUUID().toString()); + + // wrong payload for operation + try { + remediationDAO.save(remediation); + fail("This should not happen"); + } catch (InvalidEntityException e) { + Set violations = e.getViolations().values().iterator().next(); + assertEquals(1, violations.size()); + assertTrue(violations.stream().anyMatch(violation -> violation.getPropertyPath().equals("payload"))); + } + } + + @Test + public void create() { + Remediation remediation = entityFactory.newEntity(Remediation.class); + remediation.setAnyType(anyTypeDAO.findById("PRINTER").orElseThrow()); + remediation.setOperation(ResourceOperation.CREATE); + remediation.setError("Error"); + remediation.setInstant(OffsetDateTime.now()); + remediation.setRemoteName("remote"); + remediation.setPullTask((PullTask) taskDAO.findById( + TaskType.PULL, "38abbf9e-a1a3-40a1-a15f-7d0ac02f47f1").orElseThrow()); + remediation.setPayload(UUID.randomUUID().toString()); + remediation.setOperation(ResourceOperation.DELETE); + + remediation = remediationDAO.save(remediation); + assertNotNull(remediation.getKey()); + assertNotNull(remediation.getPullTask()); + + taskDAO.delete(remediation.getPullTask()); + + remediation = remediationDAO.findById(remediation.getKey()).orElseThrow(); + assertNull(remediation.getPullTask()); + } +} 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 new file mode 100644 index 0000000000..f2acfccb24 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ReportTest.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.persistence.neo4j.inner; + +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 jakarta.ws.rs.core.MediaType; +import java.util.List; +import java.util.UUID; +import org.apache.syncope.core.persistence.api.dao.ImplementationDAO; +import org.apache.syncope.core.persistence.api.dao.ReportDAO; +import org.apache.syncope.core.persistence.api.entity.Report; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class ReportTest extends AbstractTest { + + @Autowired + private ReportDAO reportDAO; + + @Autowired + private ImplementationDAO implementationDAO; + + @Test + public void find() { + Report report = reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").orElseThrow(); + assertNotNull(report); + + assertTrue(reportDAO.findById(UUID.randomUUID().toString()).isEmpty()); + } + + @Test + public void findAll() { + List reports = reportDAO.findAll(); + assertNotNull(reports); + assertEquals(1, reports.size()); + } + + @Test + public void save() { + int beforeCount = reportDAO.findAll().size(); + + Report report = entityFactory.newEntity(Report.class); + report.setName("new report"); + report.setJobDelegate(implementationDAO.findById("SampleReportJobDelegate").orElseThrow()); + report.setMimeType(MediaType.TEXT_PLAIN); + report.setFileExt("txt"); + report.setActive(true); + + report = reportDAO.save(report); + assertNotNull(report); + assertNotNull(report.getKey()); + + int afterCount = reportDAO.findAll().size(); + assertEquals(afterCount, beforeCount + 1); + } + + @Test + public void delete() { + Report report = reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").orElseThrow(); + assertNotNull(report); + + reportDAO.deleteById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b"); + + assertTrue(reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").isEmpty()); + } +} 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 new file mode 100644 index 0000000000..b2f181ca89 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ResourceTest.java @@ -0,0 +1,334 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.inner; + +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 static org.junit.jupiter.api.Assertions.fail; + +import java.util.List; +import java.util.stream.Collectors; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.to.Item; +import org.apache.syncope.common.lib.to.Mapping; +import org.apache.syncope.common.lib.to.Provision; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.EntityViolationType; +import org.apache.syncope.common.lib.types.IdMEntitlement; +import org.apache.syncope.common.lib.types.MappingPurpose; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +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.persistence.neo4j.AbstractTest; +import org.apache.syncope.core.spring.security.DelegatedAdministrationException; +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.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 ResourceTest extends AbstractTest { + + @Autowired + private ExternalResourceDAO resourceDAO; + + @Test + public void findById() { + ExternalResource resource = resourceDAO.findById("ws-target-resource-1").orElseThrow(); + + ConnInstance connector = resource.getConnector(); + assertNotNull(connector); + assertEquals("net.tirasa.connid.bundles.soap.WebServiceConnector", connector.getConnectorName()); + assertEquals("net.tirasa.connid.bundles.soap", connector.getBundleName()); + + Mapping mapping = resource.getProvisionByAnyType(AnyTypeKind.USER.name()).get().getMapping(); + assertFalse(mapping.getItems().isEmpty()); + + assertTrue(mapping.getItems().stream(). + anyMatch(item -> "email".equals(item.getExtAttrName()) && "email".equals(item.getIntAttrName()))); + + try { + resourceDAO.authFind("ws-target-resource-1"); + fail("This should not happen"); + } catch (DelegatedAdministrationException e) { + assertNotNull(e); + } + } + + @Test + public void findWithOrgUnit() { + ExternalResource resource = resourceDAO.findById("resource-ldap-orgunit").orElseThrow(); + assertNotNull(resource.getOrgUnit()); + } + + @Test + public void findAll() { + 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); + + try { + List resources = resourceDAO.findAll(); + assertNotNull(resources); + assertFalse(resources.isEmpty()); + } finally { + SecurityContextHolder.getContext().setAuthentication(null); + } + } + + @Test + public void getConnObjectKey() { + ExternalResource resource = resourceDAO.findById("ws-target-resource-2").orElseThrow(); + assertEquals("fullname", resource.getProvisionByAnyType(AnyTypeKind.USER.name()).get(). + getMapping().getConnObjectKeyItem().get().getIntAttrName()); + } + + @Test + public void save() { + ExternalResource resource = entityFactory.newEntity(ExternalResource.class); + resource.setKey("ws-target-resource-basic-save"); + resource.setPropagationPriority(2); + + Provision provision = new Provision(); + provision.setAnyType(AnyTypeKind.USER.name()); + provision.setObjectClass(ObjectClass.ACCOUNT_NAME); + resource.getProvisions().add(provision); + + Mapping mapping = new Mapping(); + provision.setMapping(mapping); + + Item connObjectKey = new Item(); + connObjectKey.setExtAttrName("username"); + connObjectKey.setIntAttrName("fullname"); + connObjectKey.setPurpose(MappingPurpose.BOTH); + mapping.setConnObjectKeyItem(connObjectKey); + + ConnInstance connector = resourceDAO.findById("ws-target-resource-1").orElseThrow().getConnector(); + resource.setConnector(connector); + + // save the resource + ExternalResource actual = resourceDAO.save(resource); + assertNotNull(actual); + assertNotNull(actual.getConnector()); + assertNotNull(actual.getProvisionByAnyType(AnyTypeKind.USER.name()). + get().getMapping()); + assertFalse(actual.getProvisionByAnyType(AnyTypeKind.USER.name()). + get().getMapping().getItems().isEmpty()); + assertEquals(Integer.valueOf(2), actual.getPropagationPriority()); + } + + @Test + public void saveInvalidMappingIntAttr() { + assertThrows(InvalidEntityException.class, () -> { + ExternalResource resource = entityFactory.newEntity(ExternalResource.class); + resource.setKey("ws-target-resource-basic-save-invalid"); + + ConnInstance connector = resourceDAO.findById("ws-target-resource-1").orElseThrow().getConnector(); + resource.setConnector(connector); + + Provision provision = new Provision(); + provision.setAnyType(AnyTypeKind.USER.name()); + provision.setObjectClass(ObjectClass.ACCOUNT_NAME); + resource.getProvisions().add(provision); + + Mapping mapping = new Mapping(); + provision.setMapping(mapping); + + Item connObjectKey = new Item(); + connObjectKey.setConnObjectKey(true); + mapping.add(connObjectKey); + + // save the resource + resourceDAO.save(resource); + }); + } + + @Test + public void saveInvalidMappingExtAttr() { + assertThrows(InvalidEntityException.class, () -> { + ExternalResource resource = entityFactory.newEntity(ExternalResource.class); + resource.setKey("ws-target-resource-basic-save-invalid"); + + ConnInstance connector = resourceDAO.findById("ws-target-resource-1").orElseThrow().getConnector(); + resource.setConnector(connector); + + Provision provision = new Provision(); + provision.setAnyType(AnyTypeKind.USER.name()); + provision.setObjectClass(ObjectClass.ACCOUNT_NAME); + resource.getProvisions().add(provision); + + Mapping mapping = new Mapping(); + provision.setMapping(mapping); + + Item item = new Item(); + item.setConnObjectKey(true); + item.setIntAttrName("fullname"); + mapping.add(item); + + item = new Item(); + item.setIntAttrName("userId"); + mapping.add(item); + + resourceDAO.save(resource); + }); + } + + @Test + public void saveInvalidProvision() { + assertThrows(InvalidEntityException.class, () -> { + ExternalResource resource = entityFactory.newEntity(ExternalResource.class); + resource.setKey("invalidProvision"); + + Provision provision = new Provision(); + provision.setAnyType(AnyTypeKind.USER.name()); + provision.setObjectClass(ObjectClass.ACCOUNT_NAME); + resource.getProvisions().add(provision); + + Mapping mapping = new Mapping(); + provision.setMapping(mapping); + + Item connObjectKey = new Item(); + connObjectKey.setExtAttrName("username"); + connObjectKey.setIntAttrName("fullname"); + connObjectKey.setPurpose(MappingPurpose.BOTH); + mapping.setConnObjectKeyItem(connObjectKey); + + provision = new Provision(); + provision.setAnyType(AnyTypeKind.GROUP.name()); + provision.setObjectClass(ObjectClass.ACCOUNT_NAME); + resource.getProvisions().add(provision); + + ConnInstance connector = resourceDAO.findById("ws-target-resource-1").orElseThrow().getConnector(); + resource.setConnector(connector); + + // save the resource + resourceDAO.save(resource); + }); + } + + @Test + public void saveVirtualMapping() { + ExternalResource resource = entityFactory.newEntity(ExternalResource.class); + resource.setKey("ws-target-resource-virtual-mapping"); + resource.setPropagationPriority(2); + + Provision provision = new Provision(); + provision.setAnyType(AnyTypeKind.USER.name()); + provision.setObjectClass(ObjectClass.ACCOUNT_NAME); + resource.getProvisions().add(provision); + + Mapping mapping = new Mapping(); + provision.setMapping(mapping); + + Item connObjectKey = new Item(); + connObjectKey.setExtAttrName("username"); + connObjectKey.setIntAttrName("fullname"); + connObjectKey.setPurpose(MappingPurpose.BOTH); + mapping.setConnObjectKeyItem(connObjectKey); + + Item virtualMapItem = new Item(); + virtualMapItem.setIntAttrName("virtualReadOnly"); + virtualMapItem.setExtAttrName("TEST"); + virtualMapItem.setPurpose(MappingPurpose.PROPAGATION); + mapping.add(virtualMapItem); + + ConnInstance connector = resourceDAO.findById("ws-target-resource-1").orElseThrow().getConnector(); + resource.setConnector(connector); + + resourceDAO.save(resource); + } + + @Test + public void saveWithGroupMappingType() { + ExternalResource resource = entityFactory.newEntity(ExternalResource.class); + resource.setKey("ws-target-resource-basic-save-invalid"); + + ConnInstance connector = resourceDAO.findById("ws-target-resource-1").orElseThrow().getConnector(); + resource.setConnector(connector); + + Provision provision = new Provision(); + provision.setAnyType(AnyTypeKind.USER.name()); + provision.setObjectClass(ObjectClass.ACCOUNT_NAME); + resource.getProvisions().add(provision); + + Mapping mapping = new Mapping(); + provision.setMapping(mapping); + + Item item = new Item(); + item.setIntAttrName("fullname"); + item.setExtAttrName("fullname"); + item.setPurpose(MappingPurpose.BOTH); + mapping.setConnObjectKeyItem(item); + + item = new Item(); + item.setIntAttrName("icon"); + item.setExtAttrName("icon"); + item.setPurpose(MappingPurpose.BOTH); + mapping.add(item); + + item = new Item(); + item.setIntAttrName("mderiveddata"); + item.setExtAttrName("mderiveddata"); + item.setPurpose(MappingPurpose.PROPAGATION); + mapping.add(item); + + // save the resource + ExternalResource actual = resourceDAO.save(resource); + assertNotNull(actual); + + assertEquals(3, actual.getProvisionByAnyType(AnyTypeKind.USER.name()).get().getMapping().getItems().size()); + } + + @Test + public void delete() { + ExternalResource resource = resourceDAO.findById("ws-target-resource-2").orElseThrow(); + + resourceDAO.deleteById(resource.getKey()); + + assertTrue(resourceDAO.findById("ws-target-resource-2").isEmpty()); + } + + @Test + public void issueSYNCOPE418() { + ExternalResource resource = entityFactory.newEntity(ExternalResource.class); + resource.setKey("http://schemas.examples.org/security/authorization/organizationUnit"); + + try { + resourceDAO.save(resource); + fail("This should not happen"); + } catch (InvalidEntityException e) { + assertTrue(e.hasViolation(EntityViolationType.InvalidKey)); + } + } +} 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 new file mode 100644 index 0000000000..c5733dd848 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RoleTest.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.persistence.neo4j.inner; + +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.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.persistence.api.dao.RealmDAO; +import org.apache.syncope.core.persistence.api.dao.RoleDAO; +import org.apache.syncope.core.persistence.api.entity.Role; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class RoleTest extends AbstractTest { + + @Autowired + private RoleDAO roleDAO; + + @Autowired + private RealmDAO realmDAO; + + @Test + public void find() { + Role role = roleDAO.findById("User manager").orElseThrow(); + assertNotNull(role.getKey()); + assertFalse(role.getRealms().isEmpty()); + assertFalse(role.getEntitlements().isEmpty()); + assertTrue(role.getEntitlements().contains(IdRepoEntitlement.USER_SEARCH)); + } + + @Test + public void findAll() { + List list = roleDAO.findAll(); + assertNotNull(list); + assertFalse(list.isEmpty()); + list.forEach(Assertions::assertNotNull); + } + + @Test + public void save() { + Role role = entityFactory.newEntity(Role.class); + role.setKey("new"); + role.add(realmDAO.getRoot()); + role.add(realmDAO.findByFullPath("/even/two").orElseThrow()); + role.getEntitlements().add(IdRepoEntitlement.AUDIT_LIST); + role.getEntitlements().add(IdRepoEntitlement.AUDIT_SET); + + Role actual = roleDAO.save(role); + assertNotNull(actual); + } + + @Test + public void delete() { + assertTrue(roleDAO.findById("Other").isPresent()); + + roleDAO.deleteById("Other"); + + assertTrue(roleDAO.findById("Other").isEmpty()); + } +} 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 new file mode 100644 index 0000000000..8571be7c03 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2IdPEntityTest.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.core.persistence.neo4j.inner; + +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.nio.charset.StandardCharsets; +import java.util.UUID; +import org.apache.syncope.core.persistence.api.dao.SAML2IdPEntityDAO; +import org.apache.syncope.core.persistence.api.entity.am.SAML2IdPEntity; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class SAML2IdPEntityTest extends AbstractTest { + + @Autowired + private SAML2IdPEntityDAO saml2IdPEntityDAO; + + private SAML2IdPEntity create(final String owner) { + SAML2IdPEntity entity = entityFactory.newEntity(SAML2IdPEntity.class); + entity.setKey(owner); + entity.setMetadata("metadata".getBytes(StandardCharsets.UTF_8)); + entity.setEncryptionCertificate("encryptionCert".getBytes(StandardCharsets.UTF_8)); + entity.setEncryptionKey("encryptionKey".getBytes(StandardCharsets.UTF_8)); + entity.setSigningCertificate("signatureCert".getBytes(StandardCharsets.UTF_8)); + entity.setSigningKey("signatureKey".getBytes(StandardCharsets.UTF_8)); + entity = saml2IdPEntityDAO.save(entity); + assertNotNull(entity); + assertNotNull(saml2IdPEntityDAO.findById(entity.getKey())); + + return entity; + } + + @Test + public void find() { + create("Syncope"); + + assertTrue(saml2IdPEntityDAO.findById("Syncope").isPresent()); + + assertTrue(saml2IdPEntityDAO.findById(UUID.randomUUID().toString()).isEmpty()); + } + + @Test + public void save() { + create("SyncopeCreate"); + } + + @Test + public void update() { + SAML2IdPEntity entity = create("SyncopeUpdate"); + assertNotNull(entity); + entity.setKey("OtherSyncope"); + + entity = saml2IdPEntityDAO.save(entity); + assertNotNull(entity); + + SAML2IdPEntity found = saml2IdPEntityDAO.findById(entity.getKey()).orElseThrow(); + assertEquals("OtherSyncope", found.getKey()); + } +} diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2SPEntityTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2SPEntityTest.java new file mode 100644 index 0000000000..27232e6464 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2SPEntityTest.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.inner; + +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.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.Signature; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.util.Date; +import java.util.UUID; +import org.apache.cxf.helpers.IOUtils; +import org.apache.syncope.core.persistence.api.dao.SAML2SPEntityDAO; +import org.apache.syncope.core.persistence.api.entity.am.SAML2SPEntity; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.DERBitString; +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.asn1.x509.TBSCertificate; +import org.bouncycastle.asn1.x509.Time; +import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ClassPathResource; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class SAML2SPEntityTest extends AbstractTest { + + private static Certificate createSelfSignedCert(final KeyPair keyPair) throws Exception { + X500Name dn = new X500Name("cn=Unknown"); + V3TBSCertificateGenerator certGen = new V3TBSCertificateGenerator(); + + certGen.setSerialNumber(new ASN1Integer(BigInteger.valueOf(1))); + certGen.setIssuer(dn); + certGen.setSubject(dn); + certGen.setStartDate(new Time(new Date(System.currentTimeMillis() - 1000L))); + + Date expiration = new Date(System.currentTimeMillis() + 100000); + certGen.setEndDate(new Time(expiration)); + + AlgorithmIdentifier sigAlgID = new AlgorithmIdentifier( + PKCSObjectIdentifiers.sha1WithRSAEncryption, DERNull.INSTANCE); + certGen.setSignature(sigAlgID); + certGen.setSubjectPublicKeyInfo(SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded())); + + Signature sig = Signature.getInstance("SHA1WithRSA"); + sig.initSign(keyPair.getPrivate()); + sig.update(certGen.generateTBSCertificate().getEncoded(ASN1Encoding.DER)); + + TBSCertificate tbsCert = certGen.generateTBSCertificate(); + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(tbsCert); + v.add(sigAlgID); + v.add(new DERBitString(sig.sign())); + + Certificate cert = CertificateFactory.getInstance("X.509"). + generateCertificate(new ByteArrayInputStream(new DERSequence(v).getEncoded(ASN1Encoding.DER))); + cert.verify(keyPair.getPublic()); + return cert; + } + + @Autowired + private SAML2SPEntityDAO saml2SPEntityDAO; + + private SAML2SPEntity create(final String owner) throws Exception { + SAML2SPEntity entity = entityFactory.newEntity(SAML2SPEntity.class); + entity.setKey(owner); + entity.setMetadata(IOUtils.toString( + new ClassPathResource("sp-metadata.xml").getInputStream()).getBytes(StandardCharsets.UTF_8)); + + KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); + char[] pwdArray = "password".toCharArray(); + ks.load(null, pwdArray); + + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(4096); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + Certificate certificate = createSelfSignedCert(keyPair); + ks.setKeyEntry("main", keyPair.getPrivate(), "password".toCharArray(), new Certificate[] { certificate }); + + try (ByteArrayOutputStream fos = new ByteArrayOutputStream()) { + ks.store(fos, pwdArray); + fos.flush(); + entity.setKeystore(fos.toByteArray()); + } + assertNotNull(entity.getKeystore()); + + entity = saml2SPEntityDAO.save(entity); + assertNotNull(saml2SPEntityDAO.findById(entity.getKey())); + return entity; + } + + @Test + public void find() throws Exception { + create("Syncope"); + + assertTrue(saml2SPEntityDAO.findById("Syncope").isPresent()); + + assertTrue(saml2SPEntityDAO.findById(UUID.randomUUID().toString()).isEmpty()); + } + + @Test + public void save() throws Exception { + SAML2SPEntity created = create("SyncopeCreate"); + + KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); + char[] pwdArray = "password".toCharArray(); + ks.load(new ByteArrayInputStream(created.getKeystore()), pwdArray); + assertTrue(ks.size() > 0); + } + + @Test + public void update() throws Exception { + SAML2SPEntity entity = create("SyncopeUpdate"); + assertNotNull(entity); + entity.setKey("OtherSyncope"); + + entity = saml2SPEntityDAO.save(entity); + assertNotNull(entity); + + SAML2SPEntity found = saml2SPEntityDAO.findById(entity.getKey()).orElseThrow(); + assertEquals("OtherSyncope", found.getKey()); + } +} 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 new file mode 100644 index 0000000000..8f55c29de8 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SAML2SPTest.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.persistence.neo4j.inner; + +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.UUID; +import org.apache.syncope.common.lib.types.SAML2SPNameId; +import org.apache.syncope.common.lib.types.XmlSecAlgorithm; +import org.apache.syncope.core.persistence.api.dao.SAML2SPClientAppDAO; +import org.apache.syncope.core.persistence.api.entity.am.SAML2SPClientApp; +import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class SAML2SPTest extends AbstractClientAppTest { + + @Autowired + private SAML2SPClientAppDAO saml2spDAO; + + @Test + public void find() { + long beforeCount = saml2spDAO.count(); + + SAML2SPClientApp sp = entityFactory.newEntity(SAML2SPClientApp.class); + sp.setName("SAML2"); + sp.setClientAppId(UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE); + sp.setDescription("This is a sample SAML2 SP"); + sp.setEntityId("urn:example:saml2:sp"); + sp.setMetadataLocation("https://example.org/metadata.xml"); + sp.setRequiredNameIdFormat(SAML2SPNameId.EMAIL_ADDRESS); + sp.setEncryptionOptional(true); + sp.setEncryptAssertions(true); + sp.getEncryptionDataAlgorithms().add(XmlSecAlgorithm.AES_128_GCM); + sp.getEncryptionKeyAlgorithms().add(XmlSecAlgorithm.RSA_OAEP_11); + sp.getSigningSignatureReferenceDigestMethods().add(XmlSecAlgorithm.SHA1); + sp.getSigningSignatureAlgorithms().add(XmlSecAlgorithm.SHA256); + sp.getSigningSignatureAlgorithms().add(XmlSecAlgorithm.SHA512); + + AccessPolicy accessPolicy = buildAndSaveAccessPolicy(); + sp.setAccessPolicy(accessPolicy); + + AuthPolicy authnPolicy = buildAndSaveAuthPolicy(); + sp.setAuthPolicy(authnPolicy); + + saml2spDAO.save(sp); + + assertNotNull(sp); + assertNotNull(sp.getKey()); + + long afterCount = saml2spDAO.count(); + assertEquals(afterCount, beforeCount + 1); + + sp = saml2spDAO.findByEntityId(sp.getEntityId()).orElseThrow(); + assertNotNull(sp); + + sp = saml2spDAO.findByName(sp.getName()).orElseThrow(); + assertNotNull(sp); + + sp = saml2spDAO.findByClientAppId(sp.getClientAppId()).orElseThrow(); + assertNotNull(sp); + + assertFalse(sp.getSigningSignatureAlgorithms().isEmpty()); + assertFalse(sp.getSigningSignatureReferenceDigestMethods().isEmpty()); + assertFalse(sp.getEncryptionDataAlgorithms().isEmpty()); + assertFalse(sp.getEncryptionKeyAlgorithms().isEmpty()); + + saml2spDAO.deleteByEntityId(sp.getEntityId()); + assertTrue(saml2spDAO.findByName(sp.getName()).isEmpty()); + } +} diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SRARouteTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SRARouteTest.java new file mode 100644 index 0000000000..303d830ae5 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SRARouteTest.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.persistence.neo4j.inner; + +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 jakarta.ws.rs.HttpMethod; +import java.net.URI; +import java.util.List; +import java.util.UUID; +import org.apache.syncope.common.lib.types.SRARouteFilter; +import org.apache.syncope.common.lib.types.SRARouteFilterFactory; +import org.apache.syncope.common.lib.types.SRARoutePredicate; +import org.apache.syncope.common.lib.types.SRARoutePredicateFactory; +import org.apache.syncope.common.lib.types.SRARouteType; +import org.apache.syncope.core.persistence.api.dao.SRARouteDAO; +import org.apache.syncope.core.persistence.api.entity.SRARoute; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class SRARouteTest extends AbstractTest { + + @Autowired + private SRARouteDAO routeDAO; + + @Test + public void find() { + SRARoute route = routeDAO.findById("ec7bada2-3dd6-460c-8441-65521d005ffa").orElseThrow(); + assertEquals(1, route.getPredicates().size()); + + assertTrue(routeDAO.findById(UUID.randomUUID().toString()).isEmpty()); + } + + @Test + public void findAll() { + List routes = routeDAO.findAll(); + assertNotNull(routes); + assertEquals(1, routes.size()); + } + + @Test + public void save() { + SRARoute route = entityFactory.newEntity(SRARoute.class); + route.setName("just for test"); + route.setTarget(URI.create("http://localhost:80")); + route.setType(SRARouteType.PUBLIC); + route.setPredicates(List.of(new SRARoutePredicate.Builder(). + factory(SRARoutePredicateFactory.METHOD).args(HttpMethod.GET).build())); + route.setFilters(List.of(new SRARouteFilter.Builder(). + factory(SRARouteFilterFactory.ADD_REQUEST_HEADER).args("X-Request-Foo, Bar").build())); + + int beforeCount = routeDAO.findAll().size(); + + route = routeDAO.save(route); + assertNotNull(route); + assertNotNull(route.getKey()); + + int afterCount = routeDAO.findAll().size(); + assertEquals(afterCount, beforeCount + 1); + } + + @Test + public void delete() { + assertTrue(routeDAO.findById("ec7bada2-3dd6-460c-8441-65521d005ffa").isPresent()); + + routeDAO.deleteById("ec7bada2-3dd6-460c-8441-65521d005ffa"); + + assertTrue(routeDAO.findById("ec7bada2-3dd6-460c-8441-65521d005ffa").isEmpty()); + } +} 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 new file mode 100644 index 0000000000..d6aa353fbd --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/SecurityQuestionTest.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.core.persistence.neo4j.inner; + +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.core.persistence.api.dao.SecurityQuestionDAO; +import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class SecurityQuestionTest extends AbstractTest { + + @Autowired + private SecurityQuestionDAO securityQuestionDAO; + + @Test + public void find() { + SecurityQuestion question = securityQuestionDAO.findById("887028ea-66fc-41e7-b397-620d7ea6dfbb").orElseThrow(); + assertNotNull(question.getContent()); + } + + @Test + public void findAll() { + List securityQuestions = securityQuestionDAO.findAll(); + assertNotNull(securityQuestions); + assertFalse(securityQuestions.isEmpty()); + } + + @Test + public void save() { + SecurityQuestion securityQuestion = entityFactory.newEntity(SecurityQuestion.class); + securityQuestion.setContent("What is your favorite pet's name?"); + + SecurityQuestion actual = securityQuestionDAO.save(securityQuestion); + assertNotNull(actual); + assertNotNull(actual.getKey()); + } + + @Test + public void delete() { + assertTrue(securityQuestionDAO.findById("887028ea-66fc-41e7-b397-620d7ea6dfbb").isPresent()); + + securityQuestionDAO.deleteById("887028ea-66fc-41e7-b397-620d7ea6dfbb"); + + assertTrue(securityQuestionDAO.findById("887028ea-66fc-41e7-b397-620d7ea6dfbb").isEmpty()); + } +} 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 new file mode 100644 index 0000000000..d6d27bb91e --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/TaskExecTest.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.persistence.neo4j.inner; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.time.OffsetDateTime; +import java.util.List; +import org.apache.syncope.common.lib.types.ExecStatus; +import org.apache.syncope.common.lib.types.TaskType; +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.task.PropagationTask; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory; +import org.apache.syncope.core.persistence.api.utils.FormatUtils; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class TaskExecTest extends AbstractTest { + + @Autowired + private TaskExecDAO taskExecDAO; + + @Autowired + private TaskDAO taskDAO; + + @Autowired + private TaskUtilsFactory taskUtilsFactory; + + @Test + public void findAll() { + PropagationTask task = (PropagationTask) taskDAO.findById( + TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); + assertNotNull(task); + + OffsetDateTime startedBefore = OffsetDateTime.of(2015, 12, 18, 0, 0, 0, 0, FormatUtils.DEFAULT_OFFSET); + + List> execs = taskExecDAO.findAll(task, startedBefore, null, Pageable.unpaged()); + assertNotNull(execs); + assertEquals(1, execs.size()); + } + + @Test + public void findLatestStarted() { + PropagationTask task = (PropagationTask) taskDAO.findById( + TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); + assertNotNull(task); + + TaskExec latestStarted = taskExecDAO.findLatestStarted(TaskType.PROPAGATION, task).orElseThrow(); + assertEquals("e58ca1c7-178a-4012-8a71-8aa14eaf0655", latestStarted.getKey()); + } + + @Test + public void issueSYNCOPE214() { + PropagationTask task = (PropagationTask) taskDAO.findById( + TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); + assertNotNull(task); + + String faultyMessage = "A faulty message"; + faultyMessage = faultyMessage.replace('a', '\0'); + + TaskExec exec = taskUtilsFactory.getInstance(TaskType.PROPAGATION).newTaskExec(); + exec.setStart(OffsetDateTime.now()); + exec.setEnd(OffsetDateTime.now()); + exec.setStatus(ExecStatus.SUCCESS.name()); + exec.setMessage(faultyMessage); + exec.setExecutor("admin"); + + task.add(exec); + exec.setTask(task); + + exec = taskExecDAO.save(exec); + assertNotNull(exec); + + assertEquals(faultyMessage.replace('\0', '\n'), exec.getMessage()); + } +} 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 new file mode 100644 index 0000000000..82c85740fa --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/TaskTest.java @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.inner; + +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.HashSet; +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.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.common.lib.types.TaskType; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.TaskDAO; +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.PropagationTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.apache.syncope.core.spring.security.SyncopeAuthenticationDetails; +import org.apache.syncope.core.spring.security.SyncopeGrantedAuthority; +import org.identityconnectors.framework.common.objects.Attribute; +import org.identityconnectors.framework.common.objects.AttributeBuilder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +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 TaskTest extends AbstractTest { + + @Autowired + private TaskDAO taskDAO; + + @Autowired + private ExternalResourceDAO resourceDAO; + + @Test + public 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() { + List tasks = taskDAO.findToExec(TaskType.PROPAGATION); + assertNotNull(tasks); + assertEquals(3, tasks.size()); + } + + @Test + public void findPaginated() { + List tasks = taskDAO.findAll( + TaskType.PROPAGATION, null, null, null, null, PageRequest.of(0, 2)); + assertNotNull(tasks); + assertEquals(2, tasks.size()); + + for (PropagationTask task : tasks) { + assertNotNull(task); + } + + tasks = taskDAO.findAll(TaskType.PROPAGATION, null, null, null, null, PageRequest.of(1, 2)); + assertNotNull(tasks); + assertEquals(2, tasks.size()); + + for (PropagationTask task : tasks) { + assertNotNull(task); + } + + tasks = taskDAO.findAll(TaskType.PROPAGATION, null, null, null, null, PageRequest.of(1000, 2)); + assertNotNull(tasks); + assertTrue(tasks.isEmpty()); + + assertEquals(6, taskDAO.count(TaskType.PROPAGATION, null, null, null, null)); + } + + @Test + public void findAll() { + assertEquals(6, taskDAO.findAll(TaskType.PROPAGATION).size()); + assertEquals(1, taskDAO.findAll(TaskType.NOTIFICATION).size()); + assertEquals(3, taskDAO.findAll(TaskType.SCHEDULED).size()); + assertEquals(10, taskDAO.findAll(TaskType.PULL).size()); + assertEquals(11, taskDAO.findAll(TaskType.PUSH).size()); + + 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); + try { + assertEquals(0, taskDAO.findAll(TaskType.MACRO).size()); + } finally { + SecurityContextHolder.getContext().setAuthentication(null); + } + } + + @Test + public void savePropagationTask() { + ExternalResource resource = resourceDAO.findById("ws-target-resource-1").orElseThrow(); + + PropagationTask task = entityFactory.newEntity(PropagationTask.class); + task.setResource(resource); + task.setAnyTypeKind(AnyTypeKind.USER); + task.setAnyType(AnyTypeKind.USER.name()); + task.setOperation(ResourceOperation.CREATE); + task.setConnObjectKey("one@two.com"); + + Set attributes = new HashSet<>(); + attributes.add(AttributeBuilder.build("testAttribute", "testValue1", "testValue2")); + attributes.add(AttributeBuilder.buildPassword("password".toCharArray())); + task.setPropagationData(new PropagationData(attributes)); + + task = taskDAO.save(task); + assertNotNull(task); + + PropagationTask actual = (PropagationTask) taskDAO.findById(TaskType.PROPAGATION, task.getKey()).orElseThrow(); + assertEquals(task, actual); + } + + @Test + public void delete() { + PropagationTask task = (PropagationTask) taskDAO.findById( + TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); + assertNotNull(task); + + ExternalResource resource = task.getResource(); + assertNotNull(resource); + + taskDAO.delete(task); + + assertTrue(taskDAO.findById(TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").isEmpty()); + + resource = resourceDAO.findById(resource.getKey()).orElseThrow(); + assertFalse(taskDAO.findAll( + TaskType.PROPAGATION, resource, null, null, null, Pageable.unpaged()). + contains(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 new file mode 100644 index 0000000000..c11bddde4e --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/UserTest.java @@ -0,0 +1,285 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.inner; + +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.time.OffsetDateTime; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; +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.RealmDAO; +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.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.user.UMembership; +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrUniqueValue; +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.apache.syncope.core.spring.security.Encryptor; +import org.apache.syncope.core.spring.security.PasswordGenerator; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class UserTest extends AbstractTest { + + @Autowired + private PasswordGenerator passwordGenerator; + + @Autowired + private UserDAO userDAO; + + @Autowired + private RealmDAO realmDAO; + + @Autowired + private ExternalResourceDAO resourceDAO; + + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + @Autowired + private DerSchemaDAO derSchemaDAO; + + @Autowired + private SecurityQuestionDAO securityQuestionDAO; + + @Test + public void find() { + User user = userDAO.findById("823074dc-d280-436d-a7dd-07399fae48ec").orElseThrow(); + assertEquals("puccini", user.getUsername()); + assertFalse(user.isSuspended()); + assertFalse(user.isMustChangePassword()); + assertEquals("active", user.getStatus()); + assertEquals(CipherAlgorithm.SHA1, user.getCipherAlgorithm()); + assertEquals("e4c28e7a-9dbf-4ee7-9441-93812a0d4a28", user.getRealm().getKey()); + assertNull(user.getSecurityQuestion()); + assertNull(user.getSecurityAnswer()); + assertEquals("admin", user.getCreator()); + assertEquals("Giacomo", user.getPlainAttr("firstname").get().getValuesAsStrings().get(0)); + assertEquals("Puccini", user.getPlainAttr("surname").get().getValuesAsStrings().get(0)); + } + + @Test + public void findUsername() { + assertEquals("puccini", userDAO.findUsername("823074dc-d280-436d-a7dd-07399fae48ec").orElseThrow()); + } + + @Test + public void findByToken() { + assertTrue(userDAO.findByToken("WRONG TOKEN").isEmpty()); + } + + @Test + public void findAll() { + List users = userDAO.findAll(); + assertEquals(5, users.size()); + } + + @Test + public void count() { + long count = userDAO.count(); + assertNotNull(count); + assertEquals(5, count); + } + + @Test + public void findByDerAttrValue() { + List list = userDAO.findByDerAttrValue( + derSchemaDAO.findById("cn").orElseThrow(), "Vivaldi, Antonio", false); + assertEquals(1, list.size()); + + list = userDAO.findByDerAttrValue( + derSchemaDAO.findById("cn").orElseThrow(), "VIVALDI, ANTONIO", false); + assertEquals(0, list.size()); + + list = userDAO.findByDerAttrValue( + derSchemaDAO.findById("cn").orElseThrow(), "VIVALDI, ANTONIO", true); + assertEquals(1, list.size()); + } + + @Test + public void findByInvalidDerAttrValue() { + assertTrue(userDAO.findByDerAttrValue( + derSchemaDAO.findById("cn").orElseThrow(), "Antonio, Maria, Rossi", false).isEmpty()); + } + + @Test + public void findByInvalidDerAttrExpression() { + assertTrue(userDAO.findByDerAttrValue( + derSchemaDAO.findById("noschema").orElseThrow(), "Antonio, Maria", false).isEmpty()); + } + + @Test + public void findByPlainAttrUniqueValue() { + UPlainAttrUniqueValue fullnameValue = entityFactory.newEntity(UPlainAttrUniqueValue.class); + fullnameValue.setStringValue("Gioacchino Rossini"); + + PlainSchema fullname = plainSchemaDAO.findById("fullname").orElseThrow(); + + Optional found = userDAO.findByPlainAttrUniqueValue(fullname, fullnameValue, false); + assertTrue(found.isPresent()); + + fullnameValue.setStringValue("Gioacchino ROSSINI"); + + found = userDAO.findByPlainAttrUniqueValue(fullname, fullnameValue, false); + assertFalse(found.isPresent()); + + found = userDAO.findByPlainAttrUniqueValue(fullname, fullnameValue, true); + assertTrue(found.isPresent()); + } + + @Test + public void findByPlainAttrBooleanValue() { + UPlainAttrValue coolValue = entityFactory.newEntity(UPlainAttrValue.class); + coolValue.setBooleanValue(true); + + List list = userDAO.findByPlainAttrValue( + plainSchemaDAO.findById("cool").orElseThrow(), coolValue, false); + assertEquals(1, list.size()); + } + + @Test + public void findByKey() { + assertTrue(userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").isPresent()); + } + + @Test + public void findByUsername() { + assertTrue(userDAO.findByUsername("rossini").isPresent()); + assertTrue(userDAO.findByUsername("vivaldi").isPresent()); + assertTrue(userDAO.findByUsername("user6").isEmpty()); + } + + @Test + public void findMembership() { + UMembership memb = userDAO.findMembership("3d5e91f6-305e-45f9-ad30-4897d3d43bd9"); + assertNotNull(memb); + assertEquals("1417acbe-cbf6-4277-9372-e75e04f97000", memb.getLeftEnd().getKey()); + } + + @Test + public void save() { + User user = entityFactory.newEntity(User.class); + user.setUsername("username"); + user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.setCreator("admin"); + user.setCreationDate(OffsetDateTime.now()); + user.setCipherAlgorithm(CipherAlgorithm.SHA256); + user.setPassword("password123"); + + User actual = userDAO.save(user); + assertNotNull(actual); + } + + @Test + public void delete() { + User user = userDAO.findById("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee").orElseThrow(); + + userDAO.deleteById(user.getKey()); + + assertTrue(userDAO.findById("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee").isEmpty()); + } + + @Test + public void issue237() { + User user = entityFactory.newEntity(User.class); + user.setUsername("username"); + user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.setCreator("admin"); + user.setCreationDate(OffsetDateTime.now()); + + user.setCipherAlgorithm(CipherAlgorithm.AES); + user.setPassword("password123"); + + User actual = userDAO.save(user); + assertNotNull(actual); + } + + @Test + public void issueSYNCOPE391() { + User user = entityFactory.newEntity(User.class); + user.setUsername("username"); + user.setCipherAlgorithm(CipherAlgorithm.AES); + user.setPassword(null); + user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + + User actual = userDAO.save(user); + assertNull(user.getPassword()); + assertNotNull(actual); + } + + @Test + public void testPasswordGenerator() { + String password = passwordGenerator.generate( + resourceDAO.findById("ws-target-resource-nopropagation").orElseThrow(), + List.of(realmDAO.getRoot())); + assertNotNull(password); + + User user = userDAO.findById("c9b2dec2-00a7-4855-97c0-d854842b4b24").orElseThrow(); + user.setPassword(password); + userDAO.save(user); + } + + @Test + public void passwordGeneratorFailing() { + assertThrows(IllegalArgumentException.class, () -> { + String password = passwordGenerator.generate( + resourceDAO.findById("ws-target-resource-nopropagation").orElseThrow(), + List.of(realmDAO.getRoot())); + assertNotNull(password); + + User user = userDAO.findById("c9b2dec2-00a7-4855-97c0-d854842b4b24").orElseThrow(); + // SYNCOPE-1666 fail because cipherAlgorithm is already set + user.setCipherAlgorithm(CipherAlgorithm.SHA); + user.setPassword(password); + userDAO.save(user); + }); + } + + @Test + public void issueSYNCOPE1666() { + User user = entityFactory.newEntity(User.class); + user.setUsername("username"); + user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.setCreator("admin"); + user.setCreationDate(OffsetDateTime.now()); + user.setCipherAlgorithm(CipherAlgorithm.SSHA256); + user.setPassword("password123"); + user.setSecurityQuestion(securityQuestionDAO.findById("887028ea-66fc-41e7-b397-620d7ea6dfbb").orElseThrow()); + String securityAnswer = "my complex answer to @ $complex question è ? £12345"; + user.setSecurityAnswer(securityAnswer); + + User actual = userDAO.save(user); + assertNotNull(actual); + assertNotNull(actual.getSecurityAnswer()); + assertTrue(Encryptor.getInstance().verify(securityAnswer, CipherAlgorithm.SSHA256, actual.getSecurityAnswer())); + } +} diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/VirSchemaTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/VirSchemaTest.java new file mode 100644 index 0000000000..4a5353e339 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/VirSchemaTest.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.persistence.neo4j.inner; + +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.junit.jupiter.api.Assertions.fail; + +import java.util.List; +import org.apache.syncope.common.lib.types.EntityViolationType; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO; +import org.apache.syncope.core.persistence.api.entity.VirSchema; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class VirSchemaTest extends AbstractTest { + + @Autowired + private VirSchemaDAO virSchemaDAO; + + @Autowired + private ExternalResourceDAO resourceDAO; + + @Autowired + private AnyTypeDAO anyTypeDAO; + + @Test + public void findAll() { + List list = virSchemaDAO.findAll(); + assertEquals(3, list.size()); + } + + @Test + public void findByIdLike() { + List schemas = virSchemaDAO.findByIdLike("rvirtuald*"); + assertEquals(1, schemas.size()); + } + + @Test + public void findByName() { + VirSchema attributeSchema = virSchemaDAO.findById("virtualdata").orElseThrow(); + assertNotNull(attributeSchema); + } + + @Test + public void save() { + VirSchema virSchema = entityFactory.newEntity(VirSchema.class); + virSchema.setKey("virtual"); + virSchema.setResource(resourceDAO.findById("resource-csv").orElseThrow()); + virSchema.setAnyType(anyTypeDAO.getUser()); + virSchema.setReadonly(true); + virSchema.setExtAttrName("EXT_ATTR"); + + virSchemaDAO.save(virSchema); + + VirSchema actual = virSchemaDAO.findById("virtual").orElseThrow(); + assertNotNull(actual); + assertTrue(actual.isReadonly()); + assertEquals("EXT_ATTR", actual.getExtAttrName()); + } + + @Test + public void delete() { + VirSchema virtualdata = virSchemaDAO.findById("virtualdata").orElseThrow(); + + virSchemaDAO.deleteById(virtualdata.getKey()); + + assertTrue(virSchemaDAO.findById("virtualdata").isEmpty()); + + // ------------- // + VirSchema rvirtualdata = virSchemaDAO.findById("rvirtualdata").orElseThrow(); + + virSchemaDAO.deleteById(rvirtualdata.getKey()); + + assertTrue(virSchemaDAO.findById("rvirtualdata").isEmpty()); + } + + @Test + public void issueSYNCOPE418() { + VirSchema schema = entityFactory.newEntity(VirSchema.class); + schema.setKey("http://schemas.examples.org/security/authorization/organizationUnit"); + + try { + virSchemaDAO.save(schema); + fail("This should not happen"); + } catch (InvalidEntityException e) { + assertTrue(e.hasViolation(EntityViolationType.InvalidKey)); + } + } +} 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 new file mode 100644 index 0000000000..377b01a299 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/WAConfigTest.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.core.persistence.neo4j.inner; + +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.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.apache.syncope.core.persistence.api.dao.WAConfigDAO; +import org.apache.syncope.core.persistence.api.entity.am.WAConfigEntry; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.apache.syncope.core.persistence.neo4j.entity.am.Neo4jWAConfigEntry; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class WAConfigTest extends AbstractTest { + + @Autowired + private WAConfigDAO configDAO; + + @Autowired + private Neo4jTemplate neo4jTemplate; + + @BeforeEach + public void beforeEach() { + neo4jTemplate.deleteAll(Neo4jWAConfigEntry.class); + } + + private WAConfigEntry create(final String name, final List value) { + WAConfigEntry entry = entityFactory.newEntity(WAConfigEntry.class); + entry.setKey(name); + entry.setValues(value); + entry = configDAO.save(entry); + assertNotNull(entry); + assertNotNull(entry.getKey()); + assertTrue(configDAO.findById(entry.getKey()).isPresent()); + return entry; + } + + @Test + public void saveCommaSeparatedValueStrings() { + create("system.example.key[0]", Arrays.asList("value1", "value2", "value3")); + assertFalse(configDAO.findAll().isEmpty()); + } + + @Test + public void saveNumbers() { + create("system.example.key[0]", List.of("1984")); + assertFalse(configDAO.findAll().isEmpty()); + } + + @Test + public 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() { + create("system.example.key[0].key1", List.of("value1")); + assertFalse(configDAO.findAll().isEmpty()); + } + + @Test + public void update() { + WAConfigEntry entry = create("system.syncope.key[0]", Arrays.asList("1", "2", "3", "4")); + assertNotNull(entry); + entry.setValues(List.of("v1")); + + entry = configDAO.save(entry); + assertNotNull(entry); + assertNotNull(entry.getKey()); + WAConfigEntry found = configDAO.findById(entry.getKey()).orElseThrow(); + assertEquals(List.of("v1"), found.getValues()); + } +} 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 new file mode 100644 index 0000000000..802c04844f --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnySearchTest.java @@ -0,0 +1,253 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.outer; + +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.ArrayList; +import java.util.List; +import java.util.Set; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +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.RealmDAO; +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.AnyCond; +import org.apache.syncope.core.persistence.api.dao.search.AttrCond; +import org.apache.syncope.core.persistence.api.dao.search.RoleCond; +import org.apache.syncope.core.persistence.api.dao.search.SearchCond; +import org.apache.syncope.core.persistence.api.entity.Role; +import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr; +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.UPlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class AnySearchTest extends AbstractTest { + + @Autowired + private UserDAO userDAO; + + @Autowired + private GroupDAO groupDAO; + + @Autowired + private AnySearchDAO searchDAO; + + @Autowired + private RealmDAO realmDAO; + + @Autowired + private RoleDAO roleDAO; + + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + @Autowired + private PlainAttrValidationManager validator; + + @Test + public void searchByDynMembership() { + // 1. create role with dynamic membership + Role role = entityFactory.newEntity(Role.class); + role.setKey("new"); + role.add(realmDAO.getRoot()); + role.add(realmDAO.findByFullPath("/even/two").orElseThrow()); + role.getEntitlements().add(IdRepoEntitlement.AUDIT_LIST); + role.getEntitlements().add(IdRepoEntitlement.AUDIT_SET); + role.setDynMembershipCond("cool==true"); + + role = roleDAO.saveAndRefreshDynMemberships(role); + assertNotNull(role); + + // 2. search user by this dynamic role + RoleCond roleCond = new RoleCond(); + roleCond.setRole(role.getKey()); + + List users = searchDAO.search(SearchCond.getLeaf(roleCond), AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + assertEquals("c9b2dec2-00a7-4855-97c0-d854842b4b24", users.get(0).getKey()); + } + + @Test + public void searchAsGroupOwner() { + // 1. define rossini as member of director + User rossini = userDAO.findByUsername("rossini").orElseThrow(); + + Group group = groupDAO.findByName("director").orElseThrow(); + + UMembership membership = entityFactory.newEntity(UMembership.class); + membership.setLeftEnd(rossini); + membership.setRightEnd(group); + rossini.add(membership); + + userDAO.save(rossini); + assertNotNull(rossini); + + // 2. search all users with root realm entitlements: all users are returned, including rossini + AnyCond anyCond = new AnyCond(AttrCond.Type.ISNOTNULL); + anyCond.setSchema("id"); + + List users = searchDAO.search( + realmDAO.getRoot(), true, + Set.of(SyncopeConstants.ROOT_REALM), + SearchCond.getLeaf(anyCond), PageRequest.of(0, 100), AnyTypeKind.USER); + assertNotNull(users); + assertTrue(users.stream().anyMatch(user -> rossini.getKey().equals(user.getKey()))); + + // 3. search all users with director owner's entitlements: only rossini is returned + users = searchDAO.search( + group.getRealm(), true, + Set.of(RealmUtils.getGroupOwnerRealm(group.getRealm().getFullPath(), group.getKey())), + SearchCond.getLeaf(anyCond), PageRequest.of(0, 100), AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + assertEquals(rossini.getKey(), users.get(0).getKey()); + } + + @Test + public void issueSYNCOPE95() { + groupDAO.findAll().forEach(group -> groupDAO.deleteById(group.getKey())); + + AttrCond coolLeafCond = new AttrCond(AttrCond.Type.EQ); + coolLeafCond.setSchema("cool"); + coolLeafCond.setExpression("true"); + + SearchCond cond = SearchCond.getLeaf(coolLeafCond); + assertTrue(cond.isValid()); + + List users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + + assertEquals("c9b2dec2-00a7-4855-97c0-d854842b4b24", users.get(0).getKey()); + } + + @Test + public void issueSYNCOPE1417() { + AnyCond usernameLeafCond = new AnyCond(AnyCond.Type.EQ); + usernameLeafCond.setSchema("username"); + usernameLeafCond.setExpression("rossini"); + AttrCond idRightCond = new AttrCond(AttrCond.Type.LIKE); + idRightCond.setSchema("fullname"); + idRightCond.setExpression("Giuseppe V%"); + SearchCond searchCondition = SearchCond.getOr( + SearchCond.getLeaf(usernameLeafCond), SearchCond.getLeaf(idRightCond)); + + List orderByClauses = new ArrayList<>(); + orderByClauses.add(new Sort.Order(Sort.Direction.DESC, "surname")); + orderByClauses.add(new Sort.Order(Sort.Direction.ASC, "firstname")); + + List result = searchDAO.search(searchCondition, orderByClauses, AnyTypeKind.USER); + assertEquals(2, result.size()); + assertTrue(result.stream().anyMatch(u -> "rossini".equals(u.getUsername()))); + assertTrue(result.stream().anyMatch(u -> "verdi".equals(u.getUsername()))); + } + + @Test + public void issueSYNCOPE1512() { + Group group = groupDAO.findByName("root").orElseThrow(); + + // non unique + GPlainAttr title = entityFactory.newEntity(GPlainAttr.class); + title.setOwner(group); + title.setSchema(plainSchemaDAO.findById("title").orElseThrow()); + title.add(validator, "syncope's group", anyUtilsFactory.getInstance(AnyTypeKind.GROUP)); + group.add(title); + + // unique + GPlainAttr originalName = entityFactory.newEntity(GPlainAttr.class); + originalName.setOwner(group); + originalName.setSchema(plainSchemaDAO.findById("originalName").orElseThrow()); + originalName.add(validator, "syncope's group", anyUtilsFactory.getInstance(AnyTypeKind.GROUP)); + group.add(originalName); + + groupDAO.save(group); + + AttrCond titleCond = new AttrCond(AttrCond.Type.EQ); + titleCond.setSchema("title"); + titleCond.setExpression("syncope's group"); + + List matching = searchDAO.search(SearchCond.getLeaf(titleCond), AnyTypeKind.GROUP); + assertEquals(1, matching.size()); + assertEquals(group.getKey(), matching.get(0).getKey()); + + AttrCond originalNameCond = new AttrCond(AttrCond.Type.EQ); + originalNameCond.setSchema("originalName"); + originalNameCond.setExpression("syncope's group"); + + matching = searchDAO.search(SearchCond.getLeaf(originalNameCond), AnyTypeKind.GROUP); + assertEquals(1, matching.size()); + assertEquals(group.getKey(), matching.get(0).getKey()); + } + + @Test + public void issueSYNCOPE1790() { + // 0. search by email + AttrCond emailCond = new AttrCond(AttrCond.Type.EQ); + emailCond.setSchema("email"); + emailCond.setExpression("verdi@syncope.org"); + + SearchCond cond = SearchCond.getLeaf(emailCond); + assertTrue(cond.isValid()); + + List users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + assertEquals("verdi", users.get(0).getUsername()); + + // 1. set rossini's email address for conditions as per SYNCOPE-1790 + User rossini = userDAO.findByUsername("rossini").orElseThrow(); + + UPlainAttr mail = entityFactory.newEntity(UPlainAttr.class); + mail.setOwner(rossini); + mail.setSchema(plainSchemaDAO.findById("email").orElseThrow()); + mail.add(validator, "bisverdi@syncope.org", anyUtilsFactory.getInstance(AnyTypeKind.USER)); + rossini.add(mail); + + userDAO.save(rossini); + + rossini = userDAO.findByUsername("rossini").orElseThrow(); + assertEquals( + "bisverdi@syncope.org", + rossini.getPlainAttr("email").map(a -> a.getValuesAsStrings().get(0)).orElseThrow()); + + // 2. search again + users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + assertEquals("verdi", users.get(0).getUsername()); + } +} 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 new file mode 100644 index 0000000000..2828dbd900 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnyTypeClassTest.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.persistence.neo4j.outer; + +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 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.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class AnyTypeClassTest extends AbstractTest { + + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + @Autowired + private AnyTypeClassDAO anyTypeClassDAO; + + @Test + public void create() { + PlainSchema newSchema = entityFactory.newEntity(PlainSchema.class); + newSchema.setKey("new_plain_schema"); + newSchema.setType(AttrSchemaType.String); + + plainSchemaDAO.save(newSchema); + + newSchema = plainSchemaDAO.findById(newSchema.getKey()).orElseThrow(); + + AnyTypeClass newClass = entityFactory.newEntity(AnyTypeClass.class); + newClass.setKey("new class"); + newClass.add(newSchema); + + anyTypeClassDAO.save(newClass); + + newClass = anyTypeClassDAO.findById(newClass.getKey()).orElseThrow(); + assertNotNull(newClass); + assertEquals(1, newClass.getPlainSchemas().size()); + assertEquals(newSchema, newClass.getPlainSchemas().get(0)); + assertEquals(newClass, newClass.getPlainSchemas().get(0).getAnyTypeClass()); + + assertTrue(plainSchemaDAO.findById(newSchema.getKey()).isPresent()); + } + + @Test + public void delete() { + AnyTypeClass minimalUser = anyTypeClassDAO.findById("minimal user").orElseThrow(); + + PlainSchema surname = plainSchemaDAO.findById("surname").orElseThrow(); + assertTrue(minimalUser.getPlainSchemas().contains(surname)); + int before = minimalUser.getPlainSchemas().size(); + + plainSchemaDAO.deleteById("surname"); + + minimalUser = anyTypeClassDAO.findById("minimal user").orElseThrow(); + assertEquals(before, minimalUser.getPlainSchemas().size() + 1); + } +} 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 new file mode 100644 index 0000000000..1b86adcddc --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnyTypeTest.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.persistence.neo4j.outer; + +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 org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.entity.AnyType; +import org.apache.syncope.core.persistence.api.entity.AnyTypeClass; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class AnyTypeTest extends AbstractTest { + + @Autowired + private AnyTypeDAO anyTypeDAO; + + @Autowired + private AnyTypeClassDAO anyTypeClassDAO; + + @Test + public void findByClassesContaining() { + List found = anyTypeDAO.findByClassesContaining( + anyTypeClassDAO.findById("minimal user").orElseThrow()); + assertEquals(List.of(anyTypeDAO.getUser()), found); + + found = anyTypeDAO.findByClassesContaining(anyTypeClassDAO.findById("other").orElseThrow()); + assertEquals(List.of(anyTypeDAO.getUser()), found); + + found = anyTypeDAO.findByClassesContaining(anyTypeClassDAO.findById("minimal group").orElseThrow()); + assertEquals(List.of(anyTypeDAO.getGroup()), found); + + found = anyTypeDAO.findByClassesContaining(anyTypeClassDAO.findById("minimal printer").orElseThrow()); + assertEquals(List.of(anyTypeDAO.findById("PRINTER").orElseThrow()), found); + } + + @Test + public void manyToMany() { + AnyTypeClass other = anyTypeClassDAO.findById("other").orElseThrow(); + + AnyType user = anyTypeDAO.getUser(); + assertTrue(user.getClasses().contains(other)); + + AnyType group = anyTypeDAO.getGroup(); + assertFalse(group.getClasses().contains(other)); + + group.add(other); + anyTypeDAO.save(group); + + user = anyTypeDAO.getUser(); + assertTrue(user.getClasses().contains(other)); + int userClassesBefore = user.getClasses().size(); + + group = anyTypeDAO.getGroup(); + assertTrue(group.getClasses().contains(other)); + int groupClassesBefore = group.getClasses().size(); + + anyTypeClassDAO.deleteById("other"); + + user = anyTypeDAO.getUser(); + assertEquals(userClassesBefore, user.getClasses().size() + 1); + + group = anyTypeDAO.getGroup(); + assertEquals(groupClassesBefore, group.getClasses().size() + 1); + } +} 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 new file mode 100644 index 0000000000..ac619c3589 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ConnInstanceTest.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.persistence.neo4j.outer; + +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.common.lib.types.ConnectorCapability; +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.entity.ConnInstance; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class ConnInstanceTest extends AbstractTest { + + @Autowired + private ExternalResourceDAO resourceDAO; + + @Autowired + private ConnInstanceDAO connInstanceDAO; + + @Test + public void deleteCascade() { + ConnInstance connInstance = connInstanceDAO.findById("fcf9f2b0-f7d6-42c9-84a6-61b28255a42b").orElseThrow(); + + List resources = connInstance.getResources(); + assertNotNull(resources); + assertFalse(resources.isEmpty()); + + connInstanceDAO.deleteById(connInstance.getKey()); + + assertTrue(connInstanceDAO.findById("fcf9f2b0-f7d6-42c9-84a6-61b28255a42b").isEmpty()); + + for (ExternalResource resource : resources) { + assertTrue(resourceDAO.findById(resource.getKey()).isEmpty()); + } + } + + @Test + public void issue176() { + ConnInstance connInstance = connInstanceDAO.findById("fcf9f2b0-f7d6-42c9-84a6-61b28255a42b").orElseThrow(); + assertTrue(connInstance.getCapabilities().isEmpty()); + + List resources = connInstance.getResources(); + assertNotNull(resources); + assertEquals(4, resources.size()); + assertTrue( + "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(0).getKey()) + || "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(1).getKey()) + || "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(2).getKey()) + || "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(3).getKey())); + + connInstance.getCapabilities().add(ConnectorCapability.SEARCH); + + connInstance = connInstanceDAO.save(connInstance); + assertNotNull(connInstance); + assertFalse(connInstance.getCapabilities().isEmpty()); + + resources = connInstance.getResources(); + assertNotNull(resources); + assertEquals(4, resources.size()); + assertTrue( + "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(0).getKey()) + || "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(1).getKey()) + || "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(2).getKey()) + || "ws-target-resource-nopropagation".equalsIgnoreCase(resources.get(3).getKey())); + } +} 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 new file mode 100644 index 0000000000..c6ebbabf54 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/DelegationTest.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.persistence.neo4j.outer; + +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.core.persistence.api.dao.DelegationDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.Delegation; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class DelegationTest extends AbstractTest { + + @Autowired + private DelegationDAO delegationDAO; + + @Autowired + private UserDAO userDAO; + + @Test + public void findValidFor() { + Delegation delegation = entityFactory.newEntity(Delegation.class); + delegation.setDelegating(userDAO.findByUsername("bellini").orElseThrow()); + delegation.setDelegated(userDAO.findByUsername("rossini").orElseThrow()); + delegation.setStart(OffsetDateTime.now()); + delegation = delegationDAO.save(delegation); + + List delegating = delegationDAO.findValidDelegating( + userDAO.findKey("rossini").orElseThrow(), OffsetDateTime.now()); + assertEquals(List.of("bellini"), delegating); + + String valid = delegationDAO.findValidFor( + userDAO.findKey("bellini").orElseThrow(), + userDAO.findKey("rossini").orElseThrow(), + OffsetDateTime.now()).orElseThrow(); + assertEquals(delegation.getKey(), valid); + + assertTrue(delegationDAO.findByDelegating(userDAO.findByUsername("bellini").orElseThrow()). + contains(delegation)); + assertTrue(delegationDAO.findByDelegated(userDAO.findByUsername("rossini").orElseThrow()). + contains(delegation)); + } +} diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/DynRealmTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/DynRealmTest.java new file mode 100644 index 0000000000..3f5608891c --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/DynRealmTest.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.persistence.neo4j.outer; + +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.junit.jupiter.api.Assertions.fail; + +import java.util.List; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO; +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.DynRealmDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond; +import org.apache.syncope.core.persistence.api.dao.search.SearchCond; +import org.apache.syncope.core.persistence.api.entity.DynRealm; +import org.apache.syncope.core.persistence.api.entity.DynRealmMembership; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class DynRealmTest extends AbstractTest { + + @Autowired + private AnyMatchDAO anyMatchDAO; + + @Autowired + private AnyTypeDAO anyTypeDAO; + + @Autowired + private DynRealmDAO dynRealmDAO; + + @Autowired + private AnySearchDAO searchDAO; + + @Autowired + private UserDAO userDAO; + + @Test + public void misc() { + DynRealm dynRealm = entityFactory.newEntity(DynRealm.class); + dynRealm.setKey("/name"); + + DynRealmMembership memb = entityFactory.newEntity(DynRealmMembership.class); + memb.setDynRealm(dynRealm); + memb.setAnyType(anyTypeDAO.getUser()); + memb.setFIQLCond("cool==true"); + + dynRealm.add(memb); + memb.setDynRealm(dynRealm); + + // invalid key (starts with /) + try { + dynRealmDAO.save(dynRealm); + fail("This should not happen"); + } catch (Exception e) { + assertNotNull(e); + } + + dynRealm.setKey("name"); + DynRealm actual = dynRealmDAO.saveAndRefreshDynMemberships(dynRealm); + assertNotNull(actual); + + DynRealmCond dynRealmCond = new DynRealmCond(); + dynRealmCond.setDynRealm(actual.getKey()); + List matching = searchDAO.search(SearchCond.getLeaf(dynRealmCond), AnyTypeKind.USER); + assertNotNull(matching); + assertFalse(matching.isEmpty()); + User user = matching.get(0); + assertTrue(anyMatchDAO.matches(user, SearchCond.getLeaf(dynRealmCond))); + + assertTrue(userDAO.findDynRealms(user.getKey()).contains(actual.getKey())); + } +} 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 new file mode 100644 index 0000000000..e5173c5ac8 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/FIQLQueryTest.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.persistence.neo4j.outer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.apache.syncope.core.persistence.api.dao.FIQLQueryDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.entity.FIQLQuery; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class FIQLQueryTest extends AbstractTest { + + @Autowired + private FIQLQueryDAO fiqlQueryDAO; + + @Autowired + private UserDAO userDAO; + + @Test + public void findByOwner() { + FIQLQuery query = entityFactory.newEntity(FIQLQuery.class); + query.setOwner(userDAO.findByUsername("rossini").orElseThrow()); + query.setName("name"); + query.setFIQL("id!=$null"); + query.setTarget("target"); + query = fiqlQueryDAO.save(query); + + FIQLQuery fiqlQuery = entityFactory.newEntity(FIQLQuery.class); + fiqlQuery.setOwner(userDAO.findByUsername("rossini").orElseThrow()); + + assertEquals( + List.of(query), + fiqlQueryDAO.findByOwner(userDAO.findByUsername("rossini").orElseThrow(), null)); + assertEquals( + List.of(query), + fiqlQueryDAO.findByOwner(userDAO.findByUsername("rossini").orElseThrow(), "target")); + } +} 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 new file mode 100644 index 0000000000..036a881a03 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/GroupTest.java @@ -0,0 +1,383 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.outer; + +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.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +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.PlainSchemaDAO; +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.anyobject.ADynGroupMembership; +import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttr; +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.persistence.api.entity.group.GPlainAttr; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.group.TypeExtension; +import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership; +import org.apache.syncope.core.persistence.api.entity.user.UMembership; +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jADynGroupMembership; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jUDynGroupMembership; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class GroupTest extends AbstractTest { + + @Autowired + private AnyTypeDAO anyTypeDAO; + + @Autowired + private AnyObjectDAO anyObjectDAO; + + @Autowired + private UserDAO userDAO; + + @Autowired + private GroupDAO groupDAO; + + @Autowired + private RealmDAO realmDAO; + + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + @Autowired + private AnyTypeClassDAO anyTypeClassDAO; + + @Autowired + private ExternalResourceDAO resourceDAO; + + @Autowired + private PlainAttrValidationManager validator; + + @Autowired + private Neo4jTemplate neo4jTemplate; + + @Test + public 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())); + assertTrue(found.contains(groupDAO.findById("ba9ed509-b1f5-48ab-a334-c8530a6422dc").orElseThrow())); + + found = groupDAO.findByResourcesContaining(resourceDAO.findById("resource-testdb2").orElseThrow()); + assertTrue(found.isEmpty()); + } + + @Test + public void uMemberships() { + List memberships = groupDAO.findUMemberships( + groupDAO.findById("37d15e4c-cdc1-460b-a591-8505c8133806").orElseThrow()); + assertEquals(2, memberships.size()); + assertTrue(memberships.stream().anyMatch(m -> "3d5e91f6-305e-45f9-ad30-4897d3d43bd9".equals(m.getKey()))); + assertTrue(memberships.stream().anyMatch(m -> "d53f7657-2b22-4e10-a2cd-c3379a4d1a31".equals(m.getKey()))); + + assertEquals( + memberships.stream().map(m -> m.getLeftEnd().getKey()).collect(Collectors.toSet()), + groupDAO.findUMembers("37d15e4c-cdc1-460b-a591-8505c8133806").stream().collect(Collectors.toSet())); + + assertTrue(groupDAO.existsUMembership( + "74cd8ece-715a-44a4-a736-e17b46c4e7e6", "37d15e4c-cdc1-460b-a591-8505c8133806")); + assertFalse(groupDAO.existsUMembership( + "74cd8ece-715a-44a4-a736-e17b46c4e7e6", "ece66293-8f31-4a84-8e8d-23da36e70846")); + assertFalse(groupDAO.existsUMembership( + "notfound", "ece66293-8f31-4a84-8e8d-23da36e70846")); + assertFalse(groupDAO.existsUMembership( + "74cd8ece-715a-44a4-a736-e17b46c4e7e6", "notfound")); + + assertEquals(2, groupDAO.countUMembers("37d15e4c-cdc1-460b-a591-8505c8133806")); + } + + @Test + public void saveWithTwoOwners() { + assertThrows(InvalidEntityException.class, () -> { + Group root = groupDAO.findByName("root").orElseThrow(); + + User user = userDAO.findByUsername("rossini").orElseThrow(); + + Group group = entityFactory.newEntity(Group.class); + group.setRealm(realmDAO.getRoot()); + group.setName("error"); + group.setUserOwner(user); + group.setGroupOwner(root); + + groupDAO.save(group); + }); + } + + @Test + public void findOwnedByUser() { + Group group = groupDAO.findById("ebf97068-aa4b-4a85-9f01-680e8c4cf227").orElseThrow(); + + User user = userDAO.findById("823074dc-d280-436d-a7dd-07399fae48ec").orElseThrow(); + + assertEquals(user, group.getUserOwner()); + + List ownedGroups = groupDAO.findOwnedByUser(user.getKey()); + assertFalse(ownedGroups.isEmpty()); + assertEquals(1, ownedGroups.size()); + assertTrue(ownedGroups.contains(group)); + } + + @Test + public void findOwnedByGroup() { + Group root = groupDAO.findByName("root").orElseThrow(); + Group group = entityFactory.newEntity(Group.class); + group.setRealm(realmDAO.getRoot()); + group.setName("error"); + group.setGroupOwner(root); + group = groupDAO.save(group); + + List owned = groupDAO.findOwnedByGroup(root.getKey()); + assertEquals(List.of(group), owned); + } + + @Test + public void create() { + Group group = entityFactory.newEntity(Group.class); + group.setRealm(realmDAO.getRoot()); + group.setName("new"); + + TypeExtension typeExt = entityFactory.newEntity(TypeExtension.class); + typeExt.setAnyType(anyTypeDAO.getUser()); + typeExt.add(anyTypeClassDAO.findById("csv").orElseThrow()); + typeExt.add(anyTypeClassDAO.findById("other").orElseThrow()); + + group.add(typeExt); + typeExt.setGroup(group); + + groupDAO.save(group); + + group = groupDAO.findByName("new").orElseThrow(); + assertEquals(1, group.getTypeExtensions().size()); + assertEquals(2, group.getTypeExtension(anyTypeDAO.getUser()).get().getAuxClasses().size()); + } + + @Test + public void createWithInternationalCharacters() { + Group group = entityFactory.newEntity(Group.class); + group.setName("räksmörgås"); + group.setRealm(realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); + + groupDAO.save(group); + } + + @Test + public 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(); + + groupDAO.deleteById("b1f7c12d-ec83-441f-a50e-1691daaedf3b"); + + assertTrue(groupDAO.findById("b1f7c12d-ec83-441f-a50e-1691daaedf3b").isEmpty()); + assertEquals(before - 1, userDAO.findAllGroups(userDAO.findByUsername("verdi").orElseThrow()).size()); + assertTrue(plainSchemaDAO.findById("icon").isPresent()); + } + + @Test + public void udynMembership() { + // 0. create user matching the condition below + User user = entityFactory.newEntity(User.class); + user.setUsername("username"); + user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.add(anyTypeClassDAO.findById("other").orElseThrow()); + + UPlainAttr attr = entityFactory.newEntity(UPlainAttr.class); + attr.setOwner(user); + attr.setSchema(plainSchemaDAO.findById("cool").orElseThrow()); + attr.add(validator, "true", anyUtilsFactory.getInstance(AnyTypeKind.USER)); + user.add(attr); + + user = userDAO.save(user); + String newUserKey = user.getKey(); + assertNotNull(newUserKey); + + // 1. create group with dynamic membership + Group group = entityFactory.newEntity(Group.class); + group.setRealm(realmDAO.getRoot()); + group.setName("new"); + + UDynGroupMembership dynMembership = entityFactory.newEntity(UDynGroupMembership.class); + dynMembership.setFIQLCond("cool==true"); + dynMembership.setGroup(group); + + group.setUDynMembership(dynMembership); + + Group actual = groupDAO.saveAndRefreshDynMemberships(group); + assertNotNull(actual); + + // 2. verify that dynamic membership is there + actual = groupDAO.findById(actual.getKey()).orElseThrow(); + assertNotNull(actual.getUDynMembership()); + assertNotNull(actual.getUDynMembership().getKey()); + assertEquals(actual, actual.getUDynMembership().getGroup()); + + // 3. verify that expected users have the created group dynamically assigned + List members = groupDAO.findUDynMembers(actual); + assertEquals(2, members.size()); + assertEquals(Set.of("c9b2dec2-00a7-4855-97c0-d854842b4b24", newUserKey), new HashSet<>(members)); + + user = userDAO.findByUsername("bellini").orElseThrow(); + List dynGroupMemberships = userDAO.findDynGroups(user.getKey()); + assertEquals(1, dynGroupMemberships.size()); + assertTrue(dynGroupMemberships.contains(actual.getUDynMembership().getGroup())); + + // 4. delete the new user and verify that dynamic membership was updated + userDAO.deleteById(newUserKey); + + actual = groupDAO.findById(actual.getKey()).orElseThrow(); + members = groupDAO.findUDynMembers(actual); + assertEquals(1, members.size()); + assertEquals("c9b2dec2-00a7-4855-97c0-d854842b4b24", members.get(0)); + + // 5. delete group and verify that dynamic membership was also removed + String dynMembershipKey = actual.getUDynMembership().getKey(); + + groupDAO.delete(actual); + + assertTrue(neo4jTemplate.findById(dynMembershipKey, Neo4jUDynGroupMembership.class).isEmpty()); + + dynGroupMemberships = userDAO.findDynGroups(user.getKey()); + assertTrue(dynGroupMemberships.isEmpty()); + } + + @Test + public void adynMembership() { + // 0. create any object matching the condition below + AnyObject anyObject = entityFactory.newEntity(AnyObject.class); + anyObject.setName("name"); + anyObject.setType(anyTypeDAO.findById("PRINTER").orElseThrow()); + anyObject.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + + APlainAttr attr = entityFactory.newEntity(APlainAttr.class); + attr.setOwner(anyObject); + attr.setSchema(plainSchemaDAO.findById("model").orElseThrow()); + attr.add(validator, "Canon MFC8030", anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT)); + anyObject.add(attr); + + anyObject = anyObjectDAO.save(anyObject); + String newAnyObjectKey = anyObject.getKey(); + assertNotNull(newAnyObjectKey); + + // 1. create group with dynamic membership + Group group = entityFactory.newEntity(Group.class); + group.setRealm(realmDAO.getRoot()); + group.setName("new"); + + ADynGroupMembership dynMembership = entityFactory.newEntity(ADynGroupMembership.class); + dynMembership.setAnyType(anyTypeDAO.findById("PRINTER").orElseThrow()); + dynMembership.setFIQLCond("model==Canon MFC8030"); + dynMembership.setGroup(group); + + group.add(dynMembership); + + Group actual = groupDAO.saveAndRefreshDynMemberships(group); + assertNotNull(actual); + + // 2. verify that dynamic membership is there + actual = groupDAO.findById(actual.getKey()).orElseThrow(); + assertNotNull(actual.getADynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()).get()); + assertNotNull(actual.getADynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()).get().getKey()); + assertEquals(actual, actual.getADynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()).get().getGroup()); + + // 3. verify that expected any objects have the created group dynamically assigned + List members = groupDAO.findADynMembers(actual).stream().filter( + object -> "PRINTER".equals(anyObjectDAO.findById(object). + orElseThrow().getType().getKey())).toList(); + assertEquals(2, members.size()); + assertEquals( + Set.of("fc6dbc3a-6c07-4965-8781-921e7401a4a5", newAnyObjectKey), + new HashSet<>(members)); + + anyObject = anyObjectDAO.findById("fc6dbc3a-6c07-4965-8781-921e7401a4a5").orElseThrow(); + Collection dynGroupMemberships = anyObjectDAO.findDynGroups(anyObject.getKey()); + assertEquals(1, dynGroupMemberships.size()); + assertTrue(dynGroupMemberships.contains(actual.getADynMembership(anyTypeDAO.findById("PRINTER"). + orElseThrow()).get().getGroup())); + + // 4. delete the new any object and verify that dynamic membership was updated + anyObjectDAO.deleteById(newAnyObjectKey); + + actual = groupDAO.findById(actual.getKey()).orElseThrow(); + members = groupDAO.findADynMembers(actual).stream().filter( + object -> "PRINTER".equals(anyObjectDAO.findById(object). + orElseThrow().getType().getKey())).toList(); + assertEquals(1, members.size()); + assertEquals("fc6dbc3a-6c07-4965-8781-921e7401a4a5", members.get(0)); + + // 5. delete group and verify that dynamic membership was also removed + String dynMembershipKey = actual.getADynMembership(anyTypeDAO.findById("PRINTER").orElseThrow()).get().getKey(); + + groupDAO.delete(actual); + + assertTrue(neo4jTemplate.findById(dynMembershipKey, Neo4jADynGroupMembership.class).isEmpty()); + + dynGroupMemberships = anyObjectDAO.findDynGroups(anyObject.getKey()); + assertTrue(dynGroupMemberships.isEmpty()); + } + + @Test + public void issueSYNCOPE1512() { + Group group = groupDAO.findByName("root").orElseThrow(); + + // non unique + GPlainAttr title = entityFactory.newEntity(GPlainAttr.class); + title.setOwner(group); + title.setSchema(plainSchemaDAO.findById("title").orElseThrow()); + title.add(validator, "syncope's group", anyUtilsFactory.getInstance(AnyTypeKind.GROUP)); + group.add(title); + + // unique + GPlainAttr originalName = entityFactory.newEntity(GPlainAttr.class); + originalName.setOwner(group); + originalName.setSchema(plainSchemaDAO.findById("originalName").orElseThrow()); + originalName.add(validator, "syncope's group", anyUtilsFactory.getInstance(AnyTypeKind.GROUP)); + group.add(originalName); + + groupDAO.save(group); + + group = groupDAO.findById(group.getKey()).orElseThrow(); + assertEquals("syncope's group", group.getPlainAttr("title").get().getValuesAsStrings().get(0)); + assertEquals("syncope's group", group.getPlainAttr("originalName").get().getValuesAsStrings().get(0)); + } +} 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 new file mode 100644 index 0000000000..1292093248 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/PlainSchemaTest.java @@ -0,0 +1,193 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.outer; + +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.junit.jupiter.api.Assertions.fail; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.to.Item; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.AttrSchemaType; +import org.apache.syncope.common.lib.types.IdMEntitlement; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +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.ExternalResourceDAO; +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.PlainSchema; +import org.apache.syncope.core.persistence.neo4j.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.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +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 PlainSchemaTest extends AbstractTest { + + @Autowired + private UserDAO userDAO; + + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + @Autowired + private DerSchemaDAO derSchemaDAO; + + @Autowired + private ExternalResourceDAO resourceDAO; + + @Autowired + private AnyTypeClassDAO anyTypeClassDAO; + + @BeforeAll + public static void setAuthContext() { + List authorities = Stream.concat( + IdRepoEntitlement.values().stream(), 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); + } + + @Test + public void findByAnyTypeClasses() { + List found = plainSchemaDAO.findByAnyTypeClasses( + List.of(anyTypeClassDAO.findById("minimal user").orElseThrow())); + assertEquals(5, found.size()); + + found = plainSchemaDAO.findByAnyTypeClasses( + List.of(anyTypeClassDAO.findById("other").orElseThrow())); + assertEquals(10, found.size()); + } + + @Test + public void checkIdUniqueness() { + assertNotNull(derSchemaDAO.findById("cn").orElseThrow()); + + PlainSchema schema = entityFactory.newEntity(PlainSchema.class); + schema.setKey("cn"); + schema.setType(AttrSchemaType.String); + + try { + plainSchemaDAO.save(schema); + fail("This should not happen"); + } catch (Exception e) { + assertTrue(e instanceof DataIntegrityViolationException); + } + } + + private List getMappingItems(final String intAttrName) { + return resourceDAO.findAll().stream(). + flatMap(resource -> resource.getProvisions().stream()). + flatMap(provision -> provision.getMapping().getItems().stream()). + filter(item -> intAttrName.equals(item.getIntAttrName())). + toList(); + } + + @Test + public void deleteFullname() { + // fullname is mapped as ConnObjectKey for ws-target-resource-2, need to swap it otherwise validation errors + // will be raised + resourceDAO.findById("ws-target-resource-2").orElseThrow(). + getProvisionByAnyType(AnyTypeKind.USER.name()).get().getMapping().getItems(). + forEach(item -> { + if ("fullname".equals(item.getIntAttrName())) { + item.setConnObjectKey(false); + } else if ("surname".equals(item.getIntAttrName())) { + item.setConnObjectKey(true); + } + }); + + // search for user schema fullname + plainSchemaDAO.findById("fullname").orElseThrow(); + + // check for associated mappings + List mapItems = getMappingItems("fullname"); + assertFalse(mapItems.isEmpty()); + + // delete user schema fullname + plainSchemaDAO.deleteById("fullname"); + + // check for schema deletion + assertTrue(plainSchemaDAO.findById("fullname").isEmpty()); + + // check for mappings deletion + mapItems = getMappingItems("fullname"); + assertTrue(mapItems.isEmpty()); + + assertFalse(userDAO.findByUsername("rossini").orElseThrow().getPlainAttr("fullname").isPresent()); + assertFalse(userDAO.findByUsername("vivaldi").orElseThrow().getPlainAttr("fullname").isPresent()); + } + + @Test + public void deleteSurname() { + // search for user schema surname + PlainSchema schema = plainSchemaDAO.findById("surname").orElseThrow(); + + // check for associated mappings + List mapItems = getMappingItems("surname"); + assertFalse(mapItems.isEmpty()); + + // check for labels + assertEquals(2, schema.getLabels().size()); + + // delete user schema surname + plainSchemaDAO.deleteById("surname"); + + // check for schema deletion + assertTrue(plainSchemaDAO.findById("surname").isEmpty()); + } + + @Test + public void deleteFirstname() { + int pre = resourceDAO.findById("resource-db-pull").orElseThrow(). + getProvisionByAnyType(AnyTypeKind.USER.name()).get().getMapping().getItems().size(); + + plainSchemaDAO.deleteById("firstname"); + + assertTrue(plainSchemaDAO.findById("firstname").isEmpty()); + + assertEquals(pre - 1, 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 new file mode 100644 index 0000000000..f03e4aa211 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/PolicyTest.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.outer; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.UUID; +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.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp; +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.persistence.neo4j.inner.AbstractClientAppTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class PolicyTest extends AbstractClientAppTest { + + @Autowired + private OIDCRPClientAppDAO oidcRelyingPartyDAO; + + @Autowired + private RealmDAO realmDAO; + + @Test + public void authPolicyCanBeNull() { + Realm realm = realmDAO.findByFullPath("/odd").orElseThrow(); + + // Create new client app and assign policy + OIDCRPClientApp rp = entityFactory.newEntity(OIDCRPClientApp.class); + rp.setName("OIDC"); + rp.setClientAppId(UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE); + rp.setDescription("This is a sample OIDC RP"); + rp.setClientId(UUID.randomUUID().toString()); + rp.setClientSecret("secret"); + rp.setRealm(realm); + + assertDoesNotThrow(() -> oidcRelyingPartyDAO.save(rp)); + } + + @Test + public void removePolicyFromRealm() { + AuthPolicy authPolicy = buildAndSaveAuthPolicy(); + AccessPolicy accessPolicy = buildAndSaveAccessPolicy(); + AttrReleasePolicy attrPolicy = buildAndSaveAttrRelPolicy(); + TicketExpirationPolicy ticketExpirationPolicy = buildAndSaveTicketExpirationPolicy(); + + Realm realm = realmDAO.getRoot(); + realm.setAuthPolicy(authPolicy); + realm.setAccessPolicy(accessPolicy); + realm.setAttrReleasePolicy(attrPolicy); + realm.setTicketExpirationPolicy(ticketExpirationPolicy); + + realm = realmDAO.save(realm); + assertNotNull(realm.getAuthPolicy()); + assertNotNull(realm.getAccessPolicy()); + assertNotNull(realm.getAttrReleasePolicy()); + + policyDAO.delete(authPolicy); + policyDAO.delete(accessPolicy); + policyDAO.delete(attrPolicy); + policyDAO.delete(ticketExpirationPolicy); + + realm = realmDAO.getRoot(); + assertNull(realm.getAuthPolicy()); + assertNull(realm.getAccessPolicy()); + assertNull(realm.getAttrReleasePolicy()); + assertNull(realm.getTicketExpirationPolicy()); + } + + @Test + public void removePolicyFromApps() { + // Create new policy + AccessPolicy accessPolicy = buildAndSaveAccessPolicy(); + AuthPolicy authPolicy = buildAndSaveAuthPolicy(); + TicketExpirationPolicy ticketExpirationPolicy = buildAndSaveTicketExpirationPolicy(); + + // Create new client app and assign policy + OIDCRPClientApp rp = entityFactory.newEntity(OIDCRPClientApp.class); + rp.setName("OIDC"); + rp.setClientAppId(UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE); + rp.setDescription("This is a sample OIDC RP"); + rp.setClientId(UUID.randomUUID().toString()); + rp.setClientSecret("secret"); + rp.setAccessPolicy(accessPolicy); + rp.setAuthPolicy(authPolicy); + rp.setTicketExpirationPolicy(ticketExpirationPolicy); + + rp = oidcRelyingPartyDAO.save(rp); + assertNotNull(rp.getAuthPolicy()); + assertNotNull(rp.getAccessPolicy()); + assertNotNull(rp.getTicketExpirationPolicy()); + + policyDAO.delete(accessPolicy); + policyDAO.delete(authPolicy); + policyDAO.delete(ticketExpirationPolicy); + + rp = oidcRelyingPartyDAO.findById(rp.getKey()).orElseThrow(); + assertNull(rp.getAuthPolicy()); + assertNull(rp.getAccessPolicy()); + assertNull(rp.getTicketExpirationPolicy()); + } +} 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 new file mode 100644 index 0000000000..3bb4ca017f --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/RealmTest.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.persistence.neo4j.outer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +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.RoleDAO; +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.group.Group; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class RealmTest extends AbstractTest { + + @Autowired + private RealmDAO realmDAO; + + @Autowired + private RoleDAO roleDAO; + + @Autowired + private GroupDAO groupDAO; + + @Test + public void test() { + Realm realm = realmDAO.findByFullPath("/odd").orElseThrow(); + + // need to remove this group in order to remove the realm, which is otherwise empty + Group group = groupDAO.findByName("fake").orElseThrow(); + assertEquals(realm, group.getRealm()); + groupDAO.delete(group); + + Role role = roleDAO.findById("User reviewer").orElseThrow(); + assertTrue(role.getRealms().contains(realm)); + + int beforeSize = role.getRealms().size(); + + realmDAO.delete(realm); + + role = roleDAO.findById("User reviewer").orElseThrow(); + assertEquals(beforeSize - 1, role.getRealms().size()); + } +} 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 new file mode 100644 index 0000000000..fe9bc07cf4 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ReportTest.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.persistence.neo4j.outer; + +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 jakarta.ws.rs.core.MediaType; +import java.time.OffsetDateTime; +import org.apache.syncope.core.persistence.api.dao.ImplementationDAO; +import org.apache.syncope.core.persistence.api.dao.ReportDAO; +import org.apache.syncope.core.persistence.api.dao.ReportExecDAO; +import org.apache.syncope.core.persistence.api.entity.Report; +import org.apache.syncope.core.persistence.api.entity.ReportExec; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class ReportTest extends AbstractTest { + + @Autowired + private ReportDAO reportDAO; + + @Autowired + private ReportExecDAO reportExecDAO; + + @Autowired + private ImplementationDAO implementationDAO; + + @Test + public void find() { + Report report = reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").orElseThrow(); + + assertNotNull(report.getExecs()); + assertFalse(report.getExecs().isEmpty()); + assertEquals(1, report.getExecs().size()); + } + + @Test + public void saveWithExistingName() { + assertThrows(DataIntegrityViolationException.class, () -> { + Report report = reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").orElseThrow(); + + String name = report.getName(); + + report = entityFactory.newEntity(Report.class); + report.setName(name); + report.setJobDelegate(implementationDAO.findById("SampleReportJobDelegate").orElseThrow()); + report.setMimeType(MediaType.TEXT_PLAIN); + report.setFileExt("txt"); + report.setActive(true); + + reportDAO.save(report); + }); + } + + @Test + public void save() { + Report report = reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").orElseThrow(); + assertEquals(1, report.getExecs().size()); + + ReportExec reportExec = entityFactory.newEntity(ReportExec.class); + reportExec.setReport(report); + reportExec.setStart(OffsetDateTime.now()); + reportExec.setEnd(OffsetDateTime.now()); + reportExec.setStatus("SUCCESS"); + reportExec.setExecutor("admin"); + + report.add(reportExec); + reportDAO.save(report); + + report = reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").orElseThrow(); + assertEquals(2, report.getExecs().size()); + } + + @Test + public void deleteReport() { + reportDAO.deleteById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b"); + + assertTrue(reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").isEmpty()); + assertTrue(reportExecDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").isEmpty()); + } + + @Test + public void deleteReportExecution() { + ReportExec execution = reportExecDAO.findById("c13f39c5-0d35-4bff-ba79-3cd5de940369").orElseThrow(); + int executionNumber = execution.getReport().getExecs().size(); + + reportExecDAO.deleteById("c13f39c5-0d35-4bff-ba79-3cd5de940369"); + + assertTrue(reportExecDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").isEmpty()); + + Report report = reportDAO.findById("0062ea9c-924d-4ecf-9961-4492a8cc6d1b").orElseThrow(); + assertEquals(report.getExecs().size(), executionNumber - 1); + } +} 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 new file mode 100644 index 0000000000..ce5ef540b1 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/ResourceTest.java @@ -0,0 +1,278 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.outer; + +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 java.util.Set; +import java.util.stream.Collectors; +import org.apache.syncope.common.lib.to.Item; +import org.apache.syncope.common.lib.to.Mapping; +import org.apache.syncope.common.lib.to.Provision; +import org.apache.syncope.common.lib.types.AnyTypeKind; +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.TaskType; +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.ImplementationDAO; +import org.apache.syncope.core.persistence.api.dao.PolicyDAO; +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.entity.ConnInstance; +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.policy.PasswordPolicy; +import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.identityconnectors.framework.common.objects.ObjectClass; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class ResourceTest extends AbstractTest { + + @Autowired + private ExternalResourceDAO resourceDAO; + + @Autowired + private ConnInstanceDAO connInstanceDAO; + + @Autowired + private UserDAO userDAO; + + @Autowired + private TaskDAO taskDAO; + + @Autowired + private PolicyDAO policyDAO; + + @Autowired + private ImplementationDAO implementationDAO; + + @Test + public 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() { + Implementation impl = entityFactory.newEntity(Implementation.class); + impl.setType(IdMImplementationType.PROVISION_SORTER); + impl.setEngine(ImplementationEngine.GROOVY); + impl.setKey("ProvSorter"); + impl = implementationDAO.save(impl); + + assertTrue(resourceDAO.findByProvisionSorter(impl).isEmpty()); + + ExternalResource csv = resourceDAO.findById("resource-csv").orElseThrow(); + csv.setProvisionSorter(impl); + csv = resourceDAO.save(csv); + + assertEquals(List.of(csv), resourceDAO.findByProvisionSorter(impl)); + } + + @Test + public void findByPropagationActionsContaining() { + Implementation impl = implementationDAO.findById("GenerateRandomPasswordPropagationActions").orElseThrow(); + + assertEquals( + Set.of(resourceDAO.findById("resource-testdb2").orElseThrow(), + resourceDAO.findById("resource-ldap").orElseThrow()), + resourceDAO.findByPropagationActionsContaining(impl).stream().collect(Collectors.toSet())); + } + + @Test + public void createWithPasswordPolicy() { + final String resourceName = "resourceWithPasswordPolicy"; + + PasswordPolicy policy = policyDAO.findById("986d1236-3ac5-4a19-810c-5ab21d79cba1", PasswordPolicy.class). + orElseThrow(); + ExternalResource resource = entityFactory.newEntity(ExternalResource.class); + resource.setKey(resourceName); + resource.setPasswordPolicy(policy); + + ConnInstance connector = connInstanceDAO.findById("88a7a819-dab5-46b4-9b90-0b9769eabdb8").orElseThrow(); + assertNotNull(connector); + resource.setConnector(connector); + + ExternalResource actual = resourceDAO.save(resource); + assertNotNull(actual); + + actual = resourceDAO.findById(actual.getKey()).orElseThrow(); + assertNotNull(actual); + assertNotNull(actual.getPasswordPolicy()); + + resourceDAO.deleteById(resourceName); + assertTrue(resourceDAO.findById(resourceName).isEmpty()); + + assertTrue(policyDAO.findById("986d1236-3ac5-4a19-810c-5ab21d79cba1").isPresent()); + } + + @Test + public void save() { + ExternalResource resource = entityFactory.newEntity(ExternalResource.class); + resource.setKey("ws-target-resource-save"); + + // specify the connector + ConnInstance connector = connInstanceDAO.findById("88a7a819-dab5-46b4-9b90-0b9769eabdb8").orElseThrow(); + + resource.setConnector(connector); + + Provision provision = new Provision(); + provision.setAnyType(AnyTypeKind.USER.name()); + provision.setObjectClass(ObjectClass.ACCOUNT_NAME); + resource.getProvisions().add(provision); + + Mapping mapping = new Mapping(); + provision.setMapping(mapping); + + // specify mappings + for (int i = 0; i < 3; i++) { + Item item = new Item(); + item.setExtAttrName("test" + i); + item.setIntAttrName("nonexistent" + i); + item.setMandatoryCondition("false"); + item.setPurpose(MappingPurpose.PULL); + mapping.add(item); + } + Item connObjectKey = new Item(); + connObjectKey.setExtAttrName("username"); + connObjectKey.setIntAttrName("username"); + connObjectKey.setPurpose(MappingPurpose.PROPAGATION); + mapping.setConnObjectKeyItem(connObjectKey); + + // map a derived attribute + Item derived = new Item(); + derived.setConnObjectKey(false); + derived.setExtAttrName("fullname"); + derived.setIntAttrName("cn"); + derived.setPurpose(MappingPurpose.PROPAGATION); + mapping.add(derived); + + // save the resource + ExternalResource actual = resourceDAO.save(resource); + assertNotNull(actual); + assertNotNull(actual.getProvisionByAnyType(AnyTypeKind.USER.name()).get().getMapping()); + + // assign the new resource to an user + User user = userDAO.findByUsername("rossini").orElseThrow(); + user.add(actual); + userDAO.save(user); + + // retrieve resource + resource = resourceDAO.findById(actual.getKey()).orElseThrow(); + + // check connector + connector = connInstanceDAO.findById("88a7a819-dab5-46b4-9b90-0b9769eabdb8").orElseThrow(); + assertNotNull(connector.getResources()); + + assertNotNull(resource.getConnector()); + assertTrue(resource.getConnector().equals(connector)); + + // check mappings + List items = resource.getProvisionByAnyType( + AnyTypeKind.USER.name()).get().getMapping().getItems(); + assertNotNull(items); + assertEquals(5, items.size()); + + // check user + user = userDAO.findByUsername("rossini").orElseThrow(); + assertNotNull(user.getResources()); + assertTrue(user.getResources().contains(actual)); + } + + @Test + public void delete() { + ExternalResource resource = resourceDAO.findById("resource-testdb").orElseThrow(); + + // ------------------------------------- + // Get originally associated connector + // ------------------------------------- + ConnInstance connector = resource.getConnector(); + assertNotNull(connector); + // ------------------------------------- + + // ------------------------------------- + // Get originally associated users + // ------------------------------------- + List users = userDAO.findByResourcesContaining(resource); + assertNotNull(users); + + Set userKeys = users.stream().map(User::getKey).collect(Collectors.toSet()); + // ------------------------------------- + + // Get tasks + List propagationTasks = taskDAO.findAll( + TaskType.PROPAGATION, resource, null, null, null, Pageable.unpaged()); + assertFalse(propagationTasks.isEmpty()); + + // delete resource + resourceDAO.deleteById(resource.getKey()); + + // resource must be removed + assertTrue(resourceDAO.findById("resource-testdb").isEmpty()); + + // resource must be not referenced any more from users + userKeys.stream().map(u -> userDAO.findById(u).orElseThrow()).forEach(user -> { + assertNotNull(user); + userDAO.findAllResources(user). + forEach(r -> assertFalse(r.getKey().equalsIgnoreCase(resource.getKey()))); + }); + + // resource must be not referenced any more from the connector + ConnInstance actualConnector = connInstanceDAO.findById(connector.getKey()).orElseThrow(); + actualConnector.getResources(). + forEach(res -> assertFalse(res.getKey().equalsIgnoreCase(resource.getKey()))); + + // there must be no tasks + propagationTasks.forEach(task -> assertTrue(taskDAO.findById(task.getKey()).isEmpty())); + } + + @Test + public void issue243() { + ExternalResource csv = resourceDAO.findById("resource-csv").orElseThrow(); + + int origMapItems = csv.getProvisionByAnyType( + AnyTypeKind.USER.name()).get().getMapping().getItems().size(); + + Item newMapItem = new Item(); + newMapItem.setIntAttrName("TEST"); + newMapItem.setExtAttrName("TEST"); + newMapItem.setPurpose(MappingPurpose.PROPAGATION); + csv.getProvisionByAnyType(AnyTypeKind.USER.name()).get().getMapping().add(newMapItem); + + resourceDAO.save(csv); + + csv = resourceDAO.findById("resource-csv").orElseThrow(); + assertEquals( + origMapItems + 1, + csv.getProvisionByAnyType(AnyTypeKind.USER.name()).get().getMapping().getItems().size()); + } +} 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 new file mode 100644 index 0000000000..aad36bc70e --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/RoleTest.java @@ -0,0 +1,183 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.outer; + +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.OffsetDateTime; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; +import org.apache.syncope.core.persistence.api.dao.DelegationDAO; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.RealmDAO; +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.Delegation; +import org.apache.syncope.core.persistence.api.entity.Role; +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class RoleTest extends AbstractTest { + + @Autowired + private RoleDAO roleDAO; + + @Autowired + private RealmDAO realmDAO; + + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + @Autowired + private UserDAO userDAO; + + @Autowired + private AnyTypeClassDAO anyTypeClassDAO; + + @Autowired + private DelegationDAO delegationDAO; + + @Autowired + private PlainAttrValidationManager validator; + + @Test + public void dynMembership() { + // 0. create user matching the condition below + User user = entityFactory.newEntity(User.class); + user.setUsername("username"); + user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.add(anyTypeClassDAO.findById("other").orElseThrow()); + + UPlainAttr attr = entityFactory.newEntity(UPlainAttr.class); + attr.setOwner(user); + attr.setSchema(plainSchemaDAO.findById("cool").orElseThrow()); + attr.add(validator, "true", anyUtilsFactory.getInstance(AnyTypeKind.USER)); + user.add(attr); + + user = userDAO.save(user); + String newUserKey = user.getKey(); + assertNotNull(newUserKey); + + // 1. create role with dynamic membership + Role role = entityFactory.newEntity(Role.class); + role.setKey("new"); + role.add(realmDAO.getRoot()); + role.add(realmDAO.findByFullPath("/even/two").orElseThrow()); + role.getEntitlements().add(IdRepoEntitlement.AUDIT_LIST); + role.getEntitlements().add(IdRepoEntitlement.AUDIT_SET); + role.setDynMembershipCond("cool==true"); + + Role actual = roleDAO.saveAndRefreshDynMemberships(role); + assertNotNull(actual); + + // 2. verify that dynamic membership is there + actual = roleDAO.findById(actual.getKey()).orElseThrow(); + assertNotNull(actual.getDynMembershipCond()); + + // 3. verify that expected users have the created role dynamically assigned + List members = roleDAO.findDynMembers(actual); + assertEquals(2, members.size()); + assertEquals(Set.of("c9b2dec2-00a7-4855-97c0-d854842b4b24", newUserKey), new HashSet<>(members)); + + user = userDAO.findById("c9b2dec2-00a7-4855-97c0-d854842b4b24").orElseThrow(); + Collection dynRoleMemberships = userDAO.findDynRoles(user.getKey()); + assertEquals(1, dynRoleMemberships.size()); + assertTrue(dynRoleMemberships.contains(actual)); + + // 4. delete the new user and verify that dynamic membership was updated + userDAO.deleteById(newUserKey); + + actual = roleDAO.findById(actual.getKey()).orElseThrow(); + members = roleDAO.findDynMembers(actual); + assertEquals(1, members.size()); + assertEquals("c9b2dec2-00a7-4855-97c0-d854842b4b24", members.get(0)); + + // 5. delete role and verify that dynamic membership was also removed + roleDAO.delete(actual); + + dynRoleMemberships = userDAO.findDynRoles(user.getKey()); + assertTrue(dynRoleMemberships.isEmpty()); + } + + @Test + public void delete() { + // 0. create role + Role role = entityFactory.newEntity(Role.class); + role.setKey("new"); + role.add(realmDAO.getRoot()); + role.add(realmDAO.findByFullPath("/even/two").orElseThrow()); + role.getEntitlements().add(IdRepoEntitlement.AUDIT_LIST); + role.getEntitlements().add(IdRepoEntitlement.AUDIT_SET); + + role = roleDAO.save(role); + assertNotNull(role); + + // 1. create user and assign that role + User user = entityFactory.newEntity(User.class); + user.setUsername("username"); + user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.add(role); + + user = userDAO.save(user); + assertNotNull(user); + + // 2. remove role + roleDAO.delete(role); + + // 3. verify that role was removed from user + user = userDAO.findById(user.getKey()).orElseThrow(); + assertTrue(user.getRoles().isEmpty()); + } + + @Test + public void deleteCascadeOnDelegations() { + User bellini = userDAO.findByUsername("bellini").orElseThrow(); + User rossini = userDAO.findByUsername("rossini").orElseThrow(); + + Role reviewer = roleDAO.findById("User reviewer").orElseThrow(); + + Delegation delegation = entityFactory.newEntity(Delegation.class); + delegation.setDelegating(bellini); + delegation.setDelegated(rossini); + delegation.setStart(OffsetDateTime.now()); + delegation.add(reviewer); + delegation = delegationDAO.save(delegation); + + delegation = delegationDAO.findById(delegation.getKey()).orElseThrow(); + + assertEquals(List.of(delegation), delegationDAO.findByRoles(reviewer)); + + roleDAO.delete(reviewer); + + assertTrue(delegationDAO.findById(delegation.getKey()).orElseThrow().getRoles().isEmpty()); + } +} 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 new file mode 100644 index 0000000000..f6cab07ec4 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/SecurityQuestionTest.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.core.persistence.neo4j.outer; + +import static org.junit.jupiter.api.Assertions.assertNull; + +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.User; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class SecurityQuestionTest extends AbstractTest { + + @Autowired + private SecurityQuestionDAO securityQuestionDAO; + + @Autowired + private UserDAO userDAO; + + @Test + public void test() { + User user = userDAO.findByUsername("bellini").orElseThrow(); + assertNull(user.getSecurityQuestion()); + assertNull(user.getSecurityAnswer()); + + user.setSecurityQuestion(securityQuestionDAO.findById("887028ea-66fc-41e7-b397-620d7ea6dfbb").orElseThrow()); + user.setSecurityAnswer("Rossi"); + userDAO.save(user); + + securityQuestionDAO.deleteById("887028ea-66fc-41e7-b397-620d7ea6dfbb"); + + 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 new file mode 100644 index 0000000000..fffa07bcee --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/TaskTest.java @@ -0,0 +1,327 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.outer; + +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.junit.jupiter.api.Assertions.fail; + +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.ExecStatus; +import org.apache.syncope.common.lib.types.IdMImplementationType; +import org.apache.syncope.common.lib.types.ImplementationEngine; +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.UnmatchingRule; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +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.RealmDAO; +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.Implementation; +import org.apache.syncope.core.persistence.api.entity.task.PropagationData; +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.PushTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.api.entity.task.Task; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.apache.syncope.core.provisioning.api.pushpull.PullActions; +import org.identityconnectors.framework.common.objects.Attribute; +import org.identityconnectors.framework.common.objects.AttributeBuilder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class TaskTest extends AbstractTest { + + @Autowired + private TaskDAO taskDAO; + + @Autowired + private TaskExecDAO taskExecDAO; + + @Autowired + private ExternalResourceDAO resourceDAO; + + @Autowired + private RealmDAO realmDAO; + + @Autowired + private ImplementationDAO implementationDAO; + + @Autowired + private TaskUtilsFactory taskUtilsFactory; + + @Test + public void read() { + Task task = taskDAO.findById(TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); + assertNotNull(task); + + assertNotNull(task.getExecs()); + assertFalse(task.getExecs().isEmpty()); + assertEquals(1, task.getExecs().size()); + } + + @Test + public void readMultipleOrderBy() { + List orderByClauses = new ArrayList<>(); + orderByClauses.add(new Sort.Order(Sort.DEFAULT_DIRECTION, "start")); + orderByClauses.add(new Sort.Order(Sort.DEFAULT_DIRECTION, "latestExecStatus")); + orderByClauses.add(new Sort.Order(Sort.DEFAULT_DIRECTION, "connObjectKey")); + assertFalse(taskDAO.findAll( + TaskType.PROPAGATION, null, null, null, null, Pageable.unpaged(Sort.by(orderByClauses))). + isEmpty()); + } + + @Test + public void savePropagationTask() { + ExternalResource resource = resourceDAO.findById("ws-target-resource-1").orElseThrow(); + + PropagationTask task = entityFactory.newEntity(PropagationTask.class); + task.setResource(resource); + task.setAnyTypeKind(AnyTypeKind.USER); + task.setAnyType(AnyTypeKind.USER.name()); + task.setOperation(ResourceOperation.CREATE); + task.setConnObjectKey("one@two.com"); + + Set attributes = new HashSet<>(); + attributes.add(AttributeBuilder.build("testAttribute", "testValue1", "testValue2")); + attributes.add(AttributeBuilder.buildPassword("password".toCharArray())); + task.setPropagationData(new PropagationData(attributes)); + + task = taskDAO.save(task); + assertNotNull(task); + + Task actual = taskDAO.findById(TaskType.PROPAGATION, task.getKey()).orElseThrow(); + assertEquals(task, actual); + + assertTrue(taskDAO.findAll( + TaskType.PROPAGATION, resource, null, null, null, Pageable.unpaged()). + contains(task)); + } + + @Test + public void addPropagationTaskExecution() { + PropagationTask task = (PropagationTask) taskDAO.findById( + TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); + assertNotNull(task); + + int executionNumber = task.getExecs().size(); + + TaskExec execution = taskUtilsFactory.getInstance(TaskType.PROPAGATION).newTaskExec(); + execution.setTask(task); + execution.setStatus(ExecStatus.CREATED.name()); + execution.setStart(OffsetDateTime.now()); + execution.setExecutor("admin"); + task.add(execution); + execution.setTask(task); + + taskDAO.save(task); + + task = (PropagationTask) taskDAO.findById( + TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); + assertNotNull(task); + + assertEquals(executionNumber + 1, task.getExecs().size()); + } + + @Test + public void addPullTaskExecution() { + PullTask task = (PullTask) taskDAO.findById( + TaskType.PULL, "c41b9b71-9bfa-4f90-89f2-84787def4c5c").orElseThrow(); + assertNotNull(task); + + int executionNumber = task.getExecs().size(); + + TaskExec execution = taskUtilsFactory.getInstance(TaskType.PULL).newTaskExec(); + execution.setStatus("Text-free status"); + execution.setTask(task); + execution.setStart(OffsetDateTime.now()); + execution.setMessage("A message"); + execution.setExecutor("admin"); + task.add(execution); + execution.setTask(task); + + taskDAO.save(task); + + task = (PullTask) taskDAO.findById( + TaskType.PULL, "c41b9b71-9bfa-4f90-89f2-84787def4c5c").orElseThrow(); + assertNotNull(task); + + assertEquals(executionNumber + 1, task.getExecs().size()); + } + + @Test + public void addPushTaskExecution() { + PushTask task = (PushTask) taskDAO.findById( + TaskType.PUSH, "af558be4-9d2f-4359-bf85-a554e6e90be1").orElseThrow(); + assertNotNull(task); + + int executionNumber = task.getExecs().size(); + + TaskExec execution = taskUtilsFactory.getInstance(TaskType.PUSH).newTaskExec(); + execution.setStatus("Text-free status"); + execution.setTask(task); + execution.setStart(OffsetDateTime.now()); + execution.setMessage("A message"); + execution.setExecutor("admin"); + task.add(execution); + execution.setTask(task); + + taskDAO.save(task); + + task = (PushTask) taskDAO.findById(TaskType.PUSH, "af558be4-9d2f-4359-bf85-a554e6e90be1").orElseThrow(); + assertNotNull(task); + + assertEquals(executionNumber + 1, task.getExecs().size()); + } + + @Test + public void deleteTask() { + taskDAO.delete(TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c"); + + assertTrue(taskDAO.findById("1e697572-b896-484c-ae7f-0c8f63fcbc6c").isEmpty()); + assertTrue(taskExecDAO.findById("e58ca1c7-178a-4012-8a71-8aa14eaf0655").isEmpty()); + } + + @Test + public void deleteTaskExecution() { + @SuppressWarnings("unchecked") + TaskExec execution = (TaskExec) taskExecDAO.findById( + TaskType.PROPAGATION, "e58ca1c7-178a-4012-8a71-8aa14eaf0655").orElseThrow(); + int executionNumber = execution.getTask().getExecs().size(); + + taskExecDAO.delete(TaskType.PROPAGATION, "e58ca1c7-178a-4012-8a71-8aa14eaf0655"); + + assertTrue(taskExecDAO.findById("e58ca1c7-178a-4012-8a71-8aa14eaf0655").isEmpty()); + + PropagationTask task = (PropagationTask) taskDAO.findById( + TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow(); + assertEquals(task.getExecs().size(), executionNumber - 1); + } + + @Test + public void savePullTask() { + PullTask task = entityFactory.newEntity(PullTask.class); + task.setName("savePullTask"); + task.setDescription("PullTask description"); + task.setActive(true); + task.setPullMode(PullMode.FULL_RECONCILIATION); + task.setJobDelegate(implementationDAO.findById("PullJobDelegate").orElseThrow()); + task.setDestinationRealm(realmDAO.getRoot()); + task.setCronExpression("BLA BLA"); + task.setMatchingRule(MatchingRule.UPDATE); + task.setUnmatchingRule(UnmatchingRule.PROVISION); + + // now adding PullActions + Implementation pullActions = entityFactory.newEntity(Implementation.class); + pullActions.setKey("PullActions" + UUID.randomUUID().toString()); + pullActions.setEngine(ImplementationEngine.JAVA); + pullActions.setType(IdMImplementationType.PULL_ACTIONS); + pullActions.setBody(PullActions.class.getName()); + pullActions = implementationDAO.save(pullActions); + + task.add(pullActions); + + // this save() fails because of an invalid Cron Expression + try { + taskDAO.save(task); + fail(); + } catch (InvalidEntityException e) { + assertNotNull(e); + } + task.setCronExpression(null); + + // this save() fails because a PullTask requires a target resource + try { + taskDAO.save(task); + fail(); + } catch (InvalidEntityException e) { + assertNotNull(e); + } + task.setResource(resourceDAO.findById("ws-target-resource-1").orElseThrow()); + + // this save() finally works + task = taskDAO.save(task); + assertNotNull(task); + + PullTask actual = (PullTask) taskDAO.findById(TaskType.PULL, task.getKey()).orElseThrow(); + assertEquals(task, actual); + } + + @Test + public void issueSYNCOPE144() { + ExternalResource resource = resourceDAO.findById("ws-target-resource-1").orElseThrow(); + assertNotNull(resource); + + Implementation pullActions = entityFactory.newEntity(Implementation.class); + pullActions.setKey("syncope144"); + pullActions.setEngine(ImplementationEngine.JAVA); + pullActions.setType(IdMImplementationType.PULL_ACTIONS); + pullActions.setBody(PullActions.class.getName()); + pullActions = implementationDAO.save(pullActions); + + PullTask task = entityFactory.newEntity(PullTask.class); + + task.setResource(resource); + task.setName("issueSYNCOPE144"); + task.setDescription("issueSYNCOPE144 Description"); + task.setActive(true); + task.setPullMode(PullMode.FULL_RECONCILIATION); + task.add(pullActions); + task.setMatchingRule(MatchingRule.UPDATE); + task.setUnmatchingRule(UnmatchingRule.PROVISION); + task.setJobDelegate(implementationDAO.findById("PullJobDelegate").orElseThrow()); + task.setDestinationRealm(realmDAO.getRoot()); + + task = taskDAO.save(task); + assertNotNull(task); + + PullTask actual = (PullTask) taskDAO.findById(TaskType.PULL, task.getKey()).orElseThrow(); + assertEquals(task, actual); + assertEquals("issueSYNCOPE144", actual.getName()); + assertEquals("issueSYNCOPE144 Description", actual.getDescription()); + + actual.setName("issueSYNCOPE144_2"); + actual.setDescription("issueSYNCOPE144 Description_2"); + + actual = taskDAO.save(actual); + assertNotNull(actual); + assertEquals("issueSYNCOPE144_2", actual.getName()); + assertEquals("issueSYNCOPE144 Description_2", actual.getDescription()); + } +} 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 new file mode 100644 index 0000000000..d8180e6fba --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/UserTest.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.persistence.neo4j.outer; + +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.time.OffsetDateTime; +import java.util.List; +import java.util.UUID; +import org.apache.syncope.common.lib.types.CipherAlgorithm; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +import org.apache.syncope.core.persistence.api.dao.ApplicationDAO; +import org.apache.syncope.core.persistence.api.dao.DelegationDAO; +import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; +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.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; +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.AnyUtils; +import org.apache.syncope.core.persistence.api.entity.Delegation; +import org.apache.syncope.core.persistence.api.entity.DerSchema; +import org.apache.syncope.core.persistence.api.entity.Role; +import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount; +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.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jLAPlainAttr; +import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jLinkedAccount; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class UserTest extends AbstractTest { + + @Autowired + private RelationshipTypeDAO relationshipTypeDAO; + + @Autowired + private AnyObjectDAO anyObjectDAO; + + @Autowired + private UserDAO userDAO; + + @Autowired + private GroupDAO groupDAO; + + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + @Autowired + private DerSchemaDAO derSchemaDAO; + + @Autowired + private ExternalResourceDAO resourceDAO; + + @Autowired + private ApplicationDAO applicationDAO; + + @Autowired + private DelegationDAO delegationDAO; + + @Autowired + private RoleDAO roleDAO; + + @Autowired + private PlainAttrValidationManager validator; + + @Autowired + private Neo4jTemplate neo4jTemplate; + + @Test + public void delete() { + List memberships = groupDAO.findUMemberships( + groupDAO.findByName("managingDirector").orElseThrow()); + assertFalse(memberships.isEmpty()); + + userDAO.deleteById("c9b2dec2-00a7-4855-97c0-d854842b4b24"); + + assertTrue(userDAO.findByUsername("bellini").isEmpty()); + assertTrue(plainSchemaDAO.findById("loginDate").isPresent()); + + memberships = groupDAO.findUMemberships(groupDAO.findByName("managingDirector").orElseThrow()); + assertTrue(memberships.isEmpty()); + } + + @Test + public void ships() { + User user = userDAO.findByUsername("bellini").orElseThrow(); + assertEquals(1, user.getMemberships().size()); + assertEquals("bf825fe1-7320-4a54-bd64-143b5c18ab97", user.getMemberships().get(0).getRightEnd().getKey()); + + user.remove(user.getMemberships().get(0)); + + UMembership newM = entityFactory.newEntity(UMembership.class); + newM.setLeftEnd(user); + newM.setRightEnd(groupDAO.findById("ba9ed509-b1f5-48ab-a334-c8530a6422dc").orElseThrow()); + user.add(newM); + + userDAO.save(user); + + user = userDAO.findByUsername("bellini").orElseThrow(); + assertEquals(1, user.getMemberships().size()); + assertEquals("ba9ed509-b1f5-48ab-a334-c8530a6422dc", user.getMemberships().get(0).getRightEnd().getKey()); + assertEquals(1, user.getRelationships().size()); + assertEquals("fc6dbc3a-6c07-4965-8781-921e7401a4a5", user.getRelationships().get(0).getRightEnd().getKey()); + + user.getRelationships().remove(0); + + URelationship newR = entityFactory.newEntity(URelationship.class); + newR.setType(relationshipTypeDAO.findById("neighborhood").orElseThrow()); + newR.setLeftEnd(user); + newR.setRightEnd(anyObjectDAO.findById("8559d14d-58c2-46eb-a2d4-a7d35161e8f8").orElseThrow()); + user.add(newR); + + userDAO.save(user); + + user = userDAO.findByUsername("bellini").orElseThrow(); + assertEquals(1, user.getRelationships().size()); + assertEquals("8559d14d-58c2-46eb-a2d4-a7d35161e8f8", user.getRelationships().get(0).getRightEnd().getKey()); + } + + private LinkedAccount newLinkedAccount(final String connObjectKeyValue) { + User user = userDAO.findByUsername("vivaldi").orElseThrow(); + assertTrue(user.getLinkedAccounts().isEmpty()); + + LinkedAccount account = entityFactory.newEntity(LinkedAccount.class); + account.setOwner(user); + user.add(account); + + account.setConnObjectKeyValue(connObjectKeyValue); + account.setResource(resourceDAO.findById("resource-ldap").orElseThrow()); + account.add(applicationDAO.findPrivilege("getMighty").orElseThrow()); + + account.setUsername(UUID.randomUUID().toString()); + account.setCipherAlgorithm(CipherAlgorithm.AES); + account.setPassword("Password123"); + + AnyUtils anyUtils = anyUtilsFactory.getLinkedAccountInstance(); + + Neo4jLAPlainAttr attr = anyUtils.newPlainAttr(); + attr.setOwner(user); + attr.setAccount(account); + attr.setSchema(plainSchemaDAO.findById("obscure").orElseThrow()); + attr.add(validator, "testvalue", anyUtils); + account.add(attr); + + user = userDAO.save(user); + + assertEquals(1, user.getLinkedAccounts().size()); + + return user.getLinkedAccounts().get(0); + } + + @Test + public void findLinkedAccount() { + LinkedAccount account = newLinkedAccount("findLinkedAccount"); + assertNotNull(account.getKey()); + assertEquals(1, account.getPlainAttrs().size()); + assertTrue(account.getPlainAttr("obscure").isPresent()); + assertEquals(account.getOwner(), account.getPlainAttr("obscure").get().getOwner()); + + assertTrue(userDAO.linkedAccountExists(account.getOwner().getKey(), account.getConnObjectKeyValue())); + + LinkedAccount found = userDAO.findLinkedAccount( + resourceDAO.findById("resource-ldap").orElseThrow(), "findLinkedAccount").orElseThrow(); + assertEquals(account, found); + + List accounts = userDAO.findLinkedAccountsByResource( + resourceDAO.findById("resource-ldap").orElseThrow()); + assertEquals(1, accounts.size()); + assertEquals(account, accounts.get(0)); + + accounts = userDAO.findLinkedAccountsByPrivilege( + applicationDAO.findPrivilege("getMighty").orElseThrow()); + assertEquals(1, accounts.size()); + assertEquals(account, accounts.get(0)); + } + + @Test + public void deleteLinkedAccountResourceCascade() { + LinkedAccount account = newLinkedAccount("deleteLinkedAccountResourceCascade"); + assertNotNull(account.getKey()); + + LinkedAccount found = neo4jTemplate.findById(account.getKey(), Neo4jLinkedAccount.class).orElseThrow(); + assertEquals(account, found); + + resourceDAO.deleteById(account.getResource().getKey()); + + assertTrue(neo4jTemplate.findById(account.getKey(), Neo4jLinkedAccount.class).isEmpty()); + } + + @Test + public void deleteCascadeOnDelegations() { + User bellini = userDAO.findByUsername("bellini").orElseThrow(); + User rossini = userDAO.findByUsername("rossini").orElseThrow(); + + Role reviewer = roleDAO.findById("User reviewer").orElseThrow(); + + Delegation delegation = entityFactory.newEntity(Delegation.class); + delegation.setDelegating(bellini); + delegation.setDelegated(rossini); + delegation.setStart(OffsetDateTime.now()); + delegation.add(reviewer); + delegation = delegationDAO.save(delegation); + + delegation = delegationDAO.findById(delegation.getKey()).orElseThrow(); + + assertEquals(List.of(delegation), delegationDAO.findByDelegating(bellini)); + assertEquals(List.of(delegation), delegationDAO.findByDelegated(rossini)); + + userDAO.deleteById(rossini.getKey()); + + assertTrue(delegationDAO.findById(delegation.getKey()).isEmpty()); + } + + /** + * Search by derived attribute. + */ + @Test + public void issueSYNCOPE800() { + // create derived attribute (literal as prefix) + DerSchema prefix = entityFactory.newEntity(DerSchema.class); + prefix.setKey("kprefix"); + prefix.setExpression("'k' + firstname"); + + derSchemaDAO.save(prefix); + + // create derived attribute (literal as suffix) + DerSchema suffix = entityFactory.newEntity(DerSchema.class); + suffix.setKey("ksuffix"); + suffix.setExpression("firstname + 'k'"); + + derSchemaDAO.save(suffix); + + // add derived attributes to user + User owner = userDAO.findByUsername("vivaldi").orElseThrow(); + + String firstname = owner.getPlainAttr("firstname").get().getValuesAsStrings().iterator().next(); + assertNotNull(firstname); + + // search by ksuffix derived attribute + List list = userDAO.findByDerAttrValue( + derSchemaDAO.findById("ksuffix").orElseThrow(), firstname + 'k', false); + assertEquals(1, list.size()); + + // search by kprefix derived attribute + list = userDAO.findByDerAttrValue(derSchemaDAO.findById("kprefix").orElseThrow(), 'k' + firstname, false); + assertEquals(1, list.size()); + } +} diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/VirSchemaTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/VirSchemaTest.java new file mode 100644 index 0000000000..ab9af6ffaf --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/VirSchemaTest.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.core.persistence.neo4j.outer; + +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 org.apache.syncope.common.lib.to.Item; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.VirSchema; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class VirSchemaTest extends AbstractTest { + + @Autowired + private VirSchemaDAO virSchemaDAO; + + @Autowired + private ExternalResourceDAO resourceDAO; + + @Autowired + private AnyTypeDAO anyTypeDAO; + + @Test + public void deal() { + ExternalResource resource = resourceDAO.findById("ws-target-resource-1").orElseThrow(); + assertTrue(virSchemaDAO.findByResource(resource).isEmpty()); + assertTrue(virSchemaDAO.findByResourceAndAnyType(resource.getKey(), AnyTypeKind.USER.name()).isEmpty()); + + VirSchema virSchema = entityFactory.newEntity(VirSchema.class); + virSchema.setKey("vSchema"); + virSchema.setReadonly(true); + virSchema.setExtAttrName("EXT_ATTR"); + virSchema.setResource(resource); + virSchema.setAnyType(anyTypeDAO.getUser()); + + virSchemaDAO.save(virSchema); + + virSchema = virSchemaDAO.findById("vSchema").orElseThrow(); + assertTrue(virSchema.isReadonly()); + assertEquals("EXT_ATTR", virSchema.getExtAttrName()); + + assertFalse(virSchemaDAO.findByResource(resource).isEmpty()); + assertTrue(virSchemaDAO.findByResource(resource).contains(virSchema)); + + assertFalse(virSchemaDAO.findByResourceAndAnyType( + resource.getKey(), AnyTypeKind.USER.name()).isEmpty()); + assertTrue(virSchemaDAO.findByResourceAndAnyType( + resource.getKey(), AnyTypeKind.USER.name()).contains(virSchema)); + + Item item = virSchema.asLinkingMappingItem(); + assertNotNull(item); + assertEquals(virSchema.getKey(), item.getIntAttrName()); + } +} 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 new file mode 100644 index 0000000000..ed6c542fd7 --- /dev/null +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/XMLContentExporterTest.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.outer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.List; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.core.persistence.api.content.ContentExporter; +import org.apache.syncope.core.persistence.neo4j.AbstractTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class XMLContentExporterTest extends AbstractTest { + + @Autowired + private ContentExporter exporter; + + /** + * Also checks for SYNCOPE-1307. + * + * @throws Exception exception thrown when dealing with IO. + */ + @Test + public void issueSYNCOPE1128() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + exporter.export(SyncopeConstants.MASTER_DOMAIN, 100, baos); + + String exported = baos.toString(StandardCharsets.UTF_8); + assertTrue(StringUtils.isNotBlank(exported)); + + Files.writeString(Path.of("/tmp/export.xml"), exported, + StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE); + + List realms = exported.lines().filter(row -> row.trim().startsWith(" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/persistence-neo4j/src/test/resources/domains/MasterKeymasterConfParams.json b/core/persistence-neo4j/src/test/resources/domains/MasterKeymasterConfParams.json new file mode 100644 index 0000000000..38f2b162f8 --- /dev/null +++ b/core/persistence-neo4j/src/test/resources/domains/MasterKeymasterConfParams.json @@ -0,0 +1,17 @@ +{ + "password.cipher.algorithm": "SHA1", + "notificationjob.cronExpression": "0/20 * * * * ?", + "notification.maxRetries": 3, + "token.length": 256, + "token.expireTime": 60, + "selfRegistration.allowed": true, + "passwordReset.allowed": true, + "passwordReset.securityQuestion": true, + "authentication.attributes": ["username", "userId"], + "authentication.statuses": ["created", "active"], + "log.lastlogindate": true, + "return.password.value": false, + "jwt.lifetime.minutes": 120, + "connector.conf.history.size": 10, + "resource.conf.history.size": 10 +} diff --git a/core/persistence-neo4j/src/test/resources/domains/TwoContent.xml b/core/persistence-neo4j/src/test/resources/domains/TwoContent.xml new file mode 100644 index 0000000000..297c886364 --- /dev/null +++ b/core/persistence-neo4j/src/test/resources/domains/TwoContent.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/persistence-neo4j/src/test/resources/domains/TwoKeymasterConfParams.json b/core/persistence-neo4j/src/test/resources/domains/TwoKeymasterConfParams.json new file mode 100644 index 0000000000..796f3a0472 --- /dev/null +++ b/core/persistence-neo4j/src/test/resources/domains/TwoKeymasterConfParams.json @@ -0,0 +1,15 @@ +{ + "password.cipher.algorithm": "SHA1", + "token.length": 256, + "token.expireTime": 60, + "selfRegistration.allowed": true, + "passwordReset.allowed": true, + "passwordReset.securityQuestion": true, + "authentication.attributes": ["username"], + "authentication.statuses": ["created", "active"], + "log.lastlogindate": true, + "return.password.value": false, + "jwt.lifetime.minutes": 120, + "connector.conf.history.size": 10, + "resource.conf.history.size": 10 +} \ No newline at end of file diff --git a/core/persistence-neo4j/src/test/resources/domains/TwoSecurity.json b/core/persistence-neo4j/src/test/resources/domains/TwoSecurity.json new file mode 100644 index 0000000000..53e0269b71 --- /dev/null +++ b/core/persistence-neo4j/src/test/resources/domains/TwoSecurity.json @@ -0,0 +1,4 @@ +{ + "password": "2AA60A8FF7FCD473D321E0146AFD9E26DF395147", + "cipherAlgorithm": "SHA" +} diff --git a/core/persistence-neo4j/src/test/resources/idp-metadata.xml b/core/persistence-neo4j/src/test/resources/idp-metadata.xml new file mode 100644 index 0000000000..2e0162ba73 --- /dev/null +++ b/core/persistence-neo4j/src/test/resources/idp-metadata.xml @@ -0,0 +1,144 @@ + + + + + + example.net + + + + + MIIDLTCCAhWgAwIBAgIUVqwgQMQunB5UtoiiOqP1oQeg7lcwDQYJKoZIhvcNAQEL + BQAwHjEcMBoGA1UEAwwTbW1vYXl5ZWQudW5pY29uLm5ldDAeFw0xOTExMDExNDQ3 + NDhaFw0zOTExMDExNDQ3NDhaMB4xHDAaBgNVBAMME21tb2F5eWVkLnVuaWNvbi5u + ZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLXKHf93KZztJfCpNg + R/ip6EdOp3Z52HmwT32QlOzeby+2prqbOanQcs5oEeXoz6cdzjwOO6isnqZ3ES7p + BuVyuUoYVZyuXY6dsk6ANxeOXBRzGBS3ZemzYRQVmvQudNHUqdXpJelkFZvz3Au2 + I594V2PZjywtuGIUb+T7j+8hh6Srf8c/W/KmC3KLFfU2yDQrcjuhGv+0Py5ZUpXs + EANs/d/AYV+LbMp3UtvWSOy8xGb+xxjS2KhTd53Oc6xsCgTPgTM5Y3DVA0ERNH+n + ppngRi/t3NggIN0EKYAS6ZqJi1GBEVHFOoacebLSy/UQA8tYI170/gf03/OYwO2S + 9GATAgMBAAGjYzBhMB0GA1UdDgQWBBQxJh8NNf+qGJNZPlOItCWFQFY/wDBABgNV + HREEOTA3ghNtbW9heXllZC51bmljb24ubmV0hiBtbW9heXllZC51bmljb24ubmV0 + L2lkcC9tZXRhZGF0YTANBgkqhkiG9w0BAQsFAAOCAQEAMMOb+f4Log69KUeAEvgh + sWTjiZujvl44nY4roXofAoXYc3vos/p5JVwEtrxgTLdyTsz65kZtaRISRrUJ3k0n + K22L2eXGa85qPhdKivRyNip5AMVi0zSXC6uhG50571Gy5UK/Rh3gvg7VM8GUFDHL + +Zay9ffV9lf0UVmFObA+PAe+HNY/dYRLIP9/pFW0+c1MmFtwCTrO4xbecfzA+Yde + 9dbaBjS4veOSvFKiaCOvsiIVEUt1J7NrqM5sgYvOR5Q5zv0G72pmzS8cuGe2UP7e + i24oGm471cMDTLyFLYMCL8veHydcgfIV9z5g0PksV0kQL91r4XVkIp3iFZJ+TUBF + zg== + + + + + + + MIIDLTCCAhWgAwIBAgIUKymtgciRE6pWwDfrsI58qL9pQMgwDQYJKoZIhvcNAQEL + BQAwHjEcMBoGA1UEAwwTbW1vYXl5ZWQudW5pY29uLm5ldDAeFw0xOTExMDExNDQ3 + NDhaFw0zOTExMDExNDQ3NDhaMB4xHDAaBgNVBAMME21tb2F5eWVkLnVuaWNvbi5u + ZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCKrT805sny3vlaYjTn + +m6m3VbUoStvnacgwHH+orNFhHoV1HP2ndoH5BsEDB9tQYuyRbGUm/nYVOSHayzB + G3bzMGdU7woO6rsCqpHuxUyEvojd/y/N/r9jMzeBOCu0KDBTrn3BJhnGSwSTfhOS + 3r20JFmDuTkHmabRs7ro0BvDaQ29jh38ro1iwB4E/4mqb1zYP13NI3ooErN/o6pl + XKpnFY37bDDOyOuocjN9tfPNIANNFKah0HjWOP0Nso0D1g6jHOSzmOw/Yxg61vBk + qOD4aKhLYPAxsXRl80nDrwTnm3/9xLQj9D3uLAtDLnn9pSqn3jCLxsxsHfKL/zkB + IKEBAgMBAAGjYzBhMB0GA1UdDgQWBBSrPjAgCJIHYmsofDcDIPzEhnYxmTBABgNV + HREEOTA3ghNtbW9heXllZC51bmljb24ubmV0hiBtbW9heXllZC51bmljb24ubmV0 + L2lkcC9tZXRhZGF0YTANBgkqhkiG9w0BAQsFAAOCAQEAI8MlofbE0tbq8ez2d0Lq + Syhp4Q/shMEwjqcDarOwR+ACB9McOannUpAG7TCDp8Ch5E/V1B0Uo/5DF2tAzB1y + 7sgAmy2mY9/mFhMYpOqTCagufwewaMkn9n7ETzC/6vQEjYrjiNyNR0F3UQQz2bhe + ROM3YuKctuOnMthc+ZE7vn+AXCGumRHBhyCaYdzfeUh7id+yrd9B51+o3iF4eu6w + zJi5z7FMCS6I4PSc/uWYDw1ahzoPONjazWSEWGUibZaJYM3pJHkuwqyWKOFGVknH + J1Qv4WCfSPb6eva94TZX0lkLM01C7NZObnfxY3fvJGcyFl8wlRTUYvuqM8md5CEp + LA== + + + + + + + + + + urn:mace:shibboleth:1.0:nameIdentifier + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + + + + + + + + + + + diff --git a/core/persistence-neo4j/src/test/resources/simplelogger.properties b/core/persistence-neo4j/src/test/resources/simplelogger.properties new file mode 100644 index 0000000000..973e0096ff --- /dev/null +++ b/core/persistence-neo4j/src/test/resources/simplelogger.properties @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# See http://www.slf4j.org/api/org/slf4j/impl/SimpleLogger.html +# Possible values: "trace", "debug", "info", "warn", or "error" +org.slf4j.simpleLogger.defaultLogLevel=debug + diff --git a/core/persistence-neo4j/src/test/resources/sp-metadata.xml b/core/persistence-neo4j/src/test/resources/sp-metadata.xml new file mode 100644 index 0000000000..351d556af4 --- /dev/null +++ b/core/persistence-neo4j/src/test/resources/sp-metadata.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + + + diff --git a/core/pom.xml b/core/pom.xml index 4d5c41a016..71e07b9966 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -81,8 +81,10 @@ under the License. idm am persistence-api + persistence-common persistence-jpa persistence-jpa-json + persistence-neo4j spring provisioning-api provisioning-java diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrNameParser.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrNameParser.java index 738d212257..f8db080331 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrNameParser.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrNameParser.java @@ -19,6 +19,7 @@ package org.apache.syncope.core.provisioning.api; import java.text.ParseException; +import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang3.tuple.Pair; @@ -100,15 +101,12 @@ protected void setFieldOrSchemaName( final AnyTypeKind anyTypeKind, final IntAttrName result) { - if (anyUtilsFactory.getInstance(anyTypeKind).getField(fieldOrSchemaName) == null) { - Pair schemaInfo = find(fieldOrSchemaName); - if (schemaInfo != null) { - result.setSchemaType(schemaInfo.getRight()); - result.setSchema(schemaInfo.getLeft()); - } - } else { - result.setField(fieldOrSchemaName); - } + anyUtilsFactory.getInstance(anyTypeKind).getField(fieldOrSchemaName).ifPresentOrElse( + field -> result.setField(fieldOrSchemaName), + () -> Optional.ofNullable(find(fieldOrSchemaName)).ifPresent(schemaInfo -> { + result.setSchemaType(schemaInfo.getRight()); + result.setSchema(schemaInfo.getLeft()); + })); } @Transactional(readOnly = true) diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtils.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtils.java index 88963eaa43..45b22af1dd 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtils.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtils.java @@ -49,8 +49,8 @@ import org.apache.syncope.core.persistence.api.entity.DerSchema; import org.apache.syncope.core.persistence.api.entity.PlainAttr; import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.utils.FormatUtils; import org.apache.syncope.core.provisioning.api.DerAttrHandler; -import org.apache.syncope.core.provisioning.api.utils.FormatUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.ReflectionUtils; 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 acb0f76db5..fe8656b9b0 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 @@ -30,6 +30,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.lang.reflect.Field; import java.text.ParseException; import java.util.HashMap; import java.util.List; @@ -50,7 +51,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; -import org.springframework.util.ReflectionUtils; public class IntAttrNameParserTest extends AbstractTest { @@ -86,10 +86,14 @@ public void initMocks() throws NoSuchFieldException { return anyUtils; }); lenient().when(anyUtils.getField(anyString())).thenAnswer(ic -> { - String field = ic.getArgument(0); - return FIELDS.get(anyUtils.anyTypeKind()).contains(field) - ? ReflectionUtils.findField(getClass(), "anyUtils") - : null; + String fieldName = ic.getArgument(0); + if (FIELDS.get(anyUtils.anyTypeKind()).contains(fieldName)) { + Field field = mock(Field.class); + when(field.getName()).thenReturn(fieldName); + when(field.getType()).thenAnswer(ic2 -> String.class); + return Optional.of(field); + } + return Optional.empty(); }); lenient().when(plainSchemaDAO.findById(anyString())).thenAnswer(ic -> { String schemaName = ic.getArgument(0); diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/JexlUtilsTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java similarity index 98% rename from core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/JexlUtilsTest.java rename to core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java index 79678a16f3..82d8226ed2 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/JexlUtilsTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.provisioning.api.utils; +package org.apache.syncope.core.provisioning.api.jexl; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -45,7 +45,6 @@ import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.provisioning.api.AbstractTest; import org.apache.syncope.core.provisioning.api.DerAttrHandler; -import org.apache.syncope.core.provisioning.api.jexl.JexlUtils; import org.junit.jupiter.api.Test; import org.mockito.Mock; diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/ConnPoolConfUtilsTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/ConnPoolConfUtilsTest.java deleted file mode 100644 index cbf4dbf873..0000000000 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/utils/ConnPoolConfUtilsTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.provisioning.api.utils; - -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.Mockito.verify; - -import org.apache.syncope.common.lib.to.ConnPoolConfTO; -import org.apache.syncope.core.persistence.api.entity.ConnPoolConf; -import org.apache.syncope.core.provisioning.api.AbstractTest; -import org.identityconnectors.common.pooling.ObjectPoolConfiguration; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; - -public class ConnPoolConfUtilsTest extends AbstractTest { - - @Mock - private ConnPoolConf cpc; - - @Test - public void getConnPoolConf() { - ConnPoolConfTO cpcto = new ConnPoolConfTO(); - ConnPoolConfUtils.getConnPoolConf(cpcto, cpc); - verify(cpc).setMaxIdle(anyInt()); - verify(cpc).setMaxObjects(anyInt()); - verify(cpc).setMaxWait(anyLong()); - verify(cpc).setMinEvictableIdleTimeMillis(anyLong()); - verify(cpc).setMinIdle(anyInt()); - } - - @Test - public void updateObjectPoolConfiguration(final @Mock ObjectPoolConfiguration opc) { - ConnPoolConfUtils.updateObjectPoolConfiguration(opc, cpc); - verify(opc).setMaxIdle(anyInt()); - verify(opc).setMaxObjects(anyInt()); - verify(opc).setMaxWait(anyLong()); - verify(opc).setMinEvictableIdleTimeMillis(anyLong()); - verify(opc).setMinIdle(anyInt()); - } -} diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java index 0d4f12b60c..fcae6cc399 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java @@ -27,11 +27,11 @@ import java.util.concurrent.atomic.AtomicReference; import org.apache.syncope.common.lib.types.ConnectorCapability; import org.apache.syncope.core.persistence.api.entity.ConnInstance; +import org.apache.syncope.core.persistence.api.utils.ConnPoolConfUtils; import org.apache.syncope.core.provisioning.api.ConnIdBundleManager; import org.apache.syncope.core.provisioning.api.Connector; import org.apache.syncope.core.provisioning.api.TimeoutException; import org.apache.syncope.core.provisioning.api.pushpull.ReconFilterBuilder; -import org.apache.syncope.core.provisioning.api.utils.ConnPoolConfUtils; import org.apache.syncope.core.spring.ApplicationContextProvider; import org.identityconnectors.common.CollectionUtil; import org.identityconnectors.common.security.GuardedByteArray; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorLoader.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorLoader.java index b04e0b4342..215cbe887a 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorLoader.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorLoader.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.provisioning.java; -import javax.sql.DataSource; import org.apache.syncope.core.persistence.api.SyncopeCoreLoader; import org.apache.syncope.core.provisioning.api.ConnectorManager; import org.apache.syncope.core.spring.security.AuthContextUtils; @@ -37,7 +36,7 @@ public int getOrder() { } @Override - public void load(final String domain, final DataSource datasource) { + public void load(final String domain) { AuthContextUtils.runAsAdmin(domain, () -> connectorManager.load()); } } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAuditManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAuditManager.java index 78bec62ec9..3dbf589c6e 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAuditManager.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAuditManager.java @@ -31,10 +31,10 @@ import org.apache.syncope.common.lib.types.AuditLoggerName; import org.apache.syncope.core.persistence.api.dao.AuditConfDAO; import org.apache.syncope.core.persistence.api.entity.AuditConf; +import org.apache.syncope.core.persistence.api.utils.ExceptionUtils2; import org.apache.syncope.core.provisioning.api.AuditManager; import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; -import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnIdBundleManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnIdBundleManager.java index 7cddc0b221..c64bdac22a 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnIdBundleManager.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnIdBundleManager.java @@ -35,8 +35,8 @@ import org.apache.commons.lang3.tuple.Pair; 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.utils.URIUtils; import org.apache.syncope.core.provisioning.api.ConnIdBundleManager; -import org.apache.syncope.core.provisioning.api.utils.URIUtils; import org.identityconnectors.common.IOUtil; import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.framework.api.APIConfiguration; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java index ac4fb56469..449312ba27 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java @@ -34,11 +34,11 @@ import org.apache.syncope.core.persistence.api.entity.ConnInstance; 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.utils.ConnPoolConfUtils; import org.apache.syncope.core.provisioning.api.ConnIdBundleManager; import org.apache.syncope.core.provisioning.api.Connector; import org.apache.syncope.core.provisioning.api.ConnectorManager; import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder; -import org.apache.syncope.core.provisioning.api.utils.ConnPoolConfUtils; import org.apache.syncope.core.spring.ApplicationContextProvider; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.identityconnectors.common.l10n.CurrentLocale; @@ -151,8 +151,7 @@ public ConnInstance buildConnInstanceOverride( }); if (connInstance.getPoolConf() != null) { - override.setPoolConf( - ConnPoolConfUtils.getConnPoolConf(connInstance.getPoolConf(), entityFactory.newConnPoolConf())); + override.setPoolConf(ConnPoolConfUtils.getConnPoolConf(connInstance.getPoolConf())); } return override; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java index 20ca3ac9c8..697941a179 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java @@ -79,6 +79,7 @@ import org.apache.syncope.core.persistence.api.entity.user.LAPlainAttr; 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.persistence.api.utils.FormatUtils; import org.apache.syncope.core.provisioning.api.AccountGetter; import org.apache.syncope.core.provisioning.api.DerAttrHandler; import org.apache.syncope.core.provisioning.api.IntAttrName; @@ -90,7 +91,6 @@ import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheKey; import org.apache.syncope.core.provisioning.api.data.ItemTransformer; import org.apache.syncope.core.provisioning.api.jexl.JexlUtils; -import org.apache.syncope.core.provisioning.api.utils.FormatUtils; import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils; import org.apache.syncope.core.provisioning.java.utils.MappingUtils; import org.apache.syncope.core.spring.security.Encryptor; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java index 56f0764fd2..0cbb1be445 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java @@ -26,7 +26,7 @@ import javax.sql.DataSource; import org.apache.syncope.common.keymaster.client.api.ConfParamOps; import org.apache.syncope.core.persistence.api.DomainHolder; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; @@ -327,7 +327,7 @@ public SchedulerFactoryBean scheduler(final ApplicationContext ctx, final Provis @Bean public JobManager jobManager( final ProvisioningProperties provisioningProperties, - final DomainHolder domainHolder, + final DomainHolder domainHolder, final SecurityProperties securityProperties, final SchedulerFactoryBean scheduler, final TaskDAO taskDAO, @@ -709,7 +709,7 @@ public NotificationJobDelegate notificationJobDelegate( @Bean public NotificationJob notificationJob( final NotificationJobDelegate delegate, - final DomainHolder domainHolder, + final DomainHolder domainHolder, final SecurityProperties securityProperties) { return new NotificationJob(securityProperties, domainHolder, delegate); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java index 657abc7200..bddacb5dde 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java @@ -45,8 +45,8 @@ import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.common.lib.types.PatchOperation; import org.apache.syncope.common.lib.types.ResourceOperation; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidPlainAttrValueException; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidPlainAttrValueException; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AllowedSchemas; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractExecutableDatabinder.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractExecutableDatabinder.java index ab939e745a..d3d52ba6ba 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractExecutableDatabinder.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractExecutableDatabinder.java @@ -21,7 +21,7 @@ import java.time.OffsetDateTime; import java.util.Date; import java.util.Optional; -import org.apache.syncope.core.provisioning.api.utils.FormatUtils; +import org.apache.syncope.core.persistence.api.utils.FormatUtils; public abstract class AbstractExecutableDatabinder { diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java index 0f4ab32f0d..ba52f75893 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java @@ -38,7 +38,7 @@ import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.common.lib.types.PatchOperation; import org.apache.syncope.common.lib.types.ResourceOperation; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeClassDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeClassDataBinderImpl.java index b112bf3464..d8c3ccb362 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeClassDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeClassDataBinderImpl.java @@ -75,8 +75,7 @@ public void update(final AnyTypeClass anyTypeClass, final AnyTypeClassTO anyType anyTypeClass.setKey(anyTypeClassTO.getKey()); } - plainSchemaDAO.findByAnyTypeClasses(List.of(anyTypeClass)). - forEach(schema -> schema.setAnyTypeClass(null)); + plainSchemaDAO.findByAnyTypeClasses(List.of(anyTypeClass)).forEach(schema -> schema.setAnyTypeClass(null)); anyTypeClass.getPlainSchemas().clear(); anyTypeClassTO.getPlainSchemas().forEach(key -> { @@ -85,11 +84,11 @@ public void update(final AnyTypeClass anyTypeClass, final AnyTypeClassTO anyType LOG.debug("Invalid or already in use: {} {}, ignoring...", PlainSchema.class.getSimpleName(), key); } else { anyTypeClass.add(schema); + schema.setAnyTypeClass(anyTypeClass); } }); - derSchemaDAO.findByAnyTypeClasses(List.of(anyTypeClass)). - forEach((schema) -> schema.setAnyTypeClass(null)); + derSchemaDAO.findByAnyTypeClasses(List.of(anyTypeClass)).forEach(schema -> schema.setAnyTypeClass(null)); anyTypeClass.getDerSchemas().clear(); anyTypeClassTO.getDerSchemas().forEach(key -> { @@ -98,11 +97,11 @@ public void update(final AnyTypeClass anyTypeClass, final AnyTypeClassTO anyType LOG.debug("Invalid or already in use: {} {}, ignoring...", DerSchema.class.getSimpleName(), key); } else { anyTypeClass.add(schema); + schema.setAnyTypeClass(anyTypeClass); } }); - virSchemaDAO.findByAnyTypeClasses(List.of(anyTypeClass)). - forEach(schema -> schema.setAnyTypeClass(null)); + virSchemaDAO.findByAnyTypeClasses(List.of(anyTypeClass)).forEach(schema -> schema.setAnyTypeClass(null)); anyTypeClass.getVirSchemas().clear(); anyTypeClassTO.getVirSchemas().forEach(key -> { @@ -111,6 +110,7 @@ public void update(final AnyTypeClass anyTypeClass, final AnyTypeClassTO anyType LOG.debug("Invalid or already in use: {} {}, ignoring...", VirSchema.class.getSimpleName(), key); } else { anyTypeClass.add(schema); + schema.setAnyTypeClass(anyTypeClass); } }); } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java index 007f19a187..e4bcf32398 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java @@ -25,7 +25,6 @@ import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.lib.SyncopeClientException; import org.apache.syncope.common.lib.to.ConnInstanceTO; -import org.apache.syncope.common.lib.to.ConnPoolConfTO; import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.common.lib.types.ConnConfPropSchema; import org.apache.syncope.common.lib.types.ConnConfProperty; @@ -35,9 +34,9 @@ import org.apache.syncope.core.persistence.api.entity.ConnInstance; 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.utils.ConnPoolConfUtils; import org.apache.syncope.core.provisioning.api.ConnIdBundleManager; import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder; -import org.apache.syncope.core.provisioning.api.utils.ConnPoolConfUtils; import org.identityconnectors.framework.api.ConfigurationProperties; import org.identityconnectors.framework.api.ConfigurationProperty; import org.identityconnectors.framework.api.ConnectorInfo; @@ -114,8 +113,7 @@ public ConnInstance getConnInstance(final ConnInstanceTO connInstanceTO) { } connInstance.setConf(connInstanceTO.getConf()); if (connInstanceTO.getPoolConf() != null) { - connInstance.setPoolConf( - ConnPoolConfUtils.getConnPoolConf(connInstanceTO.getPoolConf(), entityFactory.newConnPoolConf())); + connInstance.setPoolConf(ConnPoolConfUtils.getConnPoolConf(connInstanceTO.getPoolConf())); } // Throw exception if there is at least one element set @@ -177,8 +175,7 @@ public ConnInstance update(final ConnInstanceTO connInstanceTO) { if (connInstanceTO.getPoolConf() == null) { connInstance.setPoolConf(null); } else { - connInstance.setPoolConf( - ConnPoolConfUtils.getConnPoolConf(connInstanceTO.getPoolConf(), entityFactory.newConnPoolConf())); + connInstance.setPoolConf(ConnPoolConfUtils.getConnPoolConf(connInstanceTO.getPoolConf())); } return connInstance; @@ -251,22 +248,7 @@ public ConnInstanceTO getConnInstanceTO(final ConnInstance connInstance) { Collections.sort(connInstanceTO.getConf()); - // pool configuration - if (connInstance.getPoolConf() != null - && (connInstance.getPoolConf().getMaxIdle() != null - || connInstance.getPoolConf().getMaxObjects() != null - || connInstance.getPoolConf().getMaxWait() != null - || connInstance.getPoolConf().getMinEvictableIdleTimeMillis() != null - || connInstance.getPoolConf().getMinIdle() != null)) { - - ConnPoolConfTO poolConf = new ConnPoolConfTO(); - poolConf.setMaxIdle(connInstance.getPoolConf().getMaxIdle()); - poolConf.setMaxObjects(connInstance.getPoolConf().getMaxObjects()); - poolConf.setMaxWait(connInstance.getPoolConf().getMaxWait()); - poolConf.setMinEvictableIdleTimeMillis(connInstance.getPoolConf().getMinEvictableIdleTimeMillis()); - poolConf.setMinIdle(connInstance.getPoolConf().getMinIdle()); - connInstanceTO.setPoolConf(poolConf); - } + connInstanceTO.setPoolConf(connInstance.getPoolConf()); return connInstanceTO; } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java index 4459a51ea8..89534f5fa4 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java @@ -34,7 +34,7 @@ import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.common.lib.types.ResourceOperation; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java index 18a9b792ac..bae33cd75d 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java @@ -25,13 +25,11 @@ import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.RealmDAO; import org.apache.syncope.core.persistence.api.dao.RoleDAO; -import org.apache.syncope.core.persistence.api.dao.search.SearchCond; import org.apache.syncope.core.persistence.api.entity.DynRealm; import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.Privilege; 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.DynRoleMembership; import org.apache.syncope.core.persistence.api.search.SearchCondConverter; import org.apache.syncope.core.persistence.api.search.SearchCondVisitor; import org.apache.syncope.core.provisioning.api.data.RoleDataBinder; @@ -70,25 +68,6 @@ public RoleDataBinderImpl( this.searchCondVisitor = searchCondVisitor; } - protected void setDynMembership(final Role role, final String dynMembershipFIQL) { - SearchCond dynMembershipCond = SearchCondConverter.convert(searchCondVisitor, dynMembershipFIQL); - if (!dynMembershipCond.isValid()) { - SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchParameters); - sce.getElements().add(dynMembershipFIQL); - throw sce; - } - - DynRoleMembership dynMembership; - if (role.getDynMembership() == null) { - dynMembership = entityFactory.newEntity(DynRoleMembership.class); - dynMembership.setRole(role); - role.setDynMembership(dynMembership); - } else { - dynMembership = role.getDynMembership(); - } - dynMembership.setFIQLCond(dynMembershipFIQL); - } - @Override public Role create(final RoleTO roleTO) { return update(entityFactory.newEntity(Role.class), roleTO); @@ -118,20 +97,7 @@ public Role update(final Role toBeUpdated, final RoleTO roleTO) { role = roleDAO.save(role); - // dynamic membership - roleDAO.clearDynMembers(role); - if (role.getKey() == null && roleTO.getDynMembershipCond() != null) { - setDynMembership(role, roleTO.getDynMembershipCond()); - } else if (role.getDynMembership() != null && roleTO.getDynMembershipCond() == null) { - role.setDynMembership(null); - } else if (role.getDynMembership() == null && roleTO.getDynMembershipCond() != null) { - setDynMembership(role, roleTO.getDynMembershipCond()); - } else if (role.getDynMembership() != null && roleTO.getDynMembershipCond() != null - && !role.getDynMembership().getFIQLCond().equals(roleTO.getDynMembershipCond())) { - - setDynMembership(role, roleTO.getDynMembershipCond()); - } - + // privileges role.getPrivileges().clear(); for (String key : roleTO.getPrivileges()) { applicationDAO.findPrivilege(key).ifPresentOrElse( @@ -139,6 +105,20 @@ public Role update(final Role toBeUpdated, final RoleTO roleTO) { () -> LOG.debug("Invalid privilege {}, ignoring", key)); } + // dynamic membership + roleDAO.clearDynMembers(role); + if (roleTO.getDynMembershipCond() == null) { + role.setDynMembershipCond(null); + } else { + if (!SearchCondConverter.convert(searchCondVisitor, roleTO.getDynMembershipCond()).isValid()) { + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchParameters); + sce.getElements().add(roleTO.getDynMembershipCond()); + throw sce; + } + + role.setDynMembershipCond(roleTO.getDynMembershipCond()); + } + return roleDAO.saveAndRefreshDynMemberships(role); } @@ -149,18 +129,13 @@ public RoleTO getRoleTO(final Role role) { roleTO.setKey(role.getKey()); roleTO.getEntitlements().addAll(role.getEntitlements()); - roleTO.getRealms().addAll(role.getRealms().stream(). - map(Realm::getFullPath).toList()); + roleTO.getRealms().addAll(role.getRealms().stream().map(Realm::getFullPath).toList()); - roleTO.getDynRealms().addAll(role.getDynRealms().stream(). - map(DynRealm::getKey).toList()); + roleTO.getDynRealms().addAll(role.getDynRealms().stream().map(DynRealm::getKey).toList()); - if (role.getDynMembership() != null) { - roleTO.setDynMembershipCond(role.getDynMembership().getFIQLCond()); - } + roleTO.setDynMembershipCond(role.getDynMembershipCond()); - roleTO.getPrivileges().addAll(role.getPrivileges().stream(). - map(Privilege::getKey).toList()); + roleTO.getPrivileges().addAll(role.getPrivileges().stream().map(Privilege::getKey).toList()); return roleTO; } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java index cad8ad01e3..86df6cb15d 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java @@ -48,7 +48,7 @@ import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.common.lib.types.PatchOperation; import org.apache.syncope.common.lib.types.ResourceOperation; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java index 66017c62df..d51763dfa3 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java @@ -27,13 +27,13 @@ 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.TaskUtilsFactory; +import org.apache.syncope.core.persistence.api.utils.ExceptionUtils2; import org.apache.syncope.core.provisioning.api.AuditManager; import org.apache.syncope.core.provisioning.api.data.TaskDataBinder; import org.apache.syncope.core.provisioning.api.event.JobStatusEvent; import org.apache.syncope.core.provisioning.api.job.JobManager; import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate; import org.apache.syncope.core.provisioning.api.notification.NotificationManager; -import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.core.spring.security.SecureRandomUtils; import org.apache.syncope.core.spring.security.SecurityProperties; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java index b20e3a5379..7a9e8ed468 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java @@ -18,10 +18,6 @@ */ package org.apache.syncope.core.provisioning.java.job; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; import java.time.OffsetDateTime; import java.util.Date; import java.util.HashMap; @@ -57,7 +53,6 @@ import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.core.spring.security.SecurityProperties; -import org.identityconnectors.common.IOUtil; import org.quartz.CronScheduleBuilder; import org.quartz.Job; import org.quartz.JobBuilder; @@ -71,7 +66,7 @@ import org.quartz.impl.jdbcjobstore.Constants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.jdbc.datasource.DataSourceUtils; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import org.springframework.transaction.annotation.Transactional; @@ -79,7 +74,7 @@ public class DefaultJobManager implements JobManager, SyncopeCoreLoader { protected static final Logger LOG = LoggerFactory.getLogger(JobManager.class); - protected final DomainHolder domainHolder; + protected final DomainHolder domainHolder; protected final SchedulerFactoryBean scheduler; @@ -98,7 +93,7 @@ public class DefaultJobManager implements JobManager, SyncopeCoreLoader { protected boolean disableQuartzInstance; public DefaultJobManager( - final DomainHolder domainHolder, + final DomainHolder domainHolder, final SchedulerFactoryBean scheduler, final TaskDAO taskDAO, final ReportDAO reportDAO, @@ -131,26 +126,19 @@ protected boolean isRunningElsewhere(final JobKey jobKey) throws SchedulerExcept return false; } - DataSource dataSource = domainHolder.getDomains().get(SyncopeConstants.MASTER_DOMAIN); - Connection conn = DataSourceUtils.getConnection(dataSource); - PreparedStatement stmt = null; - ResultSet resultSet = null; - try { - stmt = conn.prepareStatement( + Object v = domainHolder.getDomains().get(SyncopeConstants.MASTER_DOMAIN); + if (v instanceof DataSource dataSource) { + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); + return jdbcTemplate.queryForObject( "SELECT 1 FROM " + Constants.DEFAULT_TABLE_PREFIX + "FIRED_TRIGGERS " - + "WHERE JOB_NAME = ? AND JOB_GROUP = ?"); - stmt.setString(1, jobKey.getName()); - stmt.setString(2, jobKey.getGroup()); - - resultSet = stmt.executeQuery(); - return resultSet.next(); - } catch (SQLException e) { - throw new SchedulerException(e); - } finally { - IOUtil.quietClose(resultSet); - IOUtil.quietClose(stmt); - DataSourceUtils.releaseConnection(conn, dataSource); + + "WHERE JOB_NAME = ? AND JOB_GROUP = ?", + Boolean.class, + jobKey.getName(), + jobKey.getGroup()); } + + LOG.warn("Unsupported persistence source: " + v.getClass().getName()); + return false; } @Override @@ -291,7 +279,7 @@ public int getOrder() { @Transactional @Override - public void load(final String domain, final DataSource datasource) { + public void load(final String domain) { if (disableQuartzInstance) { String instanceId = "AUTO"; try { diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java index 42d39ae5ac..1e493b8d9e 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java @@ -51,7 +51,7 @@ public enum Status { private ImplementationDAO implementationDAO; @Autowired - private DomainHolder domainHolder; + private DomainHolder domainHolder; private SchedTaskJobDelegate delegate; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/AbstractNotificationJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/AbstractNotificationJobDelegate.java index ffd915d1d0..f1462388d6 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/AbstractNotificationJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/AbstractNotificationJobDelegate.java @@ -28,12 +28,12 @@ import org.apache.syncope.core.persistence.api.entity.task.NotificationTask; import org.apache.syncope.core.persistence.api.entity.task.TaskExec; import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory; +import org.apache.syncope.core.persistence.api.utils.ExceptionUtils2; import org.apache.syncope.core.provisioning.api.AuditManager; import org.apache.syncope.core.provisioning.api.event.JobStatusEvent; 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.notification.NotificationManager; -import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.quartz.JobExecutionException; import org.slf4j.Logger; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java index c6ce74a802..4d43eaa9a2 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJob.java @@ -51,13 +51,13 @@ public enum Status { protected final SecurityProperties securityProperties; - protected final DomainHolder domainHolder; + protected final DomainHolder domainHolder; protected final NotificationJobDelegate delegate; public NotificationJob( final SecurityProperties securityProperties, - final DomainHolder domainHolder, + final DomainHolder domainHolder, final NotificationJobDelegate delegate) { this.securityProperties = securityProperties; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportJobDelegate.java index fc0fe2c5ff..2081dfdf17 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AbstractReportJobDelegate.java @@ -33,13 +33,13 @@ import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.Report; import org.apache.syncope.core.persistence.api.entity.ReportExec; +import org.apache.syncope.core.persistence.api.utils.ExceptionUtils2; import org.apache.syncope.core.provisioning.api.AuditManager; import org.apache.syncope.core.provisioning.api.data.ReportDataBinder; import org.apache.syncope.core.provisioning.api.event.JobStatusEvent; import org.apache.syncope.core.provisioning.api.job.JobManager; import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate; import org.apache.syncope.core.provisioning.api.notification.NotificationManager; -import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.core.spring.security.SecurityProperties; import org.quartz.JobExecutionContext; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java index 57e2a49acd..c5afc00247 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java @@ -58,7 +58,7 @@ public enum Status { private ImplementationDAO implementationDAO; @Autowired - private DomainHolder domainHolder; + private DomainHolder domainHolder; private ReportJobDelegate delegate; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java index 594283e8a7..0ab8a48a85 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java @@ -41,7 +41,7 @@ import org.apache.syncope.common.lib.types.ResourceOperation; import org.apache.syncope.common.lib.types.TaskType; import org.apache.syncope.common.lib.types.TraceLevel; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +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.NotFoundException; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; @@ -54,6 +54,7 @@ import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; import org.apache.syncope.core.persistence.api.entity.task.TaskExec; import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory; +import org.apache.syncope.core.persistence.api.utils.ExceptionUtils2; import org.apache.syncope.core.provisioning.api.AuditManager; import org.apache.syncope.core.provisioning.api.Connector; import org.apache.syncope.core.provisioning.api.ConnectorManager; @@ -66,7 +67,6 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter; import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor; import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo; -import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2; import org.apache.syncope.core.provisioning.java.pushpull.OutboundMatcher; import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils; import org.apache.syncope.core.provisioning.java.utils.MappingUtils; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java index e7e772b710..aba7bf5058 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java @@ -27,7 +27,7 @@ import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.Future; import org.apache.syncope.common.lib.types.ExecStatus; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +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; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java index aa031816ef..119135f72c 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java @@ -571,14 +571,14 @@ protected Result delete(final SyncDelta delta, final List realms) AnyCond keyCond = new AnyCond(AttrCond.Type.ISNOTNULL); keyCond.setSchema("key"); SearchCond allMatchingCond = SearchCond.getLeaf(keyCond); - int users = searchDAO.count( + long users = searchDAO.count( realmDAO.getRoot(), true, adminRealms, allMatchingCond, AnyTypeKind.USER); - int groups = searchDAO.count( + long groups = searchDAO.count( realmDAO.getRoot(), true, adminRealms, allMatchingCond, AnyTypeKind.GROUP); - int anyObjects = searchDAO.count( + long anyObjects = searchDAO.count( realmDAO.getRoot(), true, adminRealms, allMatchingCond, AnyTypeKind.ANY_OBJECT); - int macroTasks = taskDAO.findByRealm(realm).size(); - int clientApps = casSPClientAppDAO.findAllByRealm(realm).size() + long macroTasks = taskDAO.findByRealm(realm).size(); + long clientApps = casSPClientAppDAO.findAllByRealm(realm).size() + saml2SPClientAppDAO.findAllByRealm(realm).size() + oidcRPClientAppDAO.findAllByRealm(realm).size(); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java index 78e352dba3..2f499514ad 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java @@ -32,7 +32,7 @@ import org.apache.syncope.common.lib.to.Provision; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.MatchType; -import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException; +import org.apache.syncope.core.persistence.api.attrvalue.ParsingValidationException; 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.GroupDAO; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java index e286552502..f57d1a3e1c 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java @@ -35,7 +35,7 @@ import org.apache.syncope.common.lib.to.Provision; import org.apache.syncope.common.lib.types.ConflictResolutionAction; import org.apache.syncope.common.lib.types.ResourceOperation; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; 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.PlainSchemaDAO; 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 9f7911d5aa..90285e473a 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 @@ -255,7 +255,7 @@ protected String doExecuteProvisioning( SearchCond cond = StringUtils.isBlank(filter) ? anyDAO.getAllMatchingCond() : SearchCondConverter.convert(searchCondVisitor, filter); - int count = searchDAO.count( + long count = searchDAO.count( profile.getTask().getSourceRealm(), true, Set.of(profile.getTask().getSourceRealm().getFullPath()), diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java index fba2654b40..ab2cacaf97 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java @@ -106,7 +106,7 @@ private Provision provision( provision.setMapping(mapping); AnyUtils anyUtils = anyUtilsFactory.getInstance(anyType.getKind()); - if (anyUtils.getField(keyColumn) == null) { + if (anyUtils.getField(keyColumn).isEmpty()) { plainSchemaDAO.findById(keyColumn). orElseThrow(() -> new JobExecutionException("Plain Schema for key column not found: " + keyColumn)); } @@ -118,7 +118,7 @@ private Provision provision( mapping.setConnObjectKeyItem(connObjectKeyItem); columns.stream(). - filter(column -> anyUtils.getField(column) != null + filter(column -> anyUtils.getField(column).isPresent() || plainSchemaDAO.existsById(column) || virSchemaDAO.existsById(column)). map(column -> { Item item = new Item(); 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 f82c29aca6..6e6493a32d 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 @@ -30,7 +30,7 @@ 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.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ProvisioningTestContext.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ProvisioningTestContext.java index 12d4825006..23636cd7fd 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ProvisioningTestContext.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ProvisioningTestContext.java @@ -22,7 +22,6 @@ import org.apache.syncope.common.keymaster.client.api.ConfParamOps; import org.apache.syncope.common.keymaster.client.api.DomainOps; -import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.DomainRegistry; import org.apache.syncope.core.persistence.api.content.ContentLoader; import org.apache.syncope.core.persistence.jpa.MasterDomain; @@ -47,11 +46,10 @@ public class ProvisioningTestContext { @Bean public TestInitializer testInitializer( final StartupDomainLoader domainLoader, - final DomainHolder domainHolder, final ContentLoader contentLoader, final ConfigurableApplicationContext ctx) { - return new TestInitializer(domainLoader, domainHolder, contentLoader, ctx); + return new TestInitializer(domainLoader, contentLoader, ctx); } @Bean diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/TestInitializer.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/TestInitializer.java index b6e9c7b733..6f5828d8dd 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/TestInitializer.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/TestInitializer.java @@ -19,7 +19,6 @@ package org.apache.syncope.core.provisioning.java; import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.content.ContentLoader; import org.apache.syncope.core.persistence.jpa.StartupDomainLoader; import org.apache.syncope.core.spring.ApplicationContextProvider; @@ -32,20 +31,16 @@ public class TestInitializer implements InitializingBean { private final StartupDomainLoader domainLoader; - private final DomainHolder domainHolder; - private final ContentLoader contentLoader; private final ConfigurableApplicationContext ctx; public TestInitializer( final StartupDomainLoader domainLoader, - final DomainHolder domainHolder, final ContentLoader contentLoader, final ConfigurableApplicationContext ctx) { this.domainLoader = domainLoader; - this.domainHolder = domainHolder; this.contentLoader = contentLoader; this.ctx = ctx; } @@ -61,8 +56,6 @@ public void afterPropertiesSet() throws Exception { domainLoader.load(); - contentLoader.load( - SyncopeConstants.MASTER_DOMAIN, - domainHolder.getDomains().get(SyncopeConstants.MASTER_DOMAIN)); + contentLoader.load(SyncopeConstants.MASTER_DOMAIN); } } 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 bb40f3767a..b65fd6a9bd 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 @@ -31,7 +31,7 @@ import org.apache.syncope.common.lib.request.MembershipUR; import org.apache.syncope.common.lib.request.UserUR; import org.apache.syncope.common.lib.types.IdRepoEntitlement; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; +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.UMembership; import org.apache.syncope.core.persistence.api.entity.user.User; diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java index b5faab6af0..71e50c04aa 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java @@ -54,10 +54,10 @@ 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.user.User; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; 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.apache.syncope.core.provisioning.api.utils.RealmUtils; import org.identityconnectors.framework.common.objects.Uid; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -79,8 +79,6 @@ public class AuthDataAccessor { protected static final Logger LOG = LoggerFactory.getLogger(AuthDataAccessor.class); - public static final String GROUP_OWNER_ROLE = "GROUP_OWNER"; - protected static final Encryptor ENCRYPTOR = Encryptor.getInstance(); protected static final Set ANONYMOUS_AUTHORITIES = @@ -341,7 +339,7 @@ protected Set getUserAuthorities(final User user) { // Give entitlements as assigned by roles (with static or dynamic realms, where applicable) - assigned // either statically and dynamically userDAO.findAllRoles(user).stream(). - filter(role -> !GROUP_OWNER_ROLE.equals(role.getKey())). + filter(role -> !RoleDAO.GROUP_OWNER_ROLE.equals(role.getKey())). forEach(role -> role.getEntitlements().forEach(entitlement -> { Set realms = Optional.ofNullable(entForRealms.get(entitlement)).orElseGet(() -> { Set r = new HashSet<>(); @@ -356,7 +354,8 @@ protected Set getUserAuthorities(final User user) { })); // Give group entitlements for owned groups - groupDAO.findOwnedByUser(user.getKey()).forEach(group -> roleDAO.findById(GROUP_OWNER_ROLE).ifPresentOrElse( + groupDAO.findOwnedByUser(user.getKey()). + forEach(group -> roleDAO.findById(RoleDAO.GROUP_OWNER_ROLE).ifPresentOrElse( groupOwnerRole -> groupOwnerRole.getEntitlements().forEach(entitlement -> { Set realms = Optional.ofNullable(entForRealms.get(entitlement)).orElseGet(() -> { HashSet r = new HashSet<>(); @@ -366,7 +365,7 @@ protected Set getUserAuthorities(final User user) { realms.add(RealmUtils.getGroupOwnerRealm(group.getRealm().getFullPath(), group.getKey())); }), - () -> LOG.warn("Role {} was not found", GROUP_OWNER_ROLE))); + () -> LOG.warn("Role {} was not found", RoleDAO.GROUP_OWNER_ROLE))); return buildAuthorities(entForRealms); } @@ -374,7 +373,7 @@ protected Set getUserAuthorities(final User user) { protected Set getDelegatedAuthorities(final Delegation delegation) { Map> entForRealms = new HashMap<>(); - delegation.getRoles().stream().filter(role -> !GROUP_OWNER_ROLE.equals(role.getKey())). + delegation.getRoles().stream().filter(role -> !RoleDAO.GROUP_OWNER_ROLE.equals(role.getKey())). forEach(role -> role.getEntitlements().forEach(entitlement -> { Set realms = Optional.ofNullable(entForRealms.get(entitlement)).orElseGet(() -> { HashSet r = new HashSet<>(); diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeGrantedAuthority.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeGrantedAuthority.java index 4dc5693904..d10b72c476 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeGrantedAuthority.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SyncopeGrantedAuthority.java @@ -28,7 +28,7 @@ import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.syncope.core.provisioning.api.utils.RealmUtils; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; import org.springframework.security.core.GrantedAuthority; public class SyncopeGrantedAuthority implements GrantedAuthority { diff --git a/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreApplication.java b/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreApplication.java index 47a6bfe901..2fb90fb940 100644 --- a/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreApplication.java +++ b/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreApplication.java @@ -111,13 +111,13 @@ public TaskExecutorUnloader taskExecutorUnloader(final ListableBeanFactory beanF @ConditionalOnMissingBean @Bean - public SyncopeCoreStart keymasterStart(final DomainHolder domainHolder) { + public SyncopeCoreStart keymasterStart(final DomainHolder domainHolder) { return new SyncopeCoreStart(domainHolder); } @ConditionalOnMissingBean @Bean - public KeymasterStop keymasterStop(final DomainHolder domainHolder) { + public KeymasterStop keymasterStop(final DomainHolder domainHolder) { return new SyncopeCoreStop(domainHolder); } @@ -162,7 +162,7 @@ public SyncopeCoreInfoContributor syncopeCoreInfoContributor( @ConditionalOnMissingBean @Bean - public DomainsHealthIndicator domainsHealthIndicator(final DomainHolder domainHolder) { + public DomainsHealthIndicator domainsHealthIndicator(final DomainHolder domainHolder) { return new DomainsHealthIndicator(domainHolder); } diff --git a/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreStart.java b/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreStart.java index 35dc2b9d7a..d9d48e7d78 100644 --- a/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreStart.java +++ b/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreStart.java @@ -38,9 +38,9 @@ public class SyncopeCoreStart extends KeymasterStart implements Ordered { private static final Logger LOG = LoggerFactory.getLogger(SyncopeCoreStart.class); - private final DomainHolder domainHolder; + private final DomainHolder domainHolder; - public SyncopeCoreStart(final DomainHolder domainHolder) { + public SyncopeCoreStart(final DomainHolder domainHolder) { super(NetworkService.Type.CORE); this.domainHolder = domainHolder; } @@ -61,10 +61,10 @@ public void onApplicationEvent(final ContextRefreshedEvent event) { loader.load(); - domainHolder.getDomains().forEach((domain, datasource) -> { + domainHolder.getDomains().keySet().forEach(domain -> { LOG.debug("[{}] Starting init on domain '{}'", loaderName, domain); - AuthContextUtils.runAsAdmin(domain, () -> loader.load(domain, datasource)); + AuthContextUtils.runAsAdmin(domain, () -> loader.load(domain)); LOG.debug("[{}] Init completed on domain '{}'", loaderName, domain); }); diff --git a/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreStop.java b/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreStop.java index d55afc79fd..2a10985ac7 100644 --- a/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreStop.java +++ b/core/starter/src/main/java/org/apache/syncope/core/starter/SyncopeCoreStop.java @@ -36,9 +36,9 @@ public class SyncopeCoreStop extends KeymasterStop implements Ordered { private static final Logger LOG = LoggerFactory.getLogger(SyncopeCoreStop.class); - private final DomainHolder domainHolder; + private final DomainHolder domainHolder; - public SyncopeCoreStop(final DomainHolder domainHolder) { + public SyncopeCoreStop(final DomainHolder domainHolder) { super(NetworkService.Type.CORE); this.domainHolder = domainHolder; } diff --git a/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/DomainsHealthIndicator.java b/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/DomainsHealthIndicator.java index 53e2d8d2a8..ff3143da20 100644 --- a/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/DomainsHealthIndicator.java +++ b/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/DomainsHealthIndicator.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.starter.actuate; -import java.sql.Connection; import java.util.concurrent.atomic.AtomicReference; import org.apache.syncope.core.persistence.api.DomainHolder; import org.slf4j.Logger; @@ -26,15 +25,14 @@ import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.actuate.health.Status; -import org.springframework.jdbc.datasource.DataSourceUtils; public class DomainsHealthIndicator implements HealthIndicator { protected static final Logger LOG = LoggerFactory.getLogger(DomainsHealthIndicator.class); - protected final DomainHolder domainHolder; + protected final DomainHolder domainHolder; - public DomainsHealthIndicator(final DomainHolder domainHolder) { + public DomainsHealthIndicator(final DomainHolder domainHolder) { this.domainHolder = domainHolder; } @@ -43,25 +41,9 @@ public Health health() { Health.Builder builder = new Health.Builder(); AtomicReference anyDown = new AtomicReference<>(Boolean.FALSE); - - domainHolder.getDomains().forEach((key, ds) -> { - Status status; - - Connection conn = null; - try { - conn = DataSourceUtils.getConnection(ds); - status = conn.isValid(0) ? Status.UP : Status.OUT_OF_SERVICE; - } catch (Exception e) { - status = Status.DOWN; - LOG.debug("When attempting to connect to Domain {}", key, e); - } finally { - if (conn != null) { - DataSourceUtils.releaseConnection(conn, ds); - } - } - - builder.withDetail(key, status); - if (status != Status.UP) { + domainHolder.getHealthInfo().forEach((domain, status) -> { + builder.withDetail(domain, status ? Status.UP : Status.DOWN); + if (!status) { anyDown.set(true); } }); diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java index deb52c252c..08c7dc8967 100644 --- a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java +++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java @@ -31,7 +31,7 @@ import org.apache.syncope.common.lib.types.EntityViolationType; import org.apache.syncope.common.lib.types.IdRepoEntitlement; import org.apache.syncope.common.lib.types.ResourceOperation; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; 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; 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 99974d9d81..4b1dbf489d 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 @@ -27,7 +27,7 @@ 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.validation.InvalidEntityException; +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; diff --git a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/TestInitializer.java b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/TestInitializer.java index 808c0bc232..ef4179077c 100644 --- a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/TestInitializer.java +++ b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/TestInitializer.java @@ -19,7 +19,6 @@ package org.apache.syncope.core.workflow.java; import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.content.ContentLoader; import org.apache.syncope.core.persistence.jpa.StartupDomainLoader; import org.apache.syncope.core.spring.ApplicationContextProvider; @@ -32,20 +31,16 @@ public class TestInitializer implements InitializingBean { private final StartupDomainLoader domainLoader; - private final DomainHolder domainHolder; - private final ContentLoader contentLoader; private final ConfigurableApplicationContext ctx; public TestInitializer( final StartupDomainLoader domainLoader, - final DomainHolder domainHolder, final ContentLoader contentLoader, final ConfigurableApplicationContext ctx) { this.domainLoader = domainLoader; - this.domainHolder = domainHolder; this.contentLoader = contentLoader; this.ctx = ctx; } @@ -61,8 +56,6 @@ public void afterPropertiesSet() throws Exception { domainLoader.load(); - contentLoader.load( - SyncopeConstants.MASTER_DOMAIN, - domainHolder.getDomains().get(SyncopeConstants.MASTER_DOMAIN)); + contentLoader.load(SyncopeConstants.MASTER_DOMAIN); } } 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 3bb6f16ced..cb295070a2 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 @@ -27,7 +27,6 @@ import org.apache.syncope.common.keymaster.client.api.DomainOps; import org.apache.syncope.common.lib.request.UserCR; import org.apache.syncope.common.lib.types.CipherAlgorithm; -import org.apache.syncope.core.persistence.api.DomainHolder; 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.RealmDAO; @@ -54,11 +53,10 @@ public class WorkflowTestContext { @Bean public TestInitializer testInitializer( final StartupDomainLoader domainLoader, - final DomainHolder domainHolder, final ContentLoader contentLoader, final ConfigurableApplicationContext ctx) { - return new TestInitializer(domainLoader, domainHolder, contentLoader, ctx); + return new TestInitializer(domainLoader, contentLoader, ctx); } @Bean diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexLoader.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexLoader.java index 93e55ed7b5..d8bb181d0e 100644 --- a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexLoader.java +++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexLoader.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.ext.elasticsearch.client; -import javax.sql.DataSource; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.core.persistence.api.SyncopeCoreLoader; import org.slf4j.Logger; @@ -41,7 +40,7 @@ public int getOrder() { } @Override - public void load(final String domain, final DataSource datasource) { + public void load(final String domain) { try { if (!indexManager.existsAnyIndex(domain, AnyTypeKind.USER)) { indexManager.createAnyIndex(domain, AnyTypeKind.USER, diff --git a/ext/elasticsearch/logic/src/main/java/org/apache/syncope/core/logic/audit/ElasticsearchLogicContext.java b/ext/elasticsearch/logic/src/main/java/org/apache/syncope/core/logic/audit/ElasticsearchLogicContext.java index 09df992c1a..9280329f55 100644 --- a/ext/elasticsearch/logic/src/main/java/org/apache/syncope/core/logic/audit/ElasticsearchLogicContext.java +++ b/ext/elasticsearch/logic/src/main/java/org/apache/syncope/core/logic/audit/ElasticsearchLogicContext.java @@ -40,13 +40,13 @@ public class ElasticsearchLogicContext { @ConditionalOnMissingBean(name = { "defaultAuditAppenders", "elasticsearchDefaultAuditAppenders" }) @Bean public List defaultAuditAppenders( - final DomainHolder domainHolder, + final DomainHolder domainHolder, final ElasticsearchIndexManager elasticsearchIndexManager) { List auditAppenders = new ArrayList<>(); LoggerContext logCtx = (LoggerContext) LogManager.getContext(false); - domainHolder.getDomains().forEach((domain, dataSource) -> { + domainHolder.getDomains().keySet().forEach(domain -> { AuditAppender appender = new ElasticsearchAuditAppender(domain, elasticsearchIndexManager); LoggerConfig logConf = new LoggerConfig(AuditLoggerName.getAuditLoggerName(domain), null, false); diff --git a/ext/elasticsearch/persistence-jpa/pom.xml b/ext/elasticsearch/persistence/pom.xml similarity index 86% rename from ext/elasticsearch/persistence-jpa/pom.xml rename to ext/elasticsearch/persistence/pom.xml index 038b9907a0..e500ee00b2 100644 --- a/ext/elasticsearch/persistence-jpa/pom.xml +++ b/ext/elasticsearch/persistence/pom.xml @@ -27,10 +27,10 @@ under the License. 4.0.0-SNAPSHOT - Apache Syncope Ext: Elasticsearch Persistence JPA - Apache Syncope Ext: Elasticsearch Persistence JPA + Apache Syncope Ext: Elasticsearch Persistence + Apache Syncope Ext: Elasticsearch Persistence org.apache.syncope.ext.elasticsearch - syncope-ext-elasticsearch-persistence-jpa + syncope-ext-elasticsearch-persistence jar @@ -40,7 +40,7 @@ under the License. org.apache.syncope.core - syncope-core-persistence-jpa + syncope-core-persistence-common ${project.version} @@ -51,6 +51,12 @@ under the License. + + org.apache.syncope.core + syncope-core-persistence-jpa + ${project.version} + test + org.slf4j slf4j-simple diff --git a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ElasticsearchPersistenceContext.java b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/ElasticsearchPersistenceContext.java similarity index 60% rename from ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ElasticsearchPersistenceContext.java rename to ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/ElasticsearchPersistenceContext.java index e59e349b88..200e2d0eac 100644 --- a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ElasticsearchPersistenceContext.java +++ b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/ElasticsearchPersistenceContext.java @@ -19,31 +19,26 @@ package org.apache.syncope.core.persistence.jpa; import co.elastic.clients.elasticsearch.ElasticsearchClient; -import jakarta.persistence.EntityManager; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; 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.AuditConfDAO; +import org.apache.syncope.core.persistence.api.dao.AuditEntryDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; 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.RealmDAO; -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.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAnySearchDAO; -import org.apache.syncope.core.persistence.jpa.dao.repo.AuditConfRepo; -import org.apache.syncope.core.persistence.jpa.dao.repo.AuditConfRepoExtElasticsearchImpl; -import org.apache.syncope.core.persistence.jpa.dao.repo.RealmRepo; -import org.apache.syncope.core.persistence.jpa.dao.repo.RealmRepoExtElasticsearchImpl; +import org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAuditEntryDAO; +import org.apache.syncope.core.persistence.jpa.dao.ElasticsearchRealmDAO; import org.apache.syncope.ext.elasticsearch.client.ElasticsearchProperties; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; -import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; @Configuration(proxyBeanMethods = false) public class ElasticsearchPersistenceContext { @@ -51,7 +46,6 @@ public class ElasticsearchPersistenceContext { @ConditionalOnMissingBean(name = "elasticsearchAnySearchDAO") @Bean public AnySearchDAO anySearchDAO( - final ElasticsearchProperties props, final RealmDAO realmDAO, final @Lazy DynRealmDAO dynRealmDAO, final @Lazy UserDAO userDAO, @@ -61,7 +55,8 @@ public AnySearchDAO anySearchDAO( final EntityFactory entityFactory, final AnyUtilsFactory anyUtilsFactory, final PlainAttrValidationManager validator, - final ElasticsearchClient client) { + final ElasticsearchClient client, + final ElasticsearchProperties props) { return new ElasticsearchAnySearchDAO( realmDAO, @@ -77,34 +72,22 @@ public AnySearchDAO anySearchDAO( props.getIndexMaxResultWindow()); } - @ConditionalOnMissingBean(name = "elasticsearchRealmDAO") - @Bean + @ConditionalOnMissingBean(name = { "realmDAO", "elasticsearchRealmDAO" }) + @Bean(name = { "realmDAO", "elasticsearchRealmDAO" }) public RealmDAO realmDAO( - final JpaRepositoryFactory jpaRepositoryFactory, - final @Lazy RoleDAO roleDAO, - final ApplicationEventPublisher publisher, - final EntityManager entityManager, - final ElasticsearchProperties props, - final ElasticsearchClient client) { + @Qualifier("delegateRealmDAO") final RealmDAO delegate, + final ElasticsearchClient client, + final ElasticsearchProperties props) { - return jpaRepositoryFactory.getRepository( - RealmRepo.class, - new RealmRepoExtElasticsearchImpl( - roleDAO, - publisher, - entityManager, - client, props.getIndexMaxResultWindow())); + return new ElasticsearchRealmDAO(delegate, client, props.getIndexMaxResultWindow()); } - @ConditionalOnMissingBean(name = "elasticsearchAuditConfDAO") + @ConditionalOnMissingBean(name = "elasticsearchAuditEntryDAO") @Bean - public AuditConfDAO auditConfDAO( - final JpaRepositoryFactory jpaRepositoryFactory, - final ElasticsearchProperties props, - final ElasticsearchClient client) { + public AuditEntryDAO auditEntryDAO( + final ElasticsearchClient client, + final ElasticsearchProperties props) { - return jpaRepositoryFactory.getRepository( - AuditConfRepo.class, - new AuditConfRepoExtElasticsearchImpl(client, props.getIndexMaxResultWindow())); + return new ElasticsearchAuditEntryDAO(client, props.getIndexMaxResultWindow()); } } diff --git a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java similarity index 98% rename from ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java rename to ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java index 0de03d8461..a876d399cd 100644 --- a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java +++ b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java @@ -46,7 +46,7 @@ import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.AttrSchemaType; import org.apache.syncope.common.rest.api.service.JAXRSService; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; @@ -73,8 +73,9 @@ 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.provisioning.api.utils.FormatUtils; -import org.apache.syncope.core.provisioning.api.utils.RealmUtils; +import org.apache.syncope.core.persistence.api.utils.FormatUtils; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; +import org.apache.syncope.core.persistence.common.dao.AbstractAnySearchDAO; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils; import org.springframework.data.domain.Pageable; @@ -207,7 +208,7 @@ protected Query getQuery( } @Override - protected int doCount( + protected long doCount( final Realm base, final boolean recursive, final Set adminRealms, @@ -221,7 +222,7 @@ protected int doCount( LOG.debug("Count JSON request: {}", request); try { - return (int) client.count(request).count(); + return client.count(request).count(); } catch (Exception e) { LOG.error("While counting in Elasticsearch", e); return 0; @@ -238,7 +239,7 @@ protected List sortBuilders(final AnyTypeKind kind, final Stream sortBuilders(final Stream orderBy) { } @Override - public List searchEntries( + public List search( final String entityKey, final AuditElements.EventCategoryType type, final String category, diff --git a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepoExtElasticsearchImpl.java b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchRealmDAO.java similarity index 77% rename from ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepoExtElasticsearchImpl.java rename to ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchRealmDAO.java index 48d79058d5..8a6dc44f3a 100644 --- a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepoExtElasticsearchImpl.java +++ b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchRealmDAO.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao.repo; +package org.apache.syncope.core.persistence.jpa.dao; import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.elasticsearch._types.ScriptLanguage; @@ -29,55 +29,52 @@ import co.elastic.clients.elasticsearch.core.CountRequest; import co.elastic.clients.elasticsearch.core.SearchRequest; import co.elastic.clients.elasticsearch.core.search.Hit; -import jakarta.persistence.EntityManager; import java.util.List; import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.core.persistence.api.dao.MalformedPathException; import org.apache.syncope.core.persistence.api.dao.RealmDAO; -import org.apache.syncope.core.persistence.api.dao.RoleDAO; +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.jpa.entity.JPARealm; +import org.apache.syncope.core.persistence.api.entity.policy.Policy; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils; -import org.springframework.context.ApplicationEventPublisher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.transaction.annotation.Transactional; -public class RealmRepoExtElasticsearchImpl extends RealmRepoExtImpl { +public class ElasticsearchRealmDAO implements RealmDAO { + + protected static final Logger LOG = LoggerFactory.getLogger(RealmDAO.class); protected static final List REALM_SORT_OPTIONS = List.of( new SortOptions.Builder(). script(s -> s.type(ScriptSortType.Number). script(t -> t.inline(i -> i.lang(ScriptLanguage.Painless). - // not working with >= 8.12.0 - see - // https://discuss.elastic.co/t/painless-sort-not-working-in-call-cases-on-8-12-0/351414 - //source("doc['fullPath'].value.chars().filter(ch -> ch == '/').count()"))). - source("doc['fullPath'].value.length()"))). + source("doc['fullPath'].value.chars().filter(ch -> ch == '/').count()"))). order(SortOrder.Asc)). build()); + protected final RealmDAO delegate; + protected final ElasticsearchClient client; protected final int indexMaxResultWindow; - public RealmRepoExtElasticsearchImpl( - final RoleDAO roleDAO, - final ApplicationEventPublisher publisher, - final EntityManager entityManager, + public ElasticsearchRealmDAO( + final RealmDAO delegate, final ElasticsearchClient client, final int indexMaxResultWindow) { - super(roleDAO, publisher, entityManager); + this.delegate = delegate; this.client = client; this.indexMaxResultWindow = indexMaxResultWindow; } - protected Optional findById(final String key) { - return Optional.ofNullable(entityManager.find(JPARealm.class, key)); - } - @Transactional(readOnly = true) @Override public Optional findByFullPath(final String fullPath) { @@ -85,7 +82,7 @@ public Optional findByFullPath(final String fullPath) { return Optional.of(getRoot()); } - if (StringUtils.isBlank(fullPath) || !RealmDAO.PATH_PATTERN.matcher(fullPath).matches()) { + if (StringUtils.isBlank(fullPath) || !PATH_PATTERN.matcher(fullPath).matches()) { throw new MalformedPathException(fullPath); } @@ -101,7 +98,7 @@ public Optional findByFullPath(final String fullPath) { String result = client.search(request, Void.class).hits().hits().stream().findFirst(). map(Hit::id). orElse(null); - return findById(result); + return findById(result).map(Realm.class::cast); } catch (Exception e) { LOG.error("While searching ES for one match", e); } @@ -133,7 +130,7 @@ public List findByName(final String name) { new Query.Builder().term(QueryBuilders.term(). field("name").value(name).build()).build()); return result.stream().map(this::findById). - filter(Optional::isPresent).map(Optional::get).toList(); + filter(Optional::isPresent).map(Optional::get).map(Realm.class::cast).toList(); } @Override @@ -142,7 +139,7 @@ public List findChildren(final Realm realm) { new Query.Builder().term(QueryBuilders.term(). field("parent_id").value(realm.getKey()).build()).build()); return result.stream().map(this::findById). - filter(Optional::isPresent).map(Optional::get).toList(); + filter(Optional::isPresent).map(Optional::get).map(Realm.class::cast).toList(); } protected Query buildDescendantQuery(final String base, final String keyword) { @@ -215,7 +212,7 @@ public List findDescendants(final String base, final String keyword, fina } return result.stream().map(this::findById). - filter(Optional::isPresent).map(Optional::get).toList(); + filter(Optional::isPresent).map(Optional::get).map(Realm.class::cast).toList(); } @Override @@ -251,4 +248,70 @@ public List findDescendants(final String base, final String prefix) { } return result; } + + @Override + public Realm getRoot() { + return delegate.getRoot(); + } + + @Override + public List findByResources(final ExternalResource resource) { + return delegate.findByResources(resource); + } + + @Override + public List findByPolicy(final T policy) { + return delegate.findByPolicy(policy); + } + + @Override + public List findByActionsContaining(final Implementation logicActions) { + return delegate.findByActionsContaining(logicActions); + } + + @Override + public List findAncestors(final Realm realm) { + return delegate.findAncestors(realm); + } + + @Override + public Page findAll(final Pageable pageable) { + return delegate.findAll(pageable); + } + + @Override + public boolean existsById(final String key) { + return delegate.existsById(key); + } + + @Transactional(readOnly = true) + @Override + public Optional findById(final String key) { + return delegate.findById(key); + } + + @Override + public long count() { + return delegate.count(); + } + + @Override + public List findAll() { + return delegate.findAll(); + } + + @Override + public S save(final S entity) { + return delegate.save(entity); + } + + @Override + public void delete(final Realm entity) { + delegate.delete(entity); + } + + @Override + public void deleteById(final String key) { + delegate.deleteById(key); + } } diff --git a/ext/elasticsearch/persistence-jpa/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ext/elasticsearch/persistence/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports similarity index 100% rename from ext/elasticsearch/persistence-jpa/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports rename to ext/elasticsearch/persistence/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports diff --git a/ext/elasticsearch/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java b/ext/elasticsearch/persistence/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java similarity index 98% rename from ext/elasticsearch/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java rename to ext/elasticsearch/persistence/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java index aee1952bf9..d23dcba83c 100644 --- a/ext/elasticsearch/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java +++ b/ext/elasticsearch/persistence/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java @@ -41,7 +41,7 @@ import org.apache.commons.lang3.tuple.Triple; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.AnyTypeKind; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; import org.apache.syncope.core.persistence.api.dao.RealmDAO; @@ -55,10 +55,10 @@ 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.utils.RealmUtils; import org.apache.syncope.core.persistence.jpa.entity.JPAPlainSchema; import org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrValue; import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser; -import org.apache.syncope.core.provisioning.api.utils.RealmUtils; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils; import org.junit.jupiter.api.BeforeEach; @@ -162,7 +162,7 @@ public void getAdminRealmsFilter4groupOwner() { public void searchRequest4groupOwner() throws IOException { // 1. mock AnyUtils anyUtils = mock(AnyUtils.class); - when(anyUtils.getField("key")).thenReturn(ReflectionUtils.findField(JPAUser.class, "id")); + when(anyUtils.getField("key")).thenReturn(Optional.of(ReflectionUtils.findField(JPAUser.class, "id"))); when(anyUtils.newPlainAttrValue()).thenReturn(new JPAUPlainAttrValue()); when(anyUtilsFactory.getInstance(AnyTypeKind.USER)).thenReturn(anyUtils); @@ -204,7 +204,7 @@ public void searchRequest4groupOwner() throws IOException { public void issueSYNCOPE1725() throws IOException { // 1. mock AnyUtils anyUtils = mock(AnyUtils.class); - when(anyUtils.getField("key")).thenReturn(ReflectionUtils.findField(JPAUser.class, "id")); + when(anyUtils.getField("key")).thenReturn(Optional.of(ReflectionUtils.findField(JPAUser.class, "id"))); JPAUPlainAttrValue value = new JPAUPlainAttrValue(); when(anyUtils.newPlainAttrValue()).thenReturn(value); diff --git a/ext/elasticsearch/pom.xml b/ext/elasticsearch/pom.xml index fe6a2a9e7e..51e3876519 100644 --- a/ext/elasticsearch/pom.xml +++ b/ext/elasticsearch/pom.xml @@ -39,7 +39,7 @@ under the License. client-elasticsearch - persistence-jpa + persistence provisioning-java logic diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableRuntimeUtils.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableRuntimeUtils.java index 44b112aaed..01ba4d5cc2 100644 --- a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableRuntimeUtils.java +++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableRuntimeUtils.java @@ -27,8 +27,8 @@ import org.apache.syncope.common.lib.SyncopeClientException; import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.core.flowable.support.DomainProcessEngine; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; -import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.ParsingValidationException; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.provisioning.api.PropagationByResource; import org.apache.syncope.core.workflow.api.WorkflowException; @@ -210,12 +210,12 @@ public static void saveForFormSubmit( public static void throwException(final FlowableException e, final String defaultMessage) { if (e.getCause() == null) { throw new WorkflowException(defaultMessage, e); - } else if (e.getCause() instanceof SyncopeClientException) { - throw (SyncopeClientException) e.getCause(); - } else if (e.getCause() instanceof ParsingValidationException) { - throw (ParsingValidationException) e.getCause(); - } else if (e.getCause() instanceof InvalidEntityException) { - throw (InvalidEntityException) e.getCause(); + } else if (e.getCause() instanceof SyncopeClientException syncopeClientException) { + throw syncopeClientException; + } else if (e.getCause() instanceof ParsingValidationException parsingValidationException) { + throw parsingValidationException; + } else if (e.getCause() instanceof InvalidEntityException invalidEntityException) { + throw invalidEntityException; } else if (e.getCause().getClass().getName().contains("persistence")) { throw (RuntimeException) e.getCause(); } diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DomainProcessEngineFactoryBean.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DomainProcessEngineFactoryBean.java index 01049a7cd8..d58e82f7a1 100644 --- a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DomainProcessEngineFactoryBean.java +++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DomainProcessEngineFactoryBean.java @@ -22,7 +22,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; import javax.sql.DataSource; import org.apache.syncope.core.persistence.api.DomainHolder; @@ -86,10 +85,15 @@ protected ProcessEngine build(final DataSource datasource) { return conf.buildProcessEngine(); } + @SuppressWarnings("unchecked") + protected DomainHolder getDomainHolder() { + return ctx.getBean(DomainHolder.class); + } + @Override - public void load(final String domain, final DataSource datasource) { + public void load(final String domain) { try { - Objects.requireNonNull(getObject()).getEngines().put(domain, build(datasource)); + getObject().getEngines().put(domain, build(getDomainHolder().getDomains().get(domain))); } catch (Exception e) { LOG.error("Could not setup Flowable for {}", domain, e); } @@ -100,8 +104,7 @@ public DomainProcessEngine getObject() throws Exception { if (engine == null) { Map engines = new HashMap<>(); - ctx.getBean(DomainHolder.class).getDomains().forEach( - (domain, datasource) -> engines.put(domain, build(datasource))); + getDomainHolder().getDomains().forEach((domain, datasource) -> engines.put(domain, build(datasource))); engine = new DomainProcessEngine(engines); } diff --git a/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/init/FlowableLoader.java b/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/init/FlowableLoader.java index 5a0fb08788..c07826cc04 100644 --- a/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/init/FlowableLoader.java +++ b/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/init/FlowableLoader.java @@ -22,7 +22,6 @@ import java.io.IOException; import java.io.InputStream; import java.util.List; -import javax.sql.DataSource; import org.apache.commons.io.IOUtils; import org.apache.syncope.common.lib.types.EntitlementsHolder; import org.apache.syncope.common.lib.types.FlowableEntitlement; @@ -61,7 +60,7 @@ public void load() { } @Override - public void load(final String domain, final DataSource datasource) { + public void load(final String domain) { byte[] wfDef = new byte[0]; try (InputStream wfIn = userWorkflowDef.getInputStream()) { diff --git a/ext/oidcc4ui/persistence-api/pom.xml b/ext/oidcc4ui/persistence-api/pom.xml index 2114fa1533..7125133894 100644 --- a/ext/oidcc4ui/persistence-api/pom.xml +++ b/ext/oidcc4ui/persistence-api/pom.xml @@ -40,7 +40,7 @@ under the License. org.apache.syncope.core - syncope-core-persistence-api + syncope-core-persistence-common ${project.version} diff --git a/ext/oidcc4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/OIDCC4UIProviderCheck.java b/ext/oidcc4ui/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/validation/OIDCC4UIProviderCheck.java similarity index 95% rename from ext/oidcc4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/OIDCC4UIProviderCheck.java rename to ext/oidcc4ui/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/validation/OIDCC4UIProviderCheck.java index 1684e416d5..b5a1c06537 100644 --- a/ext/oidcc4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/OIDCC4UIProviderCheck.java +++ b/ext/oidcc4ui/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/validation/OIDCC4UIProviderCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.api.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/ext/oidcc4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/OIDCC4UIProviderValidator.java b/ext/oidcc4ui/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/validation/OIDCC4UIProviderValidator.java similarity index 95% rename from ext/oidcc4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/OIDCC4UIProviderValidator.java rename to ext/oidcc4ui/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/validation/OIDCC4UIProviderValidator.java index e9deec9f94..dfb2a53df9 100644 --- a/ext/oidcc4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/OIDCC4UIProviderValidator.java +++ b/ext/oidcc4ui/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/validation/OIDCC4UIProviderValidator.java @@ -16,12 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.api.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.to.Item; import org.apache.syncope.common.lib.types.EntityViolationType; import org.apache.syncope.core.persistence.api.entity.OIDCC4UIProvider; +import org.apache.syncope.core.persistence.common.validation.AbstractValidator; public class OIDCC4UIProviderValidator extends AbstractValidator { diff --git a/ext/oidcc4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCC4UIProvider.java b/ext/oidcc4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCC4UIProvider.java index d5e5025977..2f83eaac4d 100644 --- a/ext/oidcc4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCC4UIProvider.java +++ b/ext/oidcc4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAOIDCC4UIProvider.java @@ -47,7 +47,7 @@ import org.apache.syncope.core.persistence.api.entity.Implementation; import org.apache.syncope.core.persistence.api.entity.OIDCC4UIProvider; import org.apache.syncope.core.persistence.api.entity.OIDCC4UIUserTemplate; -import org.apache.syncope.core.persistence.jpa.validation.entity.OIDCC4UIProviderCheck; +import org.apache.syncope.core.persistence.api.validation.OIDCC4UIProviderCheck; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; import org.springframework.util.CollectionUtils; diff --git a/ext/opensearch/client-opensearch/src/main/java/org/apache/syncope/ext/opensearch/client/OpenSearchIndexLoader.java b/ext/opensearch/client-opensearch/src/main/java/org/apache/syncope/ext/opensearch/client/OpenSearchIndexLoader.java index e6cde59f4c..ca1b7c0375 100644 --- a/ext/opensearch/client-opensearch/src/main/java/org/apache/syncope/ext/opensearch/client/OpenSearchIndexLoader.java +++ b/ext/opensearch/client-opensearch/src/main/java/org/apache/syncope/ext/opensearch/client/OpenSearchIndexLoader.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.ext.opensearch.client; -import javax.sql.DataSource; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.core.persistence.api.SyncopeCoreLoader; import org.slf4j.Logger; @@ -41,7 +40,7 @@ public int getOrder() { } @Override - public void load(final String domain, final DataSource datasource) { + public void load(final String domain) { try { if (!indexManager.existsAnyIndex(domain, AnyTypeKind.USER)) { indexManager.createAnyIndex(domain, AnyTypeKind.USER, diff --git a/ext/opensearch/logic/src/main/java/org/apache/syncope/core/logic/audit/OpenSearchLogicContext.java b/ext/opensearch/logic/src/main/java/org/apache/syncope/core/logic/audit/OpenSearchLogicContext.java index a0ff23d7d4..12b2a5768e 100644 --- a/ext/opensearch/logic/src/main/java/org/apache/syncope/core/logic/audit/OpenSearchLogicContext.java +++ b/ext/opensearch/logic/src/main/java/org/apache/syncope/core/logic/audit/OpenSearchLogicContext.java @@ -40,13 +40,13 @@ public class OpenSearchLogicContext { @ConditionalOnMissingBean(name = { "defaultAuditAppenders", "openSearchDefaultAuditAppenders" }) @Bean public List defaultAuditAppenders( - final DomainHolder domainHolder, + final DomainHolder domainHolder, final OpenSearchIndexManager openSearchIndexManager) { List auditAppenders = new ArrayList<>(); LoggerContext logCtx = (LoggerContext) LogManager.getContext(false); - domainHolder.getDomains().forEach((domain, dataSource) -> { + domainHolder.getDomains().keySet().forEach(domain -> { AuditAppender appender = new OpenSearchAuditAppender(domain, openSearchIndexManager); LoggerConfig logConf = new LoggerConfig(AuditLoggerName.getAuditLoggerName(domain), null, false); diff --git a/ext/opensearch/persistence-jpa/pom.xml b/ext/opensearch/persistence/pom.xml similarity index 86% rename from ext/opensearch/persistence-jpa/pom.xml rename to ext/opensearch/persistence/pom.xml index 3dbc6ef636..9ae980640f 100644 --- a/ext/opensearch/persistence-jpa/pom.xml +++ b/ext/opensearch/persistence/pom.xml @@ -27,10 +27,10 @@ under the License. 4.0.0-SNAPSHOT - Apache Syncope Ext: OpenSearch Persistence JPA - Apache Syncope Ext: OpenSearch Persistence JPA + Apache Syncope Ext: OpenSearch Persistence + Apache Syncope Ext: OpenSearch Persistence org.apache.syncope.ext.opensearch - syncope-ext-opensearch-persistence-jpa + syncope-ext-opensearch-persistence jar @@ -40,10 +40,10 @@ under the License. org.apache.syncope.core - syncope-core-persistence-jpa + syncope-core-persistence-common ${project.version} - + org.apache.syncope.ext.opensearch syncope-ext-opensearch-client @@ -51,6 +51,12 @@ under the License. + + org.apache.syncope.core + syncope-core-persistence-jpa + ${project.version} + test + org.slf4j slf4j-simple diff --git a/ext/opensearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/OpenSearchPersistenceContext.java b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/OpenSearchPersistenceContext.java similarity index 60% rename from ext/opensearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/OpenSearchPersistenceContext.java rename to ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/OpenSearchPersistenceContext.java index c46eef067d..40422cd551 100644 --- a/ext/opensearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/OpenSearchPersistenceContext.java +++ b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/OpenSearchPersistenceContext.java @@ -18,32 +18,27 @@ */ package org.apache.syncope.core.persistence.jpa; -import jakarta.persistence.EntityManager; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; 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.AuditConfDAO; +import org.apache.syncope.core.persistence.api.dao.AuditEntryDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; 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.RealmDAO; -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.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.jpa.dao.OpenSearchAnySearchDAO; -import org.apache.syncope.core.persistence.jpa.dao.repo.AuditConfRepo; -import org.apache.syncope.core.persistence.jpa.dao.repo.AuditConfRepoExtOpenSearchImpl; -import org.apache.syncope.core.persistence.jpa.dao.repo.RealmRepo; -import org.apache.syncope.core.persistence.jpa.dao.repo.RealmRepoExtOpenSearchImpl; +import org.apache.syncope.core.persistence.jpa.dao.OpenSearchAuditEntryDAO; +import org.apache.syncope.core.persistence.jpa.dao.OpenSearchRealmDAO; import org.apache.syncope.ext.opensearch.client.OpenSearchProperties; import org.opensearch.client.opensearch.OpenSearchClient; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; -import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; @Configuration(proxyBeanMethods = false) public class OpenSearchPersistenceContext { @@ -51,7 +46,6 @@ public class OpenSearchPersistenceContext { @ConditionalOnMissingBean(name = "openSearchAnySearchDAO") @Bean public AnySearchDAO anySearchDAO( - final OpenSearchProperties props, final RealmDAO realmDAO, final @Lazy DynRealmDAO dynRealmDAO, final @Lazy UserDAO userDAO, @@ -61,7 +55,8 @@ public AnySearchDAO anySearchDAO( final EntityFactory entityFactory, final AnyUtilsFactory anyUtilsFactory, final PlainAttrValidationManager validator, - final OpenSearchClient client) { + final OpenSearchClient client, + final OpenSearchProperties props) { return new OpenSearchAnySearchDAO( realmDAO, @@ -77,34 +72,22 @@ public AnySearchDAO anySearchDAO( props.getIndexMaxResultWindow()); } - @ConditionalOnMissingBean(name = "openSearchRealmDAO") - @Bean + @ConditionalOnMissingBean(name = { "realmDAO", "openSearchRealmDAO" }) + @Bean(name = { "realmDAO", "openSearchRealmDAO" }) public RealmDAO realmDAO( - final JpaRepositoryFactory jpaRepositoryFactory, - final @Lazy RoleDAO roleDAO, - final ApplicationEventPublisher publisher, - final EntityManager entityManager, - final OpenSearchProperties props, - final OpenSearchClient client) { + @Qualifier("delegateRealmDAO") final RealmDAO delegate, + final OpenSearchClient client, + final OpenSearchProperties props) { - return jpaRepositoryFactory.getRepository( - RealmRepo.class, - new RealmRepoExtOpenSearchImpl( - roleDAO, - publisher, - entityManager, - client, props.getIndexMaxResultWindow())); + return new OpenSearchRealmDAO(delegate, client, props.getIndexMaxResultWindow()); } - @ConditionalOnMissingBean(name = "openSearchAuditConfDAO") + @ConditionalOnMissingBean(name = "openSearchAuditEntryDAO") @Bean - public AuditConfDAO auditConfDAO( - final JpaRepositoryFactory jpaRepositoryFactory, - final OpenSearchProperties props, - final OpenSearchClient client) { + public AuditEntryDAO auditEntryDAO( + final OpenSearchClient client, + final OpenSearchProperties props) { - return jpaRepositoryFactory.getRepository( - AuditConfRepo.class, - new AuditConfRepoExtOpenSearchImpl(client, props.getIndexMaxResultWindow())); + return new OpenSearchAuditEntryDAO(client, props.getIndexMaxResultWindow()); } } diff --git a/ext/opensearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAO.java b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAO.java similarity index 98% rename from ext/opensearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAO.java rename to ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAO.java index 204456aaf7..87851ec0f4 100644 --- a/ext/opensearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAO.java +++ b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAO.java @@ -32,7 +32,7 @@ import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.AttrSchemaType; import org.apache.syncope.common.rest.api.service.JAXRSService; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; @@ -59,8 +59,9 @@ 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.provisioning.api.utils.FormatUtils; -import org.apache.syncope.core.provisioning.api.utils.RealmUtils; +import org.apache.syncope.core.persistence.api.utils.FormatUtils; +import org.apache.syncope.core.persistence.api.utils.RealmUtils; +import org.apache.syncope.core.persistence.common.dao.AbstractAnySearchDAO; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.ext.opensearch.client.OpenSearchUtils; import org.opensearch.client.json.JsonData; @@ -207,7 +208,7 @@ protected Query getQuery( } @Override - protected int doCount( + protected long doCount( final Realm base, final boolean recursive, final Set adminRealms, @@ -221,7 +222,7 @@ protected int doCount( LOG.debug("Count JSON request: {}", request); try { - return (int) client.count(request).count(); + return client.count(request).count(); } catch (Exception e) { LOG.error("While counting in OpenSearch", e); return 0; @@ -238,7 +239,7 @@ protected List sortBuilders(final AnyTypeKind kind, final Stream sortBuilders(final Stream orderBy) { } @Override - public List searchEntries( + public List search( final String entityKey, final AuditElements.EventCategoryType type, final String category, diff --git a/ext/opensearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepoExtOpenSearchImpl.java b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchRealmDAO.java similarity index 77% rename from ext/opensearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepoExtOpenSearchImpl.java rename to ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchRealmDAO.java index 4a779286d5..a935d7d6db 100644 --- a/ext/opensearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RealmRepoExtOpenSearchImpl.java +++ b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchRealmDAO.java @@ -16,18 +16,18 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao.repo; +package org.apache.syncope.core.persistence.jpa.dao; -import jakarta.persistence.EntityManager; import java.util.List; import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.core.persistence.api.dao.MalformedPathException; import org.apache.syncope.core.persistence.api.dao.RealmDAO; -import org.apache.syncope.core.persistence.api.dao.RoleDAO; +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.jpa.entity.JPARealm; +import org.apache.syncope.core.persistence.api.entity.policy.Policy; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.ext.opensearch.client.OpenSearchUtils; import org.opensearch.client.opensearch.OpenSearchClient; @@ -41,11 +41,15 @@ import org.opensearch.client.opensearch.core.CountRequest; import org.opensearch.client.opensearch.core.SearchRequest; import org.opensearch.client.opensearch.core.search.Hit; -import org.springframework.context.ApplicationEventPublisher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.transaction.annotation.Transactional; -public class RealmRepoExtOpenSearchImpl extends RealmRepoExtImpl { +public class OpenSearchRealmDAO implements RealmDAO { + + protected static final Logger LOG = LoggerFactory.getLogger(RealmDAO.class); protected static final List REALM_SORT_OPTIONS = List.of( new SortOptions.Builder(). @@ -55,26 +59,22 @@ public class RealmRepoExtOpenSearchImpl extends RealmRepoExtImpl { order(SortOrder.Asc)). build()); + protected final RealmDAO delegate; + protected final OpenSearchClient client; protected final int indexMaxResultWindow; - public RealmRepoExtOpenSearchImpl( - final RoleDAO roleDAO, - final ApplicationEventPublisher publisher, - final EntityManager entityManager, + public OpenSearchRealmDAO( + final RealmDAO delegate, final OpenSearchClient client, final int indexMaxResultWindow) { - super(roleDAO, publisher, entityManager); + this.delegate = delegate; this.client = client; this.indexMaxResultWindow = indexMaxResultWindow; } - protected Optional findById(final String key) { - return Optional.ofNullable(entityManager.find(JPARealm.class, key)); - } - @Transactional(readOnly = true) @Override public Optional findByFullPath(final String fullPath) { @@ -82,7 +82,7 @@ public Optional findByFullPath(final String fullPath) { return Optional.of(getRoot()); } - if (StringUtils.isBlank(fullPath) || !RealmDAO.PATH_PATTERN.matcher(fullPath).matches()) { + if (StringUtils.isBlank(fullPath) || !PATH_PATTERN.matcher(fullPath).matches()) { throw new MalformedPathException(fullPath); } @@ -98,7 +98,7 @@ public Optional findByFullPath(final String fullPath) { String result = client.search(request, Void.class).hits().hits().stream().findFirst(). map(Hit::id). orElse(null); - return findById(result); + return findById(result).map(Realm.class::cast); } catch (Exception e) { LOG.error("While searching ES for one match", e); } @@ -130,7 +130,7 @@ public List findByName(final String name) { new Query.Builder().term(QueryBuilders.term(). field("name").value(FieldValue.of(name)).build()).build()); return result.stream().map(this::findById). - filter(Optional::isPresent).map(Optional::get).toList(); + filter(Optional::isPresent).map(Optional::get).map(Realm.class::cast).toList(); } @Override @@ -139,7 +139,7 @@ public List findChildren(final Realm realm) { new Query.Builder().term(QueryBuilders.term(). field("parent_id").value(FieldValue.of(realm.getKey())).build()).build()); return result.stream().map(this::findById). - filter(Optional::isPresent).map(Optional::get).toList(); + filter(Optional::isPresent).map(Optional::get).map(Realm.class::cast).toList(); } protected Query buildDescendantQuery(final String base, final String keyword) { @@ -212,7 +212,7 @@ public List findDescendants(final String base, final String keyword, fina } return result.stream().map(this::findById). - filter(Optional::isPresent).map(Optional::get).toList(); + filter(Optional::isPresent).map(Optional::get).map(Realm.class::cast).toList(); } @Override @@ -225,7 +225,7 @@ public List findDescendants(final String base, final String prefix) { build()).build()).build()).build(); Query query = new Query.Builder().bool(QueryBuilders.bool().must( - buildDescendantQuery(base, (String) null), + buildDescendantQuery(base, null), prefixQuery).build()). build(); @@ -248,4 +248,70 @@ public List findDescendants(final String base, final String prefix) { } return result; } + + @Override + public Realm getRoot() { + return delegate.getRoot(); + } + + @Override + public List findByResources(final ExternalResource resource) { + return delegate.findByResources(resource); + } + + @Override + public List findByPolicy(final T policy) { + return delegate.findByPolicy(policy); + } + + @Override + public List findByActionsContaining(final Implementation logicActions) { + return delegate.findByActionsContaining(logicActions); + } + + @Override + public List findAncestors(final Realm realm) { + return delegate.findAncestors(realm); + } + + @Override + public Page findAll(final Pageable pageable) { + return delegate.findAll(pageable); + } + + @Override + public boolean existsById(final String key) { + return delegate.existsById(key); + } + + @Transactional(readOnly = true) + @Override + public Optional findById(final String key) { + return delegate.findById(key); + } + + @Override + public long count() { + return delegate.count(); + } + + @Override + public List findAll() { + return delegate.findAll(); + } + + @Override + public S save(final S entity) { + return delegate.save(entity); + } + + @Override + public void delete(final Realm entity) { + delegate.delete(entity); + } + + @Override + public void deleteById(final String key) { + delegate.deleteById(key); + } } diff --git a/ext/opensearch/persistence-jpa/src/main/resources/META-INF/spring.factories b/ext/opensearch/persistence/src/main/resources/META-INF/spring.factories similarity index 100% rename from ext/opensearch/persistence-jpa/src/main/resources/META-INF/spring.factories rename to ext/opensearch/persistence/src/main/resources/META-INF/spring.factories diff --git a/ext/opensearch/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAOTest.java b/ext/opensearch/persistence/src/test/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAOTest.java similarity index 98% rename from ext/opensearch/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAOTest.java rename to ext/opensearch/persistence/src/test/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAOTest.java index d3b09d537f..a24dffd887 100644 --- a/ext/opensearch/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAOTest.java +++ b/ext/opensearch/persistence/src/test/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAOTest.java @@ -35,7 +35,7 @@ import org.apache.commons.lang3.tuple.Triple; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.AnyTypeKind; -import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; import org.apache.syncope.core.persistence.api.dao.RealmDAO; @@ -49,10 +49,10 @@ 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.utils.RealmUtils; import org.apache.syncope.core.persistence.jpa.entity.JPAPlainSchema; import org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrValue; import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser; -import org.apache.syncope.core.provisioning.api.utils.RealmUtils; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.ext.opensearch.client.OpenSearchUtils; import org.junit.jupiter.api.BeforeEach; @@ -163,7 +163,7 @@ public void getAdminRealmsFilter4groupOwner() { public void searchRequest4groupOwner() throws IOException { // 1. mock AnyUtils anyUtils = mock(AnyUtils.class); - when(anyUtils.getField("key")).thenReturn(ReflectionUtils.findField(JPAUser.class, "id")); + when(anyUtils.getField("key")).thenReturn(Optional.of(ReflectionUtils.findField(JPAUser.class, "id"))); when(anyUtils.newPlainAttrValue()).thenReturn(new JPAUPlainAttrValue()); when(anyUtilsFactory.getInstance(AnyTypeKind.USER)).thenReturn(anyUtils); @@ -205,7 +205,7 @@ public void searchRequest4groupOwner() throws IOException { public void issueSYNCOPE1725() throws IOException { // 1. mock AnyUtils anyUtils = mock(AnyUtils.class); - when(anyUtils.getField("key")).thenReturn(ReflectionUtils.findField(JPAUser.class, "id")); + when(anyUtils.getField("key")).thenReturn(Optional.of(ReflectionUtils.findField(JPAUser.class, "id"))); JPAUPlainAttrValue value = new JPAUPlainAttrValue(); when(anyUtils.newPlainAttrValue()).thenReturn(value); diff --git a/ext/opensearch/pom.xml b/ext/opensearch/pom.xml index daa967444c..28de828ab5 100644 --- a/ext/opensearch/pom.xml +++ b/ext/opensearch/pom.xml @@ -39,7 +39,7 @@ under the License. client-opensearch - persistence-jpa + persistence provisioning-java logic diff --git a/ext/saml2sp4ui/persistence-api/pom.xml b/ext/saml2sp4ui/persistence-api/pom.xml index 287b7a9314..d38be815b2 100644 --- a/ext/saml2sp4ui/persistence-api/pom.xml +++ b/ext/saml2sp4ui/persistence-api/pom.xml @@ -40,7 +40,7 @@ under the License. org.apache.syncope.core - syncope-core-persistence-api + syncope-core-persistence-common ${project.version} diff --git a/ext/saml2sp4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SAML2SP4UIIdPCheck.java b/ext/saml2sp4ui/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/validation/SAML2SP4UIIdPCheck.java similarity index 95% rename from ext/saml2sp4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SAML2SP4UIIdPCheck.java rename to ext/saml2sp4ui/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/validation/SAML2SP4UIIdPCheck.java index 4d2011a92b..4899d6b9b5 100644 --- a/ext/saml2sp4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SAML2SP4UIIdPCheck.java +++ b/ext/saml2sp4ui/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/validation/SAML2SP4UIIdPCheck.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.api.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; diff --git a/ext/saml2sp4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SAML2SP4UIIdPValidator.java b/ext/saml2sp4ui/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/validation/SAML2SP4UIIdPValidator.java similarity index 95% rename from ext/saml2sp4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SAML2SP4UIIdPValidator.java rename to ext/saml2sp4ui/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/validation/SAML2SP4UIIdPValidator.java index a59546b9f1..b3f60c463f 100644 --- a/ext/saml2sp4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SAML2SP4UIIdPValidator.java +++ b/ext/saml2sp4ui/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/validation/SAML2SP4UIIdPValidator.java @@ -16,12 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.validation.entity; +package org.apache.syncope.core.persistence.api.validation; import jakarta.validation.ConstraintValidatorContext; import org.apache.syncope.common.lib.to.Item; import org.apache.syncope.common.lib.types.EntityViolationType; import org.apache.syncope.core.persistence.api.entity.SAML2SP4UIIdP; +import org.apache.syncope.core.persistence.common.validation.AbstractValidator; public class SAML2SP4UIIdPValidator extends AbstractValidator { diff --git a/ext/saml2sp4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASAML2SP4UIIdP.java b/ext/saml2sp4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASAML2SP4UIIdP.java index 11faea6ffc..a1b72eb950 100644 --- a/ext/saml2sp4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASAML2SP4UIIdP.java +++ b/ext/saml2sp4ui/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASAML2SP4UIIdP.java @@ -48,7 +48,7 @@ import org.apache.syncope.core.persistence.api.entity.Implementation; import org.apache.syncope.core.persistence.api.entity.SAML2SP4UIIdP; import org.apache.syncope.core.persistence.api.entity.SAML2SP4UIUserTemplate; -import org.apache.syncope.core.persistence.jpa.validation.entity.SAML2SP4UIIdPCheck; +import org.apache.syncope.core.persistence.api.validation.SAML2SP4UIIdPCheck; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @Entity diff --git a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java index f812f3d522..db0a658a5f 100644 --- a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java +++ b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java @@ -32,8 +32,8 @@ import org.apache.syncope.common.lib.SyncopeClientException; import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.common.lib.types.EntityViolationType; -import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException; -import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException; +import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException; +import org.apache.syncope.core.persistence.api.attrvalue.ParsingValidationException; import org.apache.syncope.core.persistence.api.dao.DuplicateException; import org.apache.syncope.core.persistence.api.dao.MalformedPathException; import org.apache.syncope.core.persistence.api.dao.NotFoundException; diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml index 8c88785b50..18677558d6 100644 --- a/fit/core-reference/pom.xml +++ b/fit/core-reference/pom.xml @@ -526,7 +526,7 @@ under the License. org.apache.syncope.ext.elasticsearch - syncope-ext-elasticsearch-persistence-jpa + syncope-ext-elasticsearch-persistence ${project.version} @@ -641,7 +641,7 @@ under the License. org.apache.syncope.ext.opensearch - syncope-ext-opensearch-persistence-jpa + syncope-ext-opensearch-persistence ${project.version} diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/CoreReferenceContext.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/CoreReferenceContext.java index 79e9aa4f79..67cd64e9f7 100644 --- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/CoreReferenceContext.java +++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/CoreReferenceContext.java @@ -22,6 +22,7 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.core.logic.IdRepoLogicContext; import org.apache.syncope.core.logic.audit.AuditAppender; +import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.provisioning.api.ImplementationLookup; @@ -45,10 +46,11 @@ public EnableFlowableForTestUsers enableFlowableForTestUsers(final UserDAO userD @Bean public ImplementationLookup implementationLookup( + final DomainHolder domainHolder, final UserWorkflowAdapter uwf, final EnableFlowableForTestUsers enableFlowableForTestUsers) { - return new ITImplementationLookup(uwf, enableFlowableForTestUsers); + return new ITImplementationLookup(domainHolder, uwf, enableFlowableForTestUsers); } @Bean diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java index 018434f108..9341b4324c 100644 --- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java +++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java @@ -37,10 +37,11 @@ import org.apache.syncope.common.lib.types.IdMImplementationType; import org.apache.syncope.common.lib.types.IdRepoImplementationType; import org.apache.syncope.core.logic.job.MacroRunJobDelegate; -import org.apache.syncope.core.persistence.jpa.attrvalue.validation.AlwaysTrueValidator; -import org.apache.syncope.core.persistence.jpa.attrvalue.validation.BasicValidator; -import org.apache.syncope.core.persistence.jpa.attrvalue.validation.BinaryValidator; -import org.apache.syncope.core.persistence.jpa.attrvalue.validation.EmailAddressValidator; +import org.apache.syncope.core.persistence.api.DomainHolder; +import org.apache.syncope.core.persistence.common.attrvalue.AlwaysTrueValidator; +import org.apache.syncope.core.persistence.common.attrvalue.BasicValidator; +import org.apache.syncope.core.persistence.common.attrvalue.BinaryValidator; +import org.apache.syncope.core.persistence.common.attrvalue.EmailAddressValidator; import org.apache.syncope.core.provisioning.api.ImplementationLookup; import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate; import org.apache.syncope.core.provisioning.api.rules.AccountRule; @@ -193,14 +194,18 @@ public class ITImplementationLookup implements ImplementationLookup { } }; + private final DomainHolder domainHolder; + private final UserWorkflowAdapter uwf; private final EnableFlowableForTestUsers enableFlowableForTestUsers; public ITImplementationLookup( + final DomainHolder domainHolder, final UserWorkflowAdapter uwf, final EnableFlowableForTestUsers enableFlowableForTestUsers) { + this.domainHolder = domainHolder; this.uwf = uwf; this.enableFlowableForTestUsers = enableFlowableForTestUsers; } @@ -213,10 +218,15 @@ public int getOrder() { protected static final Logger LOG = LoggerFactory.getLogger(ITImplementationLookup.class); @Override - public void load(final String domain, final DataSource datasource) { + public void load(final String domain) { + Object v = domainHolder.getDomains().get(domain); + // in case the Flowable extension is enabled, enable modifications for test users - if (enableFlowableForTestUsers != null && AopUtils.getTargetClass(uwf).getName().contains("Flowable")) { - AuthContextUtils.runAsAdmin(domain, () -> enableFlowableForTestUsers.init(datasource)); + if (enableFlowableForTestUsers != null + && AopUtils.getTargetClass(uwf).getName().contains("Flowable") + && v instanceof DataSource dataSource) { + + AuthContextUtils.runAsAdmin(domain, () -> enableFlowableForTestUsers.init(dataSource)); } } diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java index df3071d417..a580cf2c45 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java @@ -60,7 +60,6 @@ import org.apache.syncope.common.lib.to.AnyObjectTO; import org.apache.syncope.common.lib.to.AuditConfTO; import org.apache.syncope.common.lib.to.ConnInstanceTO; -import org.apache.syncope.common.lib.to.ConnPoolConfTO; import org.apache.syncope.common.lib.to.GroupTO; import org.apache.syncope.common.lib.to.ImplementationTO; import org.apache.syncope.common.lib.to.PagedResult; @@ -73,6 +72,7 @@ import org.apache.syncope.common.lib.types.AuditElements; import org.apache.syncope.common.lib.types.AuditLoggerName; 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.common.lib.types.IdRepoImplementationType; import org.apache.syncope.common.lib.types.ImplementationEngine; @@ -488,7 +488,7 @@ public void customAuditAppender() throws IOException, InterruptedException { ConnInstanceTO connector = CONNECTOR_SERVICE.readByResource(RESOURCE_NAME_CSV, null); assertNotNull(connector); - connector.setPoolConf(new ConnPoolConfTO()); + connector.setPoolConf(new ConnPoolConf()); CONNECTOR_SERVICE.update(connector); // check audit_for_Master_file.log, it should contain only a static message diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java index 9bca51e19a..6ce27ee5d9 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java @@ -45,7 +45,6 @@ import org.apache.syncope.common.lib.to.ConnIdBundle; import org.apache.syncope.common.lib.to.ConnIdObjectClass; import org.apache.syncope.common.lib.to.ConnInstanceTO; -import org.apache.syncope.common.lib.to.ConnPoolConfTO; import org.apache.syncope.common.lib.to.Item; import org.apache.syncope.common.lib.to.Mapping; import org.apache.syncope.common.lib.to.Provision; @@ -54,6 +53,7 @@ import org.apache.syncope.common.lib.types.ClientExceptionType; 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.common.rest.api.service.ConnectorService; import org.apache.syncope.common.rest.api.service.ResourceService; @@ -160,7 +160,7 @@ public void create() { connectorTO.getCapabilities().add(ConnectorCapability.UPDATE); // set connector pool conf - ConnPoolConfTO cpc = new ConnPoolConfTO(); + ConnPoolConf cpc = new ConnPoolConf(); cpc.setMaxObjects(1534); connectorTO.setPoolConf(cpc); diff --git a/pom.xml b/pom.xml index 04a0d62d87..f175f6f3c7 100644 --- a/pom.xml +++ b/pom.xml @@ -501,6 +501,7 @@ under the License. 16 8.0 11 + 5.16.0 42.7.2 8.3.0 @@ -676,6 +677,18 @@ under the License. + + org.springframework.boot + spring-boot-starter-data-neo4j + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-logging + + + + org.springframework.boot spring-boot-starter-web diff --git a/src/main/asciidoc/reference-guide/concepts/typemanagement.adoc b/src/main/asciidoc/reference-guide/concepts/typemanagement.adoc index 6b6e762227..63f619cdef 100644 --- a/src/main/asciidoc/reference-guide/concepts/typemanagement.adoc +++ b/src/main/asciidoc/reference-guide/concepts/typemanagement.adoc @@ -55,10 +55,10 @@ cleartext on-demand (requires AES ciphering) ** `Binary` - it is required to provide the declared mime type * Validator class - (optional) Java class validating the value(s) provided for attributes, see ifeval::["{snapshotOrRelease}" == "release"] -https://github.com/apache/syncope/blob/syncope-{docVersion}/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/EmailAddressValidator.java[EmailAddressValidator^] +https://github.com/apache/syncope/blob/syncope-{docVersion}/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/EmailAddressValidator.java[EmailAddressValidator^] endif::[] ifeval::["{snapshotOrRelease}" == "snapshot"] -https://github.com/apache/syncope/blob/master/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/attrvalue/validation/EmailAddressValidator.java[EmailAddressValidator^] +https://github.com/apache/syncope/blob/master/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/EmailAddressValidator.java[EmailAddressValidator^] endif::[] for reference * Mandatory condition - http://commons.apache.org/proper/commons-jexl/[JEXL^] expression indicating whether values for diff --git a/src/main/asciidoc/reference-guide/usage/customization.adoc b/src/main/asciidoc/reference-guide/usage/customization.adoc index c56a611de1..c3029bebf9 100644 --- a/src/main/asciidoc/reference-guide/usage/customization.adoc +++ b/src/main/asciidoc/reference-guide/usage/customization.adoc @@ -376,7 +376,7 @@ Add the following dependencies to `core/pom.xml`: org.apache.syncope.ext.elasticsearch - syncope-ext-elasticsearch-persistence-jpa + syncope-ext-elasticsearch-persistence ${syncope.version} ---- @@ -434,7 +434,7 @@ Add the following dependencies to `core/pom.xml`: org.apache.syncope.ext.opensearch - syncope-ext-opensearch-persistence-jpa + syncope-ext-opensearch-persistence ${syncope.version} ---- From bf3ef2ba18831c141a4e856634b8eea5ad535b78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Wed, 21 Feb 2024 14:35:32 +0100 Subject: [PATCH 02/10] Fixing JPA JSON tests --- .../src/test/resources/domains/MasterContent.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml index ff80648843..232469af4b 100644 --- a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml +++ b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml @@ -344,7 +344,7 @@ under the License. realm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" creator="admin" lastModifier="admin" creationDate="2010-10-20 11:00:00" lastChangeDate="2010-10-20 11:00:00" - plainAttrs='[{"values":[{"stringValue":"niceIcon"}],"schema":"icon"},{"uniqueValue":{"booleanValue":true},"schema":"show"},{"values":[{"stringValue":"sx"}],"schema":"rderived_sx"},{"values":[{"stringValue":"dx"}],"schema":"rderived_dx"}]'/> + plainAttrs='[{"values":[{"stringValue":"niceIcon"}],"schema":"icon"},{"values":[{"booleanValue":true}],"schema":"show"},{"values":[{"stringValue":"sx"}],"schema":"rderived_sx"},{"values":[{"stringValue":"dx"}],"schema":"rderived_dx"}]'/> Date: Wed, 21 Feb 2024 15:31:32 +0100 Subject: [PATCH 03/10] Fix ITs --- .../core/persistence/jpa/dao/repo/AnyObjectRepoExt.java | 6 ++++++ .../syncope/core/persistence/jpa/dao/repo/GroupRepoExt.java | 6 ++++++ .../syncope/core/persistence/jpa/dao/repo/UserRepoExt.java | 6 ++++++ .../core/provisioning/java/job/DefaultJobManager.java | 6 +++--- .../test/java/org/apache/syncope/fit/core/RoleITCase.java | 2 +- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExt.java index 45eb7cbff0..b7842b603c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExt.java @@ -52,4 +52,10 @@ public interface AnyObjectRepoExt extends AnyRepoExt { Collection findAllResources(AnyObject anyObject); Pair, Set> saveAndGetDynGroupMembs(AnyObject anyObject); + + @Override + S save(S anyObject); + + @Override + void delete(AnyObject anyObject); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExt.java index a49a84ca2d..cc7004b39a 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExt.java @@ -73,4 +73,10 @@ public interface GroupRepoExt extends AnyRepoExt { Pair, Set> refreshDynMemberships(User user); Set removeDynMemberships(User user); + + @Override + S save(S group); + + @Override + void delete(Group group); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExt.java index 16d87a4c05..6e39be2c3c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExt.java @@ -56,4 +56,10 @@ public interface UserRepoExt extends AnyRepoExt { Pair, Set> saveAndGetDynGroupMembs(User user); boolean linkedAccountExists(String userKey, String connObjectKeyValue); + + @Override + S save(S user); + + @Override + void delete(User user); } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java index 7a9e8ed468..671f0cf7cf 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/DefaultJobManager.java @@ -130,11 +130,11 @@ protected boolean isRunningElsewhere(final JobKey jobKey) throws SchedulerExcept if (v instanceof DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); return jdbcTemplate.queryForObject( - "SELECT 1 FROM " + Constants.DEFAULT_TABLE_PREFIX + "FIRED_TRIGGERS " + "SELECT COUNT(ENTRY_ID) FROM " + Constants.DEFAULT_TABLE_PREFIX + "FIRED_TRIGGERS " + "WHERE JOB_NAME = ? AND JOB_GROUP = ?", - Boolean.class, + Integer.class, jobKey.getName(), - jobKey.getGroup()); + jobKey.getGroup()) > 0; } LOG.warn("Unsupported persistence source: " + v.getClass().getName()); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RoleITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RoleITCase.java index 4ee3590890..292a663281 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RoleITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RoleITCase.java @@ -77,7 +77,7 @@ public void create() { createRole(role); fail("This should not happen"); } catch (SyncopeClientException e) { - assertEquals(ClientExceptionType.InvalidRole, e.getType()); + assertEquals(ClientExceptionType.GenericPersistence, e.getType()); } role.setKey("new" + getUUIDString()); From af08e3eeceebb8d2c7329293bee4648f219ce390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Thu, 22 Feb 2024 16:21:35 +0100 Subject: [PATCH 04/10] Fixing Elasticsearch and OpenSearch --- .../syncope/core/logic/IdMLogicContext.java | 47 ++--- .../core/logic/ReconciliationLogic.java | 15 +- .../syncope/core/logic/AbstractAnyLogic.java | 14 +- .../syncope/core/logic/AnyObjectLogic.java | 8 +- .../apache/syncope/core/logic/GroupLogic.java | 8 +- .../core/logic/IdRepoLogicContext.java | 43 +++-- .../apache/syncope/core/logic/RealmLogic.java | 23 ++- .../syncope/core/logic/SyncopeLogic.java | 10 +- .../apache/syncope/core/logic/UserLogic.java | 10 +- .../core/persistence/api/dao/RealmDAO.java | 15 -- .../persistence/api/dao/RealmSearchDAO.java | 54 ++++++ .../common/dao/AbstractAnySearchDAO.java | 10 +- .../jpa/MyJPAJSONPersistenceContext.java | 6 +- .../jpa/OJPAJSONPersistenceContext.java | 6 +- .../jpa/PGJPAJSONPersistenceContext.java | 6 +- .../jpa/dao/MyJPAJSONAnySearchDAO.java | 6 +- .../jpa/dao/OJPAJSONAnySearchDAO.java | 6 +- .../jpa/dao/PGJPAJSONAnySearchDAO.java | 8 +- .../persistence/jpa/PersistenceContext.java | 27 ++- .../jpa/content/XMLContentExporter.java | 10 +- .../persistence/jpa/dao/JPAAnySearchDAO.java | 12 +- .../core/persistence/jpa/dao/JPARealmDAO.java | 156 +--------------- .../jpa/dao/JPARealmSearchDAO.java | 175 ++++++++++++++++++ .../core/persistence/jpa/dao/JPATaskDAO.java | 13 +- .../persistence/jpa/inner/AnyObjectTest.java | 6 +- .../persistence/jpa/inner/AnySearchTest.java | 6 +- .../core/persistence/jpa/inner/GroupTest.java | 6 +- .../jpa/inner/MultitenancyTest.java | 10 +- .../core/persistence/jpa/inner/RealmTest.java | 24 ++- .../core/persistence/jpa/inner/RoleTest.java | 6 +- .../core/persistence/jpa/inner/UserTest.java | 12 +- .../persistence/jpa/outer/AnySearchTest.java | 6 +- .../core/persistence/jpa/outer/GroupTest.java | 10 +- .../persistence/jpa/outer/PolicyTest.java | 6 +- .../core/persistence/jpa/outer/RealmTest.java | 6 +- .../core/persistence/jpa/outer/RoleTest.java | 12 +- .../persistence/neo4j/PersistenceContext.java | 26 ++- .../neo4j/dao/Neo4jAnySearchDAO.java | 12 +- .../persistence/neo4j/dao/Neo4jRealmDAO.java | 118 +----------- .../neo4j/dao/Neo4jRealmSearchDAO.java | 132 +++++++++++++ .../persistence/neo4j/dao/Neo4jTaskDAO.java | 13 +- .../neo4j/inner/AnyObjectTest.java | 6 +- .../neo4j/inner/AnySearchTest.java | 6 +- .../persistence/neo4j/inner/GroupTest.java | 6 +- .../neo4j/inner/MultitenancyTest.java | 10 +- .../persistence/neo4j/inner/RealmTest.java | 28 +-- .../persistence/neo4j/inner/RoleTest.java | 6 +- .../persistence/neo4j/inner/UserTest.java | 12 +- .../neo4j/outer/AnySearchTest.java | 6 +- .../persistence/neo4j/outer/GroupTest.java | 10 +- .../persistence/neo4j/outer/PolicyTest.java | 6 +- .../persistence/neo4j/outer/RealmTest.java | 6 +- .../persistence/neo4j/outer/RoleTest.java | 12 +- .../java/DefaultConnectorManager.java | 7 +- .../java/DefaultMappingManager.java | 10 +- .../java/ProvisioningContext.java | 47 +++-- .../java/data/AbstractAnyDataBinder.java | 10 +- .../java/data/AnyObjectDataBinderImpl.java | 8 +- .../java/data/ClientAppDataBinderImpl.java | 10 +- .../java/data/ConnInstanceDataBinderImpl.java | 12 +- .../java/data/GroupDataBinderImpl.java | 8 +- .../java/data/RoleDataBinderImpl.java | 10 +- .../java/data/TaskDataBinderImpl.java | 16 +- .../java/data/UserDataBinderImpl.java | 8 +- ...erateRandomPasswordPropagationActions.java | 9 +- .../pushpull/AbstractRealmResultHandler.java | 4 + .../DefaultRealmPullResultHandler.java | 2 +- .../DefaultRealmPushResultHandler.java | 2 +- .../java/pushpull/InboundMatcher.java | 13 +- .../java/pushpull/PushJobDelegate.java | 6 +- .../java/pushpull/SinglePullJobDelegate.java | 6 +- .../stream/StreamPullJobDelegate.java | 6 +- .../java/utils/ConnObjectUtils.java | 12 +- .../java/ConnectorManagerTest.java | 2 +- .../java/DefaultMappingManagerTest.java | 6 +- .../spring/policy/DefaultRuleEnforcer.java | 12 +- .../spring/security/AuthDataAccessor.java | 10 +- .../core/spring/security/SecurityContext.java | 6 +- .../spring/security/WebSecurityContext.java | 6 +- .../src/main/resources/core.properties | 4 +- .../workflow/java/WorkflowTestContext.java | 6 +- .../ElasticsearchPersistenceContext.java | 24 +-- .../dao/ElasticsearchAnySearchDAO.java | 14 +- .../dao/ElasticsearchAuditEntryDAO.java | 2 +- .../dao/ElasticsearchRealmDAO.java | 93 ++-------- ...ot.autoconfigure.AutoConfiguration.imports | 2 +- .../dao/ElasticsearchAnySearchDAOTest.java | 13 +- ...t.autoconfigure.AutoConfiguration.imports} | 4 +- ...t.autoconfigure.AutoConfiguration.imports} | 4 +- .../OpenSearchPersistenceContext.java | 24 +-- .../dao/OpenSearchAnySearchDAO.java | 14 +- .../dao/OpenSearchAuditEntryDAO.java | 2 +- .../dao/OpenSearchRealmDAO.java | 93 ++-------- ...t.autoconfigure.AutoConfiguration.imports} | 4 +- .../dao/OpenSearchAnySearchDAOTest.java | 13 +- wa/starter/src/main/resources/wa.properties | 7 +- 96 files changed, 949 insertions(+), 835 deletions(-) create mode 100644 core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmSearchDAO.java create mode 100644 core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmSearchDAO.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmSearchDAO.java rename ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/{jpa => elasticsearch}/ElasticsearchPersistenceContext.java (82%) rename ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/{jpa => elasticsearch}/dao/ElasticsearchAnySearchDAO.java (98%) rename ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/{jpa => elasticsearch}/dao/ElasticsearchAuditEntryDAO.java (99%) rename ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/{jpa => elasticsearch}/dao/ElasticsearchRealmDAO.java (80%) rename ext/elasticsearch/persistence/src/test/java/org/apache/syncope/core/persistence/{jpa => elasticsearch}/dao/ElasticsearchAnySearchDAOTest.java (97%) rename ext/opensearch/{logic/src/main/resources/META-INF/spring.factories => client-opensearch/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports} (86%) rename ext/opensearch/{client-opensearch/src/main/resources/META-INF/spring.factories => logic/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports} (85%) rename ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/{jpa => opensearch}/OpenSearchPersistenceContext.java (83%) rename ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/{jpa => opensearch}/dao/OpenSearchAnySearchDAO.java (98%) rename ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/{jpa => opensearch}/dao/OpenSearchAuditEntryDAO.java (99%) rename ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/{jpa => opensearch}/dao/OpenSearchRealmDAO.java (80%) rename ext/opensearch/persistence/src/main/resources/META-INF/{spring.factories => spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports} (85%) rename ext/opensearch/persistence/src/test/java/org/apache/syncope/core/persistence/{jpa => opensearch}/dao/OpenSearchAnySearchDAOTest.java (97%) diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/IdMLogicContext.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/IdMLogicContext.java index 67dd5405e6..2d07bcf632 100644 --- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/IdMLogicContext.java +++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/IdMLogicContext.java @@ -26,7 +26,7 @@ import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; 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.RealmDAO; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.RemediationDAO; import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; @@ -45,6 +45,7 @@ @Configuration(proxyBeanMethods = false) public class IdMLogicContext { + @ConditionalOnMissingBean @Bean public IdMEntitlementLoader idmEntitlementLoader() { @@ -60,11 +61,11 @@ public IdMImplementationTypeLoader idmImplementationTypeLoader() { @ConditionalOnMissingBean @Bean public ConnectorLogic connectorLogic( - final ConnIdBundleManager connIdBundleManager, - final ExternalResourceDAO resourceDAO, - final ConnInstanceDAO connInstanceDAO, - final ConnInstanceDataBinder connInstanceDataBinder, - final ConnectorManager connectorManager) { + final ConnIdBundleManager connIdBundleManager, + final ExternalResourceDAO resourceDAO, + final ConnInstanceDAO connInstanceDAO, + final ConnInstanceDataBinder connInstanceDataBinder, + final ConnectorManager connectorManager) { return new ConnectorLogic( connIdBundleManager, connectorManager, @@ -76,14 +77,14 @@ public ConnectorLogic connectorLogic( @ConditionalOnMissingBean @Bean public ReconciliationLogic reconciliationLogic( - final RealmDAO realmDAO, final AnyUtilsFactory anyUtilsFactory, - final PlainSchemaDAO plainSchemaDAO, - final DerSchemaDAO derSchemaDAO, - final AnySearchDAO anySearchDAO, final AnyTypeDAO anyTypeDAO, final ExternalResourceDAO resourceDAO, + final RealmSearchDAO realmSearchDAO, + final PlainSchemaDAO plainSchemaDAO, + final DerSchemaDAO derSchemaDAO, final VirSchemaDAO virSchemaDAO, + final AnySearchDAO anySearchDAO, final VirAttrHandler virAttrHandler, final ConnectorManager connectorManager, final InboundMatcher inboundMatcher, @@ -94,7 +95,7 @@ public ReconciliationLogic reconciliationLogic( anyUtilsFactory, anyTypeDAO, resourceDAO, - realmDAO, + realmSearchDAO, plainSchemaDAO, derSchemaDAO, virSchemaDAO, @@ -121,18 +122,18 @@ public RemediationLogic remediationLogic( @ConditionalOnMissingBean @Bean public ResourceLogic resourceLogic( - final ResourceDataBinder resourceDataBinder, - final AnyUtilsFactory anyUtilsFactory, - final AnyTypeDAO anyTypeDAO, - final ExternalResourceDAO resourceDAO, - final ConnInstanceDAO connInstanceDAO, - final VirSchemaDAO virSchemaDAO, - final VirAttrHandler virAttrHandler, - final ConnInstanceDataBinder connInstanceDataBinder, - final ConnectorManager connectorManager, - final OutboundMatcher outboundMatcher, - final MappingManager mappingManager) { - + final ResourceDataBinder resourceDataBinder, + final AnyUtilsFactory anyUtilsFactory, + final AnyTypeDAO anyTypeDAO, + final ExternalResourceDAO resourceDAO, + final ConnInstanceDAO connInstanceDAO, + final VirSchemaDAO virSchemaDAO, + final VirAttrHandler virAttrHandler, + final ConnInstanceDataBinder connInstanceDataBinder, + final ConnectorManager connectorManager, + final OutboundMatcher outboundMatcher, + final MappingManager mappingManager) { + return new ResourceLogic( resourceDAO, anyTypeDAO, 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 c5aa5df0f4..c1ee8a30d6 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 @@ -59,7 +59,7 @@ 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.PlainSchemaDAO; -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.UserDAO; import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; @@ -118,7 +118,7 @@ public class ReconciliationLogic extends AbstractTransactionalLogic { protected final ExternalResourceDAO resourceDAO; - protected final RealmDAO realmDAO; + protected final RealmSearchDAO realmSearchDAO; protected final PlainSchemaDAO plainSchemaDAO; @@ -142,7 +142,7 @@ public ReconciliationLogic( final AnyUtilsFactory anyUtilsFactory, final AnyTypeDAO anyTypeDAO, final ExternalResourceDAO resourceDAO, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final PlainSchemaDAO plainSchemaDAO, final DerSchemaDAO derSchemaDAO, final VirSchemaDAO virSchemaDAO, @@ -156,7 +156,7 @@ public ReconciliationLogic( this.anyUtilsFactory = anyUtilsFactory; this.anyTypeDAO = anyTypeDAO; this.resourceDAO = resourceDAO; - this.realmDAO = realmDAO; + this.realmSearchDAO = realmSearchDAO; this.plainSchemaDAO = plainSchemaDAO; this.derSchemaDAO = derSchemaDAO; this.virSchemaDAO = virSchemaDAO; @@ -480,7 +480,8 @@ protected List pull( final Set moreAttrsToGet, final PullTaskTO pullTask) { - if (pullTask.getDestinationRealm() == null || realmDAO.findByFullPath(pullTask.getDestinationRealm()) == null) { + if (pullTask.getDestinationRealm() == null || realmSearchDAO.findByFullPath(pullTask.getDestinationRealm()) + == null) { throw new NotFoundException("Realm " + pullTask.getDestinationRealm()); } @@ -606,7 +607,7 @@ public List push( entitlement = IdRepoEntitlement.USER_SEARCH; } - Realm base = realmDAO.findByFullPath(realm). + Realm base = realmSearchDAO.findByFullPath(realm). orElseThrow(() -> new NotFoundException("Realm " + realm)); Set adminRealms = RealmUtils.getEffective(AuthContextUtils.getAuthorizations().get(entitlement), realm); @@ -692,7 +693,7 @@ public List pull(final CSVPullSpec spec, final InputStream c AnyType anyType = anyTypeDAO.findById(spec.getAnyTypeKey()). orElseThrow(() -> new NotFoundException("AnyType " + spec.getAnyTypeKey())); - if (realmDAO.findByFullPath(spec.getDestinationRealm()) == null) { + if (realmSearchDAO.findByFullPath(spec.getDestinationRealm()) == null) { throw new NotFoundException("Realm " + spec.getDestinationRealm()); } diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java index fb5a3d0b61..79a9885a1d 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java @@ -35,7 +35,7 @@ import org.apache.syncope.common.lib.types.ClientExceptionType; 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.RealmDAO; +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; @@ -49,7 +49,7 @@ public abstract class AbstractAnyLogic perContextActions = new ConcurrentHashMap<>(); public AbstractAnyLogic( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final AnyTypeDAO anyTypeDAO, final TemplateUtils templateUtils) { - this.realmDAO = realmDAO; + this.realmSearchDAO = realmSearchDAO; this.anyTypeDAO = anyTypeDAO; this.templateUtils = templateUtils; } @@ -86,7 +86,7 @@ protected List getActions(final Realm realm) { @SuppressWarnings("unchecked") protected Pair> beforeCreate(final C input) { - Realm realm = realmDAO.findByFullPath(input.getRealm()).orElseThrow(() -> { + Realm realm = realmSearchDAO.findByFullPath(input.getRealm()).orElseThrow(() -> { SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm); sce.getElements().add(input.getRealm()); return sce; @@ -121,7 +121,7 @@ protected Pair> beforeCreate(final C input) { @SuppressWarnings("unchecked") protected Pair> beforeUpdate(final U input, final String realmPath) { - Realm realm = realmDAO.findByFullPath(realmPath).orElseThrow(() -> { + Realm realm = realmSearchDAO.findByFullPath(realmPath).orElseThrow(() -> { SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm); sce.getElements().add(realmPath); return sce; @@ -141,7 +141,7 @@ protected Pair> beforeUpdate(final U input, final String r @SuppressWarnings("unchecked") protected Pair> beforeDelete(final TO input) { - Realm realm = realmDAO.findByFullPath(input.getRealm()).orElseThrow(() -> { + Realm realm = realmSearchDAO.findByFullPath(input.getRealm()).orElseThrow(() -> { SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm); sce.getElements().add(input.getRealm()); return sce; diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java index 3255679550..b30d41c3a7 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java @@ -44,7 +44,7 @@ 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.RealmDAO; +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; @@ -74,7 +74,7 @@ public class AnyObjectLogic extends AbstractAnyLogic search( throw new UnsupportedOperationException("Need to specify " + AnyType.class.getSimpleName()); } - Realm base = realmDAO.findByFullPath(realm). + Realm base = realmSearchDAO.findByFullPath(realm). orElseThrow(() -> new NotFoundException("Realm " + realm)); Set authRealms = RealmUtils.getEffective( diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java index 7554a95749..edbc18263b 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java @@ -49,7 +49,7 @@ 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.RealmDAO; +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; @@ -107,7 +107,7 @@ public class GroupLogic extends AbstractAnyLogic { protected final EntityFactory entityFactory; public GroupLogic( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final AnyTypeDAO anyTypeDAO, final TemplateUtils templateUtils, final UserDAO userDAO, @@ -123,7 +123,7 @@ public GroupLogic( final SchedulerFactoryBean scheduler, final EntityFactory entityFactory) { - super(realmDAO, anyTypeDAO, templateUtils); + super(realmSearchDAO, anyTypeDAO, templateUtils); this.userDAO = userDAO; this.groupDAO = groupDAO; @@ -169,7 +169,7 @@ public Page search( final boolean recursive, final boolean details) { - Realm base = realmDAO.findByFullPath(realm). + Realm base = realmSearchDAO.findByFullPath(realm). orElseThrow(() -> new NotFoundException("Realm " + realm)); Set authRealms = RealmUtils.getEffective( diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java index fe3f1b34f6..f726e813bc 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java @@ -61,6 +61,7 @@ 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.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; import org.apache.syncope.core.persistence.api.dao.ReportDAO; import org.apache.syncope.core.persistence.api.dao.ReportExecDAO; @@ -208,16 +209,16 @@ public AccessTokenLogic accessTokenLogic( @ConditionalOnMissingBean @Bean public AnyObjectLogic anyObjectLogic( - final AnyObjectDataBinder binder, - final TemplateUtils templateUtils, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final AnyTypeDAO anyTypeDAO, + final TemplateUtils templateUtils, final AnyObjectDAO anyObjectDAO, final AnySearchDAO anySearchDAO, + final AnyObjectDataBinder binder, final AnyObjectProvisioningManager provisioningManager) { return new AnyObjectLogic( - realmDAO, + realmSearchDAO, anyTypeDAO, templateUtils, anyObjectDAO, @@ -316,24 +317,24 @@ public DynRealmLogic dynRealmLogic( @ConditionalOnMissingBean @Bean public GroupLogic groupLogic( - final GroupProvisioningManager provisioningManager, - final JobManager jobManager, - final TemplateUtils templateUtils, - final EntityFactory entityFactory, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final AnyTypeDAO anyTypeDAO, + final TemplateUtils templateUtils, final UserDAO userDAO, final GroupDAO groupDAO, + final SecurityProperties securityProperties, final AnySearchDAO anySearchDAO, - final SchedulerFactoryBean scheduler, + final ImplementationDAO implementationDAO, final TaskDAO taskDAO, final GroupDataBinder groupDataBinder, + final GroupProvisioningManager provisioningManager, final TaskDataBinder taskDataBinder, - final ImplementationDAO implementationDAO, - final SecurityProperties securityProperties) { + final JobManager jobManager, + final SchedulerFactoryBean scheduler, + final EntityFactory entityFactory) { return new GroupLogic( - realmDAO, + realmSearchDAO, anyTypeDAO, templateUtils, userDAO, @@ -410,18 +411,20 @@ public PolicyLogic policyLogic( @ConditionalOnMissingBean @Bean public RealmLogic realmLogic( - final RealmDataBinder binder, final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final AnySearchDAO anySearchDAO, final TaskDAO taskDAO, final CASSPClientAppDAO casSPClientAppDAO, final OIDCRPClientAppDAO oidcRPClientAppDAO, final SAML2SPClientAppDAO saml2SPClientAppDAO, + final RealmDataBinder binder, final PropagationManager propagationManager, final PropagationTaskExecutor taskExecutor) { return new RealmLogic( realmDAO, + realmSearchDAO, anySearchDAO, taskDAO, casSPClientAppDAO, @@ -496,7 +499,7 @@ public SecurityQuestionLogic securityQuestionLogic( @ConditionalOnMissingBean @Bean public SyncopeLogic syncopeLogic( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final AnyTypeDAO anyTypeDAO, final GroupDAO groupDAO, final AnySearchDAO anySearchDAO, @@ -505,7 +508,7 @@ public SyncopeLogic syncopeLogic( final ContentExporter exporter) { return new SyncopeLogic( - realmDAO, + realmSearchDAO, anyTypeDAO, groupDAO, anySearchDAO, @@ -546,10 +549,9 @@ public TaskLogic taskLogic( @ConditionalOnMissingBean @Bean public UserLogic userLogic( - final UserDataBinder binder, - final TemplateUtils templateUtils, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final AnyTypeDAO anyTypeDAO, + final TemplateUtils templateUtils, final UserDAO userDAO, final GroupDAO groupDAO, final AnySearchDAO anySearchDAO, @@ -557,12 +559,13 @@ public UserLogic userLogic( final AccessTokenDAO accessTokenDAO, final DelegationDAO delegationDAO, final ConfParamOps confParamOps, + final UserDataBinder binder, final UserProvisioningManager provisioningManager, final SyncopeLogic syncopeLogic, final RuleEnforcer ruleEnforcer) { return new UserLogic( - realmDAO, + realmSearchDAO, anyTypeDAO, templateUtils, userDAO, diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java index 5bbf0f2c9f..417dac1922 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java @@ -39,6 +39,7 @@ 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.AnyCond; @@ -63,6 +64,8 @@ public class RealmLogic extends AbstractTransactionalLogic { protected final RealmDAO realmDAO; + protected final RealmSearchDAO realmSearchDAO; + protected final AnySearchDAO searchDAO; protected final TaskDAO taskDAO; @@ -81,6 +84,7 @@ public class RealmLogic extends AbstractTransactionalLogic { public RealmLogic( final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final AnySearchDAO searchDAO, final TaskDAO taskDAO, final CASSPClientAppDAO casSPClientAppDAO, @@ -91,6 +95,7 @@ public RealmLogic( final PropagationTaskExecutor taskExecutor) { this.realmDAO = realmDAO; + this.realmSearchDAO = realmSearchDAO; this.searchDAO = searchDAO; this.taskDAO = taskDAO; this.casSPClientAppDAO = casSPClientAppDAO; @@ -110,11 +115,11 @@ public Page search( Realm baseRealm = base == null ? realmDAO.getRoot() - : realmDAO.findByFullPath(base).orElseThrow(() -> new NotFoundException("Realm " + base)); + : realmSearchDAO.findByFullPath(base).orElseThrow(() -> new NotFoundException("Realm " + base)); - long count = realmDAO.countDescendants(baseRealm.getFullPath(), keyword); + long count = realmSearchDAO.countDescendants(baseRealm.getFullPath(), keyword); - List result = realmDAO.findDescendants(baseRealm.getFullPath(), keyword, pageable).stream(). + List result = realmSearchDAO.findDescendants(baseRealm.getFullPath(), keyword, pageable).stream(). map(realm -> binder.getRealmTO( realm, AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.REALM_SEARCH).stream(). @@ -129,7 +134,7 @@ public Page search( public ProvisioningResult create(final String parentPath, final RealmTO realmTO) { Realm parent; if (StringUtils.isBlank(realmTO.getParent())) { - parent = realmDAO.findByFullPath(parentPath). + parent = realmSearchDAO.findByFullPath(parentPath). orElseThrow(() -> new NotFoundException("Realm " + parentPath)); realmTO.setParent(parent.getFullPath()); @@ -145,7 +150,7 @@ public ProvisioningResult create(final String parentPath, final RealmTO } String fullPath = StringUtils.appendIfMissing(parent.getFullPath(), "/") + realmTO.getName(); - if (realmDAO.findByFullPath(fullPath).isPresent()) { + if (realmSearchDAO.findByFullPath(fullPath).isPresent()) { throw new DuplicateException(fullPath); } @@ -165,7 +170,7 @@ public ProvisioningResult create(final String parentPath, final RealmTO @PreAuthorize("hasRole('" + IdRepoEntitlement.REALM_UPDATE + "')") public ProvisioningResult update(final RealmTO realmTO) { - Realm realm = realmDAO.findByFullPath(realmTO.getFullPath()). + Realm realm = realmSearchDAO.findByFullPath(realmTO.getFullPath()). orElseThrow(() -> new NotFoundException("Realm " + realmTO.getFullPath())); Map, Set> beforeAttrs = propagationManager.prepareAttrs(realm); @@ -189,10 +194,10 @@ public ProvisioningResult update(final RealmTO realmTO) { @PreAuthorize("hasRole('" + IdRepoEntitlement.REALM_DELETE + "')") public ProvisioningResult delete(final String fullPath) { - Realm realm = realmDAO.findByFullPath(fullPath). + Realm realm = realmSearchDAO.findByFullPath(fullPath). orElseThrow(() -> new NotFoundException("Realm " + fullPath)); - if (!realmDAO.findChildren(realm).isEmpty()) { + if (!realmSearchDAO.findChildren(realm).isEmpty()) { throw SyncopeClientException.build(ClientExceptionType.RealmContains); } @@ -251,7 +256,7 @@ protected RealmTO resolveReference(final Method method, final Object... args) if (fullPath != null) { try { - return binder.getRealmTO(realmDAO.findByFullPath(fullPath).orElseThrow(), true); + return binder.getRealmTO(realmSearchDAO.findByFullPath(fullPath).orElseThrow(), true); } catch (Throwable e) { LOG.debug("Unresolved reference", e); throw new UnresolvedReferenceException(e); diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java index d1318735e8..566c0d04ac 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java @@ -34,7 +34,7 @@ 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.RealmDAO; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.search.AnyCond; import org.apache.syncope.core.persistence.api.dao.search.AttrCond; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; @@ -52,7 +52,7 @@ @Transactional(readOnly = true) public class SyncopeLogic extends AbstractLogic { - protected final RealmDAO realmDAO; + protected final RealmSearchDAO realmSearchDAO; protected final AnyTypeDAO anyTypeDAO; @@ -67,7 +67,7 @@ public class SyncopeLogic extends AbstractLogic { protected final ContentExporter exporter; public SyncopeLogic( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final AnyTypeDAO anyTypeDAO, final GroupDAO groupDAO, final AnySearchDAO anySearchDAO, @@ -75,7 +75,7 @@ public SyncopeLogic( final ConfParamOps confParamOps, final ContentExporter exporter) { - this.realmDAO = realmDAO; + this.realmSearchDAO = realmSearchDAO; this.anyTypeDAO = anyTypeDAO; this.groupDAO = groupDAO; this.anySearchDAO = anySearchDAO; @@ -102,7 +102,7 @@ public Page searchAssignableGroups( final String term, final Pageable pageable) { - Realm base = realmDAO.findByFullPath(realm). + Realm base = realmSearchDAO.findByFullPath(realm). orElseThrow(() -> new NotFoundException("Realm " + realm)); AnyCond termCond; diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java index 6593dc1d26..50bb44bd0d 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java @@ -58,7 +58,7 @@ 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.NotFoundException; -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.UserDAO; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; import org.apache.syncope.core.persistence.api.entity.Entity; @@ -113,7 +113,7 @@ public class UserLogic extends AbstractAnyLogic { protected final RuleEnforcer ruleEnforcer; public UserLogic( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final AnyTypeDAO anyTypeDAO, final TemplateUtils templateUtils, final UserDAO userDAO, @@ -128,7 +128,7 @@ public UserLogic( final SyncopeLogic syncopeLogic, final RuleEnforcer ruleEnforcer) { - super(realmDAO, anyTypeDAO, templateUtils); + super(realmSearchDAO, anyTypeDAO, templateUtils); this.userDAO = userDAO; this.groupDAO = groupDAO; @@ -172,7 +172,7 @@ public Page search( final boolean recursive, final boolean details) { - Realm base = realmDAO.findByFullPath(realm). + Realm base = realmSearchDAO.findByFullPath(realm). orElseThrow(() -> new NotFoundException("Realm " + realm)); Set authRealms = RealmUtils.getEffective( @@ -393,7 +393,7 @@ public void compliance(final ComplianceQuery query) { Realm realm = null; if (StringUtils.isNotBlank(query.getRealm())) { - realm = realmDAO.findByFullPath(query.getRealm()). + realm = realmSearchDAO.findByFullPath(query.getRealm()). orElseThrow(() -> new NotFoundException("Realm " + query.getRealm())); } Set resources = query.getResources().stream(). diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmDAO.java index b5f3e0aec9..23bbbdc1d0 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmDAO.java @@ -19,7 +19,6 @@ package org.apache.syncope.core.persistence.api.dao; import java.util.List; -import java.util.Optional; import java.util.regex.Pattern; import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Implementation; @@ -36,25 +35,11 @@ public interface RealmDAO extends DAO { Realm getRoot(); - Optional findByFullPath(String fullPath); - - List findByName(String name); - List findByResources(ExternalResource resource); - long countDescendants(String base, String keyword); - - List findDescendants(String base, String keyword, Pageable pageable); - - List findDescendants(String base, String prefix); - List findByPolicy(T policy); List findByActionsContaining(Implementation logicActions); - List findAncestors(Realm realm); - - List findChildren(Realm realm); - Page findAll(Pageable pageable); } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmSearchDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmSearchDAO.java new file mode 100644 index 0000000000..9c968c0bf7 --- /dev/null +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmSearchDAO.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.core.persistence.api.dao; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.springframework.data.domain.Pageable; + +public interface RealmSearchDAO { + + Optional findByFullPath(String fullPath); + + List findByName(String name); + + List findChildren(Realm realm); + + long countDescendants(String base, String keyword); + + List findDescendants(String base, String keyword, Pageable pageable); + + List findDescendants(String base, String prefix); + + default void findAncestors(final List result, final Realm realm) { + if (realm.getParent() != null && !result.contains(realm.getParent())) { + result.add(realm.getParent()); + findAncestors(result, realm.getParent()); + } + } + + default List findAncestors(Realm realm) { + List result = new ArrayList<>(); + result.add(realm); + findAncestors(result, realm); + return result; + } +} diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnySearchDAO.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnySearchDAO.java index b37c72319e..b580223621 100644 --- a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnySearchDAO.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnySearchDAO.java @@ -40,7 +40,7 @@ import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; 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.RealmDAO; +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.AbstractSearchCond; import org.apache.syncope.core.persistence.api.dao.search.AnyCond; @@ -144,7 +144,7 @@ protected static String key(final AttrSchemaType schemaType) { return key; } - protected final RealmDAO realmDAO; + protected final RealmSearchDAO realmSearchDAO; protected final DynRealmDAO dynRealmDAO; @@ -163,7 +163,7 @@ protected static String key(final AttrSchemaType schemaType) { protected final PlainAttrValidationManager validator; public AbstractAnySearchDAO( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final DynRealmDAO dynRealmDAO, final UserDAO userDAO, final GroupDAO groupDAO, @@ -173,7 +173,7 @@ public AbstractAnySearchDAO( final AnyUtilsFactory anyUtilsFactory, final PlainAttrValidationManager validator) { - this.realmDAO = realmDAO; + this.realmSearchDAO = realmSearchDAO; this.dynRealmDAO = dynRealmDAO; this.userDAO = userDAO; this.groupDAO = groupDAO; @@ -219,7 +219,7 @@ public > List search( final SearchCond cond, final List orderBy, final AnyTypeKind kind) { return search( - realmDAO.getRoot(), + realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElse(null), true, SyncopeConstants.FULL_ADMIN_REALMS, cond, diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/MyJPAJSONPersistenceContext.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/MyJPAJSONPersistenceContext.java index a8092c6d76..1b26a60c3c 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/MyJPAJSONPersistenceContext.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/MyJPAJSONPersistenceContext.java @@ -29,7 +29,7 @@ import org.apache.syncope.core.persistence.api.dao.GroupDAO; import org.apache.syncope.core.persistence.api.dao.JPAJSONAnyDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; -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.UserDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; @@ -62,7 +62,7 @@ public JPAJSONAnyDAO anyDAO(final @Lazy PlainSchemaDAO plainSchemaDAO, final Ent @ConditionalOnMissingBean(name = "myJPAJSONAnySearchDAO") @Bean public AnySearchDAO anySearchDAO( - final @Lazy RealmDAO realmDAO, + final @Lazy RealmSearchDAO realmSearchDAO, final @Lazy DynRealmDAO dynRealmDAO, final @Lazy UserDAO userDAO, final @Lazy GroupDAO groupDAO, @@ -75,7 +75,7 @@ public AnySearchDAO anySearchDAO( final EntityManager entityManager) { return new MyJPAJSONAnySearchDAO( - realmDAO, + realmSearchDAO, dynRealmDAO, userDAO, groupDAO, diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/OJPAJSONPersistenceContext.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/OJPAJSONPersistenceContext.java index 36ff906d4f..028bdff336 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/OJPAJSONPersistenceContext.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/OJPAJSONPersistenceContext.java @@ -29,7 +29,7 @@ import org.apache.syncope.core.persistence.api.dao.GroupDAO; import org.apache.syncope.core.persistence.api.dao.JPAJSONAnyDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; -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.UserDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; @@ -62,7 +62,7 @@ public JPAJSONAnyDAO anyDAO(final @Lazy PlainSchemaDAO plainSchemaDAO, final Ent @ConditionalOnMissingBean(name = "oJPAJSONAnySearchDAO") @Bean public AnySearchDAO anySearchDAO( - final @Lazy RealmDAO realmDAO, + final @Lazy RealmSearchDAO realmSearchDAO, final @Lazy DynRealmDAO dynRealmDAO, final @Lazy UserDAO userDAO, final @Lazy GroupDAO groupDAO, @@ -75,7 +75,7 @@ public AnySearchDAO anySearchDAO( final EntityManager entityManager) { return new OJPAJSONAnySearchDAO( - realmDAO, + realmSearchDAO, dynRealmDAO, userDAO, groupDAO, diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/PGJPAJSONPersistenceContext.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/PGJPAJSONPersistenceContext.java index 0887a9570b..b495a0e98c 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/PGJPAJSONPersistenceContext.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/PGJPAJSONPersistenceContext.java @@ -29,7 +29,7 @@ import org.apache.syncope.core.persistence.api.dao.GroupDAO; import org.apache.syncope.core.persistence.api.dao.JPAJSONAnyDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; -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.UserDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; @@ -62,7 +62,7 @@ public JPAJSONAnyDAO anyDAO(final @Lazy PlainSchemaDAO plainSchemaDAO, final Ent @ConditionalOnMissingBean(name = "pgJPAJSONAnySearchDAO") @Bean public AnySearchDAO anySearchDAO( - final @Lazy RealmDAO realmDAO, + final @Lazy RealmSearchDAO realmSearchDAO, final @Lazy DynRealmDAO dynRealmDAO, final @Lazy UserDAO userDAO, final @Lazy GroupDAO groupDAO, @@ -75,7 +75,7 @@ public AnySearchDAO anySearchDAO( final EntityManager entityManager) { return new PGJPAJSONAnySearchDAO( - realmDAO, + realmSearchDAO, dynRealmDAO, userDAO, groupDAO, diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java index 43287537f0..f526e570fe 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAnySearchDAO.java @@ -32,7 +32,7 @@ import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; 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.RealmDAO; +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.AnyCond; import org.apache.syncope.core.persistence.api.dao.search.AttrCond; @@ -50,7 +50,7 @@ public class MyJPAJSONAnySearchDAO extends JPAAnySearchDAO { public MyJPAJSONAnySearchDAO( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final DynRealmDAO dynRealmDAO, final UserDAO userDAO, final GroupDAO groupDAO, @@ -63,7 +63,7 @@ public MyJPAJSONAnySearchDAO( final EntityManager entityManager) { super( - realmDAO, + realmSearchDAO, dynRealmDAO, userDAO, groupDAO, diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAnySearchDAO.java index 75b64d3612..063d0592ea 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAnySearchDAO.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAnySearchDAO.java @@ -33,7 +33,7 @@ import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; 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.RealmDAO; +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.AnyCond; import org.apache.syncope.core.persistence.api.dao.search.AttrCond; @@ -47,7 +47,7 @@ public class OJPAJSONAnySearchDAO extends JPAAnySearchDAO { public OJPAJSONAnySearchDAO( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final DynRealmDAO dynRealmDAO, final UserDAO userDAO, final GroupDAO groupDAO, @@ -60,7 +60,7 @@ public OJPAJSONAnySearchDAO( final EntityManager entityManager) { super( - realmDAO, + realmSearchDAO, dynRealmDAO, userDAO, groupDAO, diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java index 49e8c7a7bd..7662cc589c 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java @@ -40,7 +40,7 @@ import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; 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.RealmDAO; +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.AnyCond; import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond; @@ -86,7 +86,7 @@ protected static String escapeIfString(final String value, final boolean isStr) } public PGJPAJSONAnySearchDAO( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final DynRealmDAO dynRealmDAO, final UserDAO userDAO, final GroupDAO groupDAO, @@ -99,7 +99,7 @@ public PGJPAJSONAnySearchDAO( final EntityManager entityManager) { super( - realmDAO, + realmSearchDAO, dynRealmDAO, userDAO, groupDAO, @@ -610,7 +610,7 @@ protected String getQuery( if (JAXRSService.PARAM_REALM.equals(cond.getSchema()) && !SyncopeConstants.UUID_PATTERN.matcher(cond.getExpression()).matches()) { - Realm realm = realmDAO.findByFullPath(cond.getExpression()). + Realm realm = realmSearchDAO.findByFullPath(cond.getExpression()). orElseThrow(() -> new IllegalArgumentException("Invalid Realm full path: " + cond.getExpression())); cond.setExpression(realm.getKey()); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java index 883ba8c9c5..03df041d4f 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java @@ -62,6 +62,7 @@ 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.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; import org.apache.syncope.core.persistence.api.dao.RemediationDAO; import org.apache.syncope.core.persistence.api.dao.ReportDAO; @@ -96,6 +97,7 @@ import org.apache.syncope.core.persistence.jpa.dao.JPAPlainAttrValueDAO; import org.apache.syncope.core.persistence.jpa.dao.JPAPolicyDAO; import org.apache.syncope.core.persistence.jpa.dao.JPARealmDAO; +import org.apache.syncope.core.persistence.jpa.dao.JPARealmSearchDAO; import org.apache.syncope.core.persistence.jpa.dao.JPATaskDAO; import org.apache.syncope.core.persistence.jpa.dao.JPATaskExecDAO; import org.apache.syncope.core.persistence.jpa.dao.repo.AccessTokenRepo; @@ -302,10 +304,10 @@ public XMLContentLoader xmlContentLoader( @Bean public XMLContentExporter xmlContentExporter( final DomainHolder domainHolder, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final EntityManagerFactory entityManagerFactory) { - return new XMLContentExporter(domainHolder, realmDAO, entityManagerFactory); + return new XMLContentExporter(domainHolder, realmSearchDAO, entityManagerFactory); } @ConditionalOnMissingBean @@ -429,7 +431,7 @@ public AnyObjectDAO anyObjectDAO( @ConditionalOnMissingBean @Bean public AnySearchDAO anySearchDAO( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final @Lazy DynRealmDAO dynRealmDAO, final @Lazy UserDAO userDAO, final @Lazy GroupDAO groupDAO, @@ -442,7 +444,7 @@ public AnySearchDAO anySearchDAO( final EntityManager entityManager) { return new JPAAnySearchDAO( - realmDAO, + realmSearchDAO, dynRealmDAO, userDAO, groupDAO, @@ -843,14 +845,21 @@ public PolicyDAO policyDAO( entityManager); } - @ConditionalOnMissingBean(name = { "realmDAO", "delegateRealmDAO" }) - @Bean(name = { "realmDAO", "delegateRealmDAO" }) + @ConditionalOnMissingBean + @Bean public RealmDAO realmDAO( final @Lazy RoleDAO roleDAO, + final RealmSearchDAO realmSearchDAO, final ApplicationEventPublisher publisher, final EntityManager entityManager) { - return new JPARealmDAO(roleDAO, publisher, entityManager); + return new JPARealmDAO(roleDAO, realmSearchDAO, publisher, entityManager); + } + + @ConditionalOnMissingBean + @Bean + public RealmSearchDAO realmSearchDAO(final EntityManager entityManager) { + return new JPARealmSearchDAO(entityManager); } @ConditionalOnMissingBean @@ -1018,13 +1027,13 @@ public SRARouteDAO sraRouteDAO(final JpaRepositoryFactory jpaRepositoryFactory) @ConditionalOnMissingBean @Bean public TaskDAO taskDAO( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final RemediationDAO remediationDAO, final TaskUtilsFactory taskUtilsFactory, final SecurityProperties securityProperties, final EntityManager entityManager) { - return new JPATaskDAO(realmDAO, remediationDAO, taskUtilsFactory, securityProperties, entityManager); + return new JPATaskDAO(realmSearchDAO, remediationDAO, taskUtilsFactory, securityProperties, entityManager); } @ConditionalOnMissingBean diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java index 00954a0e6a..a7b61972b4 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java @@ -67,7 +67,7 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.dao.AuditEntryDAO; -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.utils.FormatUtils; import org.apache.syncope.core.persistence.common.content.AbstractXMLContentExporter; import org.apache.syncope.core.persistence.common.content.MultiParentNode; @@ -262,17 +262,17 @@ protected static List sortByForeignKeys( protected final DomainHolder domainHolder; - protected final RealmDAO realmDAO; + protected final RealmSearchDAO realmSearchDAO; protected final EntityManagerFactory entityManagerFactory; public XMLContentExporter( final DomainHolder domainHolder, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final EntityManagerFactory entityManagerFactory) { this.domainHolder = domainHolder; - this.realmDAO = realmDAO; + this.realmSearchDAO = realmSearchDAO; this.entityManagerFactory = entityManagerFactory; } @@ -355,7 +355,7 @@ protected void exportTable( if (tableName.equalsIgnoreCase(JPARealm.TABLE)) { List> realmRows = new ArrayList<>(rows); rows.clear(); - realmDAO.findDescendants(SyncopeConstants.ROOT_REALM, null, Pageable.unpaged()). + realmSearchDAO.findDescendants(SyncopeConstants.ROOT_REALM, null, Pageable.unpaged()). forEach(realm -> realmRows.stream().filter(row -> { String id = Optional.ofNullable(row.get("ID")).orElseGet(() -> row.get("id")); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java index 6da93ac55e..f751ca75d9 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java @@ -48,7 +48,7 @@ import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; 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.RealmDAO; +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.AnyCond; import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond; @@ -108,7 +108,7 @@ protected static void fillWithParameters(final Query query, final List p protected final EntityManager entityManager; public JPAAnySearchDAO( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final DynRealmDAO dynRealmDAO, final UserDAO userDAO, final GroupDAO groupDAO, @@ -121,7 +121,7 @@ public JPAAnySearchDAO( final EntityManager entityManager) { super( - realmDAO, + realmSearchDAO, dynRealmDAO, userDAO, groupDAO, @@ -177,14 +177,14 @@ protected Triple, Set> getAdminRealmsFilter( goRealm -> groupOwners.add(goRealm.getRight()), () -> { if (realmPath.startsWith("/")) { - Realm realm = realmDAO.findByFullPath(realmPath).orElseThrow(() -> { + Realm realm = realmSearchDAO.findByFullPath(realmPath).orElseThrow(() -> { SyncopeClientException noRealm = SyncopeClientException.build(ClientExceptionType.InvalidRealm); noRealm.getElements().add("Invalid realm specified: " + realmPath); return noRealm; }); - realmKeys.addAll(realmDAO.findDescendants(realm.getFullPath(), base.getFullPath())); + realmKeys.addAll(realmSearchDAO.findDescendants(realm.getFullPath(), base.getFullPath())); } else { dynRealmDAO.findById(realmPath).ifPresentOrElse( dynRealm -> dynRealmKeys.add(dynRealm.getKey()), @@ -1109,7 +1109,7 @@ protected String getQuery( if (JAXRSService.PARAM_REALM.equals(cond.getSchema()) && !SyncopeConstants.UUID_PATTERN.matcher(cond.getExpression()).matches()) { - Realm realm = realmDAO.findByFullPath(cond.getExpression()). + Realm realm = realmSearchDAO.findByFullPath(cond.getExpression()). orElseThrow(() -> new IllegalArgumentException("Invalid Realm full path: " + cond.getExpression())); cond.setExpression(realm.getKey()); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java index 120676c9c2..a1662fb483 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java @@ -27,8 +27,8 @@ import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.core.persistence.api.dao.MalformedPathException; 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.RoleDAO; import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Implementation; @@ -58,45 +58,23 @@ public class JPARealmDAO implements RealmDAO { protected static final Logger LOG = LoggerFactory.getLogger(RealmDAO.class); - protected static int setParameter(final List parameters, final Object parameter) { - parameters.add(parameter); - return parameters.size(); - } - - protected static StringBuilder buildDescendantQuery( - final String base, - final String keyword, - final List parameters) { - - StringBuilder queryString = new StringBuilder("SELECT e FROM "). - append(JPARealm.class.getSimpleName()).append(" e "). - append("WHERE (e.fullPath=?"). - append(setParameter(parameters, base)). - append(" OR e.fullPath LIKE ?"). - append(setParameter(parameters, SyncopeConstants.ROOT_REALM.equals(base) ? "/%" : base + "/%")). - append(')'); - - if (keyword != null) { - queryString.append(" AND LOWER(e.name) LIKE ?"). - append(setParameter(parameters, "%" + keyword.replaceAll("_", "\\\\_").toLowerCase() + "%")); - } - - return queryString; - } - protected final RoleDAO roleDAO; + protected final RealmSearchDAO realmSearchDAO; + protected final ApplicationEventPublisher publisher; protected final EntityManager entityManager; public JPARealmDAO( final RoleDAO roleDAO, + final RealmSearchDAO realmSearchDAO, final ApplicationEventPublisher publisher, final EntityManager entityManager) { - this.publisher = publisher; this.roleDAO = roleDAO; + this.realmSearchDAO = realmSearchDAO; + this.publisher = publisher; this.entityManager = entityManager; } @@ -127,102 +105,10 @@ public Optional findById(final String key) { return Optional.ofNullable(entityManager.find(JPARealm.class, key)); } - @Transactional(readOnly = true) - @Override - public Optional findByFullPath(final String fullPath) { - if (SyncopeConstants.ROOT_REALM.equals(fullPath)) { - return Optional.of(getRoot()); - } - - if (StringUtils.isBlank(fullPath) || !PATH_PATTERN.matcher(fullPath).matches()) { - throw new MalformedPathException(fullPath); - } - - TypedQuery query = entityManager.createQuery( - "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE e.fullPath=:fullPath", Realm.class); - query.setParameter("fullPath", fullPath); - - Realm result = null; - try { - result = query.getSingleResult(); - } catch (NoResultException e) { - LOG.debug("Realm with fullPath {} not found", fullPath, e); - } - - return Optional.ofNullable(result); - } - - @Override - public List findByName(final String name) { - TypedQuery query = entityManager.createQuery( - "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE e.name=:name", Realm.class); - query.setParameter("name", name); - - return query.getResultList(); - } - - @Override - public long countDescendants(final String base, final String keyword) { - List parameters = new ArrayList<>(); - - StringBuilder queryString = buildDescendantQuery(base, keyword, parameters); - Query query = entityManager.createQuery(StringUtils.replaceOnce( - queryString.toString(), - "SELECT e ", - "SELECT COUNT(e) ")); - - for (int i = 1; i <= parameters.size(); i++) { - query.setParameter(i, parameters.get(i - 1)); - } - - return ((Number) query.getSingleResult()).longValue(); - } - - @Override - public List findDescendants(final String base, final String keyword, final Pageable pageable) { - List parameters = new ArrayList<>(); - - StringBuilder queryString = buildDescendantQuery(base, keyword, parameters); - TypedQuery query = entityManager.createQuery( - queryString.append(" ORDER BY e.fullPath").toString(), Realm.class); - - for (int i = 1; i <= parameters.size(); i++) { - query.setParameter(i, parameters.get(i - 1)); - } - - if (pageable.isPaged()) { - query.setFirstResult(pageable.getPageSize() * pageable.getPageNumber()); - query.setMaxResults(pageable.getPageSize()); - } - - return query.getResultList(); - } - - @Override - public List findDescendants(final String base, final String prefix) { - List parameters = new ArrayList<>(); - - StringBuilder queryString = buildDescendantQuery(base, null, parameters); - TypedQuery query = entityManager.createQuery(queryString. - append(" AND (e.fullPath=?"). - append(setParameter(parameters, prefix)). - append(" OR e.fullPath LIKE ?"). - append(setParameter(parameters, SyncopeConstants.ROOT_REALM.equals(prefix) ? "/%" : prefix + "/%")). - append(')'). - append(" ORDER BY e.fullPath").toString(), - Realm.class); - - for (int i = 1; i <= parameters.size(); i++) { - query.setParameter(i, parameters.get(i - 1)); - } - - return query.getResultList().stream().map(Realm::getKey).toList(); - } - protected List findSamePolicyChildren(final Realm realm, final T policy) { List result = new ArrayList<>(); - findChildren(realm).stream(). + realmSearchDAO.findChildren(realm).stream(). filter(child -> (policy instanceof AccountPolicy && child.getAccountPolicy() == null || policy.equals(child.getAccountPolicy())) || (policy instanceof PasswordPolicy @@ -270,30 +156,6 @@ public List findByPolicy(final T policy) { return result; } - protected void findAncestors(final List result, final Realm realm) { - if (realm.getParent() != null && !result.contains(realm.getParent())) { - result.add(realm.getParent()); - findAncestors(result, realm.getParent()); - } - } - - @Override - public List findAncestors(final Realm realm) { - List result = new ArrayList<>(); - result.add(realm); - findAncestors(result, realm); - return result; - } - - @Override - public List findChildren(final Realm realm) { - TypedQuery query = entityManager.createQuery( - "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE e.parent=:realm", Realm.class); - query.setParameter("realm", realm); - - return query.getResultList(); - } - @Override public List findByActionsContaining(final Implementation logicActions) { TypedQuery query = entityManager.createQuery( @@ -352,7 +214,7 @@ public S save(final S realm) { S merged = entityManager.merge(realm); if (!fullPathAfter.equals(fullPathBefore)) { - findChildren(realm).forEach(this::save); + realmSearchDAO.findChildren(realm).forEach(this::save); } publisher.publishEvent( @@ -372,7 +234,7 @@ public void delete(final Realm realm) { return; } - findDescendants(realm.getFullPath(), null, Pageable.unpaged()).forEach(toBeDeleted -> { + realmSearchDAO.findDescendants(realm.getFullPath(), null, Pageable.unpaged()).forEach(toBeDeleted -> { roleDAO.findByRealms(toBeDeleted).forEach(role -> role.getRealms().remove(toBeDeleted)); toBeDeleted.setParent(null); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmSearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmSearchDAO.java new file mode 100644 index 0000000000..461f393384 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmSearchDAO.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.core.persistence.jpa.dao; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.NoResultException; +import jakarta.persistence.Query; +import jakarta.persistence.TypedQuery; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.core.persistence.api.dao.MalformedPathException; +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.entity.Realm; +import org.apache.syncope.core.persistence.jpa.entity.JPARealm; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Pageable; +import org.springframework.transaction.annotation.Transactional; + +public class JPARealmSearchDAO implements RealmSearchDAO { + + protected static final Logger LOG = LoggerFactory.getLogger(RealmSearchDAO.class); + + protected static int setParameter(final List parameters, final Object parameter) { + parameters.add(parameter); + return parameters.size(); + } + + protected static StringBuilder buildDescendantQuery( + final String base, + final String keyword, + final List parameters) { + + StringBuilder queryString = new StringBuilder("SELECT e FROM "). + append(JPARealm.class.getSimpleName()).append(" e "). + append("WHERE (e.fullPath=?"). + append(setParameter(parameters, base)). + append(" OR e.fullPath LIKE ?"). + append(setParameter(parameters, SyncopeConstants.ROOT_REALM.equals(base) ? "/%" : base + "/%")). + append(')'); + + if (keyword != null) { + queryString.append(" AND LOWER(e.name) LIKE ?"). + append(setParameter(parameters, "%" + keyword.replaceAll("_", "\\\\_").toLowerCase() + "%")); + } + + return queryString; + } + + protected final EntityManager entityManager; + + public JPARealmSearchDAO(final EntityManager entityManager) { + this.entityManager = entityManager; + } + + @Transactional(readOnly = true) + @Override + public Optional findByFullPath(final String fullPath) { + if (StringUtils.isBlank(fullPath) + || (!SyncopeConstants.ROOT_REALM.equals(fullPath) + && !RealmDAO.PATH_PATTERN.matcher(fullPath).matches())) { + + throw new MalformedPathException(fullPath); + } + + TypedQuery query = entityManager.createQuery( + "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE e.fullPath=:fullPath", Realm.class); + query.setParameter("fullPath", fullPath); + + Realm result = null; + try { + result = query.getSingleResult(); + } catch (NoResultException e) { + LOG.debug("Realm with fullPath {} not found", fullPath, e); + } + + return Optional.ofNullable(result); + } + + @Override + public List findByName(final String name) { + TypedQuery query = entityManager.createQuery( + "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE e.name=:name", Realm.class); + query.setParameter("name", name); + + return query.getResultList(); + } + + @Override + public List findChildren(final Realm realm) { + TypedQuery query = entityManager.createQuery( + "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE e.parent=:realm", Realm.class); + query.setParameter("realm", realm); + + return query.getResultList(); + } + + @Override + public long countDescendants(final String base, final String keyword) { + List parameters = new ArrayList<>(); + + StringBuilder queryString = buildDescendantQuery(base, keyword, parameters); + Query query = entityManager.createQuery(StringUtils.replaceOnce( + queryString.toString(), + "SELECT e ", + "SELECT COUNT(e) ")); + + for (int i = 1; i <= parameters.size(); i++) { + query.setParameter(i, parameters.get(i - 1)); + } + + return ((Number) query.getSingleResult()).longValue(); + } + + @Override + public List findDescendants(final String base, final String keyword, final Pageable pageable) { + List parameters = new ArrayList<>(); + + StringBuilder queryString = buildDescendantQuery(base, keyword, parameters); + TypedQuery query = entityManager.createQuery( + queryString.append(" ORDER BY e.fullPath").toString(), Realm.class); + + for (int i = 1; i <= parameters.size(); i++) { + query.setParameter(i, parameters.get(i - 1)); + } + + if (pageable.isPaged()) { + query.setFirstResult(pageable.getPageSize() * pageable.getPageNumber()); + query.setMaxResults(pageable.getPageSize()); + } + + return query.getResultList(); + } + + @Override + public List findDescendants(final String base, final String prefix) { + List parameters = new ArrayList<>(); + + StringBuilder queryString = buildDescendantQuery(base, null, parameters); + TypedQuery query = entityManager.createQuery(queryString. + append(" AND (e.fullPath=?"). + append(setParameter(parameters, prefix)). + append(" OR e.fullPath LIKE ?"). + append(setParameter(parameters, SyncopeConstants.ROOT_REALM.equals(prefix) ? "/%" : prefix + "/%")). + append(')'). + append(" ORDER BY e.fullPath").toString(), + Realm.class); + + for (int i = 1; i <= parameters.size(); i++) { + query.setParameter(i, parameters.get(i - 1)); + } + + return query.getResultList().stream().map(Realm::getKey).toList(); + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java index 4988090dac..87325d6668 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java @@ -38,7 +38,7 @@ import org.apache.syncope.common.lib.types.ExecStatus; import org.apache.syncope.common.lib.types.IdRepoEntitlement; import org.apache.syncope.common.lib.types.TaskType; -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.RemediationDAO; import org.apache.syncope.core.persistence.api.dao.TaskDAO; import org.apache.syncope.core.persistence.api.entity.ExternalResource; @@ -74,7 +74,7 @@ public class JPATaskDAO implements TaskDAO { protected static final Logger LOG = LoggerFactory.getLogger(TaskDAO.class); - protected final RealmDAO realmDAO; + protected final RealmSearchDAO realmSearchDAO; protected final RemediationDAO remediationDAO; @@ -85,13 +85,13 @@ public class JPATaskDAO implements TaskDAO { protected final EntityManager entityManager; public JPATaskDAO( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final RemediationDAO remediationDAO, final TaskUtilsFactory taskUtilsFactory, final SecurityProperties securityProperties, final EntityManager entityManager) { - this.realmDAO = realmDAO; + this.realmSearchDAO = realmSearchDAO; this.remediationDAO = remediationDAO; this.taskUtilsFactory = taskUtilsFactory; this.securityProperties = securityProperties; @@ -330,9 +330,10 @@ protected StringBuilder buildFindAllQuery( && !AuthContextUtils.getUsername().equals(securityProperties.getAdminUser())) { String realmKeysArg = AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.TASK_LIST).stream(). - map(realmDAO::findByFullPath). + map(realmSearchDAO::findByFullPath). filter(Optional::isPresent). - flatMap(r -> realmDAO.findDescendants(r.get().getFullPath(), null, Pageable.unpaged()).stream()). + flatMap(r -> realmSearchDAO.findDescendants(r.get().getFullPath(), null, Pageable.unpaged()). + stream()). map(Realm::getKey). distinct(). map(realmKey -> "?" + setParameter(parameters, realmKey)). 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 09d5f89f57..5dc27bd7b7 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 @@ -29,7 +29,7 @@ import org.apache.syncope.common.lib.SyncopeConstants; 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.RealmDAO; +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.anyobject.AnyObject; import org.apache.syncope.core.persistence.jpa.AbstractTest; @@ -47,7 +47,7 @@ public class AnyObjectTest extends AbstractTest { private AnyObjectDAO anyObjectDAO; @Autowired - private RealmDAO realmDAO; + private RealmSearchDAO realmSearchDAO; @Test public void findAll() { @@ -110,7 +110,7 @@ public void save() { AnyObject anyObject = entityFactory.newEntity(AnyObject.class); anyObject.setName("a name"); anyObject.setType(anyTypeDAO.findById("PRINTER").orElseThrow()); - anyObject.setRealm(realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); + anyObject.setRealm(realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); anyObject = anyObjectDAO.save(anyObject); assertNotNull(anyObject); 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 4d1cc5652e..b173bcc1a5 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 @@ -41,6 +41,7 @@ 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.RealmDAO; +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.AnyCond; @@ -99,6 +100,9 @@ public class AnySearchTest extends AbstractTest { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Autowired private RoleDAO roleDAO; @@ -796,7 +800,7 @@ public void issueSYNCOPE980() { AnyObject anyObject = entityFactory.newEntity(AnyObject.class); anyObject.setName("one"); anyObject.setType(service); - anyObject.setRealm(realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); + anyObject.setRealm(realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); AMembership membership = entityFactory.newEntity(AMembership.class); membership.setRightEnd(citizen); 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 4e23621055..12a71399bb 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 @@ -26,7 +26,7 @@ import org.apache.syncope.common.lib.SyncopeConstants; 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.RealmDAO; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.jpa.AbstractTest; import org.junit.jupiter.api.Test; @@ -40,7 +40,7 @@ public class GroupTest extends AbstractTest { private GroupDAO groupDAO; @Autowired - private RealmDAO realmDAO; + private RealmSearchDAO realmSearchDAO; @Autowired private AnyTypeDAO anyTypeDAO; @@ -71,7 +71,7 @@ public void findKeysByNamePattern() { public void save() { Group group = entityFactory.newEntity(Group.class); group.setName("secondChild"); - group.setRealm(realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); + group.setRealm(realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); group = groupDAO.save(group); 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 a8624f2017..cb8084b779 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 @@ -26,6 +26,7 @@ import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; 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.UserDAO; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.jpa.AbstractTest; @@ -56,6 +57,9 @@ public static void restoreDomain() { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Autowired private UserDAO userDAO; @@ -66,10 +70,12 @@ public void readPlainSchemas() { @Test public void readRealm() { - assertEquals(1, realmDAO.findDescendants(realmDAO.getRoot().getFullPath(), null, Pageable.unpaged()).size()); + assertEquals( + 1, + realmSearchDAO.findDescendants(realmDAO.getRoot().getFullPath(), null, Pageable.unpaged()).size()); assertEquals( realmDAO.getRoot(), - realmDAO.findDescendants(realmDAO.getRoot().getFullPath(), null, Pageable.unpaged()).get(0)); + realmSearchDAO.findDescendants(realmDAO.getRoot().getFullPath(), null, Pageable.unpaged()).get(0)); } @Test 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 ad90c22d55..65dd66d28a 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 @@ -32,6 +32,7 @@ import org.apache.syncope.core.persistence.api.dao.MalformedPathException; 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.RealmSearchDAO; 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; @@ -48,6 +49,9 @@ public class RealmTest extends AbstractTest { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Autowired private PolicyDAO policyDAO; @@ -68,7 +72,7 @@ public void find() { assertEquals("e4c28e7a-9dbf-4ee7-9441-93812a0d4a28", realm.getParent().getKey()); assertEquals(realmDAO.getRoot(), realm.getParent()); - realm = realmDAO.findByFullPath("/even/two").orElseThrow(); + realm = realmSearchDAO.findByFullPath("/even/two").orElseThrow(); assertEquals("0679e069-7355-4b20-bd11-a5a0a5453c7c", realm.getKey()); assertEquals("two", realm.getName()); assertEquals("/even/two", realm.getFullPath()); @@ -76,24 +80,24 @@ public void find() { @Test public void findInvalidPath() { - assertThrows(MalformedPathException.class, () -> realmDAO.findByFullPath("even/two")); + assertThrows(MalformedPathException.class, () -> realmSearchDAO.findByFullPath("even/two")); } @Test public void findChildren() { - List children = realmDAO.findChildren( - realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); + List children = realmSearchDAO.findChildren( + realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); assertEquals(2, children.size()); - assertTrue(children.contains(realmDAO.findByFullPath("/odd").orElseThrow())); - assertTrue(children.contains(realmDAO.findByFullPath("/even").orElseThrow())); + assertTrue(children.contains(realmSearchDAO.findByFullPath("/odd").orElseThrow())); + assertTrue(children.contains(realmSearchDAO.findByFullPath("/even").orElseThrow())); - children = realmDAO.findChildren(realmDAO.findByFullPath("/odd").orElseThrow()); + children = realmSearchDAO.findChildren(realmSearchDAO.findByFullPath("/odd").orElseThrow()); assertTrue(children.isEmpty()); } @Test public void findAll() { - List list = realmDAO.findDescendants(realmDAO.getRoot().getFullPath(), null, Pageable.unpaged()); + List list = realmSearchDAO.findDescendants(realmDAO.getRoot().getFullPath(), null, Pageable.unpaged()); assertNotNull(list); assertFalse(list.isEmpty()); list.forEach(Assertions::assertNotNull); @@ -105,13 +109,13 @@ public void findAll() { public void save() { Realm realm = entityFactory.newEntity(Realm.class); realm.setName("last"); - realm.setParent(realmDAO.findByFullPath("/even/two").orElseThrow()); + realm.setParent(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); Realm actual = realmDAO.save(realm); assertNotNull(actual.getKey()); assertEquals("last", actual.getName()); assertEquals("/even/two/last", actual.getFullPath()); - assertEquals(realmDAO.findByFullPath("/even/two").orElseThrow(), actual.getParent()); + assertEquals(realmSearchDAO.findByFullPath("/even/two").orElseThrow(), actual.getParent()); assertEquals("20ab5a8c-4b0c-432c-b957-f7fb9784d9f7", realm.getAccountPolicy().getKey()); assertEquals("ce93fcda-dc3a-4369-a7b0-a6108c261c85", realm.getPasswordPolicy().getKey()); 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 90f6ea9349..605e475cfb 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 @@ -25,6 +25,7 @@ import java.util.List; import org.apache.syncope.common.lib.types.IdRepoEntitlement; 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.RoleDAO; import org.apache.syncope.core.persistence.api.entity.Role; import org.apache.syncope.core.persistence.jpa.AbstractTest; @@ -42,6 +43,9 @@ public class RoleTest extends AbstractTest { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Test public void find() { Role role = roleDAO.findById("User manager").orElseThrow(); @@ -64,7 +68,7 @@ public void save() { Role role = entityFactory.newEntity(Role.class); role.setKey("new"); role.add(realmDAO.getRoot()); - role.add(realmDAO.findByFullPath("/even/two").orElseThrow()); + role.add(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); role.getEntitlements().add(IdRepoEntitlement.AUDIT_LIST); role.getEntitlements().add(IdRepoEntitlement.AUDIT_SET); 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 cca8c53cca..b1c5d46b6c 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 @@ -33,6 +33,7 @@ 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.RealmDAO; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; 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.PlainSchema; @@ -59,6 +60,9 @@ public class UserTest extends AbstractTest { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Autowired private ExternalResourceDAO resourceDAO; @@ -189,7 +193,7 @@ public void findMembership() { public void save() { User user = entityFactory.newEntity(User.class); user.setUsername("username"); - user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); user.setCreator("admin"); user.setCreationDate(OffsetDateTime.now()); user.setCipherAlgorithm(CipherAlgorithm.SHA256); @@ -212,7 +216,7 @@ public void delete() { public void issue237() { User user = entityFactory.newEntity(User.class); user.setUsername("username"); - user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); user.setCreator("admin"); user.setCreationDate(OffsetDateTime.now()); @@ -229,7 +233,7 @@ public void issueSYNCOPE391() { user.setUsername("username"); user.setCipherAlgorithm(CipherAlgorithm.AES); user.setPassword(null); - user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); User actual = userDAO.save(user); assertNull(user.getPassword()); @@ -268,7 +272,7 @@ public void passwordGeneratorFailing() { public void issueSYNCOPE1666() { User user = entityFactory.newEntity(User.class); user.setUsername("username"); - user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); user.setCreator("admin"); user.setCreationDate(OffsetDateTime.now()); user.setCipherAlgorithm(CipherAlgorithm.SSHA256); 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 5fbd9a0512..36cbc8e30f 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 @@ -36,6 +36,7 @@ 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.RealmDAO; +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.AnyCond; @@ -71,6 +72,9 @@ public class AnySearchTest extends AbstractTest { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Autowired private RoleDAO roleDAO; @@ -86,7 +90,7 @@ public void searchByDynMembership() { Role role = entityFactory.newEntity(Role.class); role.setKey("new"); role.add(realmDAO.getRoot()); - role.add(realmDAO.findByFullPath("/even/two").orElseThrow()); + role.add(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); role.getEntitlements().add(IdRepoEntitlement.AUDIT_LIST); role.getEntitlements().add(IdRepoEntitlement.AUDIT_SET); role.setDynMembershipCond("cool==true"); 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 3ffa362824..3cdecd4d57 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 @@ -43,6 +43,7 @@ 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.RealmDAO; +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.anyobject.ADynGroupMembership; import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttr; @@ -81,6 +82,9 @@ public class GroupTest extends AbstractTest { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Autowired private PlainSchemaDAO plainSchemaDAO; @@ -200,7 +204,7 @@ public void create() { public void createWithInternationalCharacters() { Group group = entityFactory.newEntity(Group.class); group.setName("räksmörgås"); - group.setRealm(realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); + group.setRealm(realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); groupDAO.save(group); entityManager.flush(); @@ -247,7 +251,7 @@ public void udynMembership() { // 0. create user matching the condition below User user = entityFactory.newEntity(User.class); user.setUsername("username"); - user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); user.add(anyTypeClassDAO.findById("other").orElseThrow()); UPlainAttr attr = entityFactory.newEntity(UPlainAttr.class); @@ -340,7 +344,7 @@ public void adynMembership() { AnyObject anyObject = entityFactory.newEntity(AnyObject.class); anyObject.setName("name"); anyObject.setType(anyTypeDAO.findById("PRINTER").orElseThrow()); - anyObject.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + anyObject.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); APlainAttr attr = entityFactory.newEntity(APlainAttr.class); attr.setOwner(anyObject); 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 bebc26db9d..34048d1b02 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 @@ -25,6 +25,7 @@ import java.util.UUID; 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.entity.Realm; import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp; import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy; @@ -45,9 +46,12 @@ public class PolicyTest extends AbstractClientAppTest { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Test public void authPolicyCanBeNull() { - Realm realm = realmDAO.findByFullPath("/odd").orElseThrow(); + Realm realm = realmSearchDAO.findByFullPath("/odd").orElseThrow(); // Create new client app and assign policy OIDCRPClientApp rp = entityFactory.newEntity(OIDCRPClientApp.class); 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 0b82f4f6bc..2fa10d3e1e 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 @@ -23,6 +23,7 @@ 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.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.RoleDAO; import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.persistence.api.entity.Role; @@ -38,6 +39,9 @@ public class RealmTest extends AbstractTest { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Autowired private RoleDAO roleDAO; @@ -46,7 +50,7 @@ public class RealmTest extends AbstractTest { @Test public void test() { - Realm realm = realmDAO.findByFullPath("/odd").orElseThrow(); + Realm realm = realmSearchDAO.findByFullPath("/odd").orElseThrow(); // need to remove this group in order to remove the realm, which is otherwise empty Group group = groupDAO.findByName("fake").orElseThrow(); 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 3e9499abba..2ed33f897d 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 @@ -36,6 +36,7 @@ import org.apache.syncope.core.persistence.api.dao.DelegationDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; 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.RoleDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.Delegation; @@ -57,6 +58,9 @@ public class RoleTest extends AbstractTest { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Autowired private PlainSchemaDAO plainSchemaDAO; @@ -98,7 +102,7 @@ public void dynMembership() { // 0. create user matching the condition below User user = entityFactory.newEntity(User.class); user.setUsername("username"); - user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); user.add(anyTypeClassDAO.findById("other").orElseThrow()); UPlainAttr attr = entityFactory.newEntity(UPlainAttr.class); @@ -115,7 +119,7 @@ public void dynMembership() { Role role = entityFactory.newEntity(Role.class); role.setKey("new"); role.add(realmDAO.getRoot()); - role.add(realmDAO.findByFullPath("/even/two").orElseThrow()); + role.add(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); role.getEntitlements().add(IdRepoEntitlement.AUDIT_LIST); role.getEntitlements().add(IdRepoEntitlement.AUDIT_SET); role.setDynMembershipCond("cool==true"); @@ -164,7 +168,7 @@ public void delete() { Role role = entityFactory.newEntity(Role.class); role.setKey("new"); role.add(realmDAO.getRoot()); - role.add(realmDAO.findByFullPath("/even/two").orElseThrow()); + role.add(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); role.getEntitlements().add(IdRepoEntitlement.AUDIT_LIST); role.getEntitlements().add(IdRepoEntitlement.AUDIT_SET); @@ -174,7 +178,7 @@ public void delete() { // 1. create user and assign that role User user = entityFactory.newEntity(User.class); user.setUsername("username"); - user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); user.add(role); user = userDAO.save(user); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java index 7ef4332525..2c1b6b1cba 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java @@ -57,6 +57,7 @@ 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.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; import org.apache.syncope.core.persistence.api.dao.RemediationDAO; import org.apache.syncope.core.persistence.api.dao.ReportDAO; @@ -90,6 +91,7 @@ import org.apache.syncope.core.persistence.neo4j.dao.Neo4jPersistenceInfoDAO; import org.apache.syncope.core.persistence.neo4j.dao.Neo4jPolicyDAO; import org.apache.syncope.core.persistence.neo4j.dao.Neo4jRealmDAO; +import org.apache.syncope.core.persistence.neo4j.dao.Neo4jRealmSearchDAO; import org.apache.syncope.core.persistence.neo4j.dao.Neo4jTaskDAO; import org.apache.syncope.core.persistence.neo4j.dao.Neo4jTaskExecDAO; import org.apache.syncope.core.persistence.neo4j.dao.repo.AccessTokenRepo; @@ -460,7 +462,7 @@ public AnyObjectDAO anyObjectDAO( @ConditionalOnMissingBean @Bean public AnySearchDAO anySearchDAO( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final @Lazy DynRealmDAO dynRealmDAO, final @Lazy UserDAO userDAO, final @Lazy GroupDAO groupDAO, @@ -473,7 +475,7 @@ public AnySearchDAO anySearchDAO( final Neo4jClient neo4jClient) { return new Neo4jAnySearchDAO( - realmDAO, + realmSearchDAO, dynRealmDAO, userDAO, groupDAO, @@ -945,16 +947,26 @@ public RelationshipTypeDAO relationshipTypeDAO( return neo4jRepositoryFactory.getRepository(RelationshipTypeRepo.class, relationshipTypeRepoExt); } - @ConditionalOnMissingBean(name = { "realmDAO", "delegateRealmDAO" }) - @Bean(name = { "realmDAO", "delegateRealmDAO" }) + @ConditionalOnMissingBean + @Bean public RealmDAO realmDAO( final @Lazy RoleDAO roleDAO, + final RealmSearchDAO realmSearchDAO, final ApplicationEventPublisher publisher, final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient, final NodeValidator nodeValidator) { - return new Neo4jRealmDAO(roleDAO, publisher, neo4jTemplate, neo4jClient, nodeValidator); + return new Neo4jRealmDAO(roleDAO, realmSearchDAO, publisher, neo4jTemplate, neo4jClient, nodeValidator); + } + + @ConditionalOnMissingBean + @Bean + public RealmSearchDAO realmSearchDAO( + final Neo4jTemplate neo4jTemplate, + final Neo4jClient neo4jClient) { + + return new Neo4jRealmSearchDAO(neo4jTemplate, neo4jClient); } @ConditionalOnMissingBean @@ -1136,7 +1148,7 @@ public SRARouteDAO sraRouteDAO(final SyncopeNeo4jRepositoryFactory neo4jReposito @ConditionalOnMissingBean @Bean public TaskDAO taskDAO( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final RemediationDAO remediationDAO, final TaskUtilsFactory taskUtilsFactory, final SecurityProperties securityProperties, @@ -1145,7 +1157,7 @@ public TaskDAO taskDAO( final NodeValidator nodeValidator) { return new Neo4jTaskDAO( - realmDAO, + realmSearchDAO, remediationDAO, taskUtilsFactory, securityProperties, diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnySearchDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnySearchDAO.java index 70e855dd4a..345ae03204 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnySearchDAO.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnySearchDAO.java @@ -43,7 +43,7 @@ import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; 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.RealmDAO; +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.AnyCond; import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond; @@ -134,7 +134,7 @@ protected static String escapeIfString(final String value, final boolean isStr) protected final Neo4jClient neo4jClient; public Neo4jAnySearchDAO( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final DynRealmDAO dynRealmDAO, final UserDAO userDAO, final GroupDAO groupDAO, @@ -147,7 +147,7 @@ public Neo4jAnySearchDAO( final Neo4jClient neo4jClient) { super( - realmDAO, + realmSearchDAO, dynRealmDAO, userDAO, groupDAO, @@ -191,14 +191,14 @@ protected AdminRealmsFilter getAdminRealmsFilter( goRealm -> groupOwners.add(goRealm.getRight()), () -> { if (realmPath.startsWith("/")) { - Realm realm = realmDAO.findByFullPath(realmPath).orElseThrow(() -> { + Realm realm = realmSearchDAO.findByFullPath(realmPath).orElseThrow(() -> { SyncopeClientException noRealm = SyncopeClientException.build(ClientExceptionType.InvalidRealm); noRealm.getElements().add("Invalid realm specified: " + realmPath); return noRealm; }); - realmKeys.addAll(realmDAO.findDescendants(realm.getFullPath(), base.getFullPath())); + realmKeys.addAll(realmSearchDAO.findDescendants(realm.getFullPath(), base.getFullPath())); } else { dynRealmDAO.findById(realmPath).ifPresentOrElse( dynRealm -> dynRealmKeys.add(dynRealm.getKey()), @@ -655,7 +655,7 @@ protected Pair getQuery( if (JAXRSService.PARAM_REALM.equals(cond.getSchema())) { if (!SyncopeConstants.UUID_PATTERN.matcher(cond.getExpression()).matches()) { - Realm realm = realmDAO.findByFullPath(cond.getExpression()). + Realm realm = realmSearchDAO.findByFullPath(cond.getExpression()). orElseThrow(() -> new IllegalArgumentException( "Invalid Realm full path: " + cond.getExpression())); cond.setExpression(realm.getKey()); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmDAO.java index 028e987588..a99339d645 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmDAO.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmDAO.java @@ -19,14 +19,12 @@ package org.apache.syncope.core.persistence.neo4j.dao; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.core.persistence.api.dao.MalformedPathException; 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.RoleDAO; import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Implementation; @@ -69,40 +67,26 @@ public class Neo4jRealmDAO extends AbstractDAO implements RealmDAO { protected static final Logger LOG = LoggerFactory.getLogger(RealmDAO.class); - protected static StringBuilder buildDescendantQuery( - final String base, - final String keyword, - final Map parameters) { - - StringBuilder queryString = new StringBuilder("MATCH (n:").append(Neo4jRealm.NODE).append(") "). - append("WHERE (n.fullPath = $base OR n.fullPath =~ $like)"); - parameters.put("base", base); - parameters.put("like", SyncopeConstants.ROOT_REALM.equals(base) ? "/.*" : base + "/.*"); - - if (keyword != null) { - queryString.append(" AND toLower(n.name) =~ $name"); - parameters.put("name", keyword.replaceAll("_", "\\\\_").toLowerCase() + ".*"); - } - - return queryString; - } - protected final RoleDAO roleDAO; + protected final RealmSearchDAO realmSearchDAO; + protected final ApplicationEventPublisher publisher; protected final NodeValidator nodeValidator; public Neo4jRealmDAO( final RoleDAO roleDAO, + final RealmSearchDAO realmSearchDAO, final ApplicationEventPublisher publisher, final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient, final NodeValidator nodeValidator) { super(neo4jTemplate, neo4jClient); - this.publisher = publisher; this.roleDAO = roleDAO; + this.realmSearchDAO = realmSearchDAO; + this.publisher = publisher; this.nodeValidator = nodeValidator; } @@ -126,72 +110,10 @@ public Optional findById(final String key) { return neo4jTemplate.findById(key, Neo4jRealm.class); } - @Transactional(readOnly = true) - @Override - public Optional findByFullPath(final String fullPath) { - if (SyncopeConstants.ROOT_REALM.equals(fullPath)) { - return Optional.of(getRoot()); - } - - if (StringUtils.isBlank(fullPath) || !RealmDAO.PATH_PATTERN.matcher(fullPath).matches()) { - throw new MalformedPathException(fullPath); - } - - return neo4jClient.query( - "MATCH (n:" + Neo4jRealm.NODE + ") WHERE n.fullPath = $fullPath RETURN n.id"). - bindAll(Map.of("fullPath", fullPath)).fetch().one(). - flatMap(toOptional("n.id", Neo4jRealm.class)); - } - - @Override - public List findByName(final String name) { - return toList(neo4jClient.query( - "MATCH (n:" + Neo4jRealm.NODE + ") WHERE n.name = $name RETURN n.id"). - bindAll(Map.of("name", name)).fetch().all(), "n.id", Neo4jRealm.class); - } - - @Override - public long countDescendants(final String base, final String keyword) { - Map parameters = new HashMap<>(); - - StringBuilder queryString = buildDescendantQuery(base, keyword, parameters).append(" RETURN COUNT(n)"); - return neo4jTemplate.count(queryString.toString(), parameters); - } - - @Override - public List findDescendants(final String base, final String keyword, final Pageable pageable) { - Map parameters = new HashMap<>(); - - StringBuilder queryString = buildDescendantQuery(base, keyword, parameters). - append(" RETURN n.id ORDER BY n.fullPath"); - if (pageable.isPaged()) { - queryString.append(" SKIP ").append(pageable.getPageSize() * pageable.getPageNumber()). - append(" LIMIT ").append(pageable.getPageSize()); - } - - return toList(neo4jClient.query( - queryString.toString()).bindAll(parameters).fetch().all(), "n.id", Neo4jRealm.class); - } - - @Override - public List findDescendants(final String base, final String prefix) { - Map parameters = new HashMap<>(); - - StringBuilder queryString = buildDescendantQuery(base, null, parameters). - append(" AND (n.fullPath = $prefix OR n.fullPath =~ $likePrefix)"). - append(" RETURN n.id ORDER BY n.fullPath"); - parameters.put("prefix", prefix); - parameters.put("likePrefix", SyncopeConstants.ROOT_REALM.equals(prefix) ? "/.*" : prefix + "/.*"); - - return neo4jClient.query(queryString.toString()). - bindAll(parameters).fetch().all().stream(). - map(found -> (String) found.values().iterator().next()).toList(); - } - protected List findSamePolicyChildren(final Realm realm, final T policy) { List result = new ArrayList<>(); - findChildren(realm).stream(). + realmSearchDAO.findChildren(realm).stream(). filter(child -> (policy instanceof AccountPolicy && child.getAccountPolicy() == null || policy.equals(child.getAccountPolicy())) || (policy instanceof PasswordPolicy @@ -248,28 +170,6 @@ public List findByPolicy(final T policy) { return result; } - protected void findAncestors(final List result, final Realm realm) { - if (realm.getParent() != null && !result.contains(realm.getParent())) { - result.add(realm.getParent()); - findAncestors(result, realm.getParent()); - } - } - - @Override - public List findAncestors(final Realm realm) { - List result = new ArrayList<>(); - result.add(realm); - findAncestors(result, realm); - return result; - } - - @Override - public List findChildren(final Realm realm) { - return toList(neo4jClient.query( - "MATCH (n:" + Neo4jRealm.NODE + " {id: $id})<-[r:" + Neo4jRealm.PARENT_REL + "]-(c) RETURN c.id"). - bindAll(Map.of("id", realm.getKey())).fetch().all(), "c.id", Neo4jRealm.class); - } - @Override public List findByActionsContaining(final Implementation logicActions) { return findByRelationship(Neo4jRealm.NODE, Neo4jImplementation.NODE, logicActions.getKey(), Neo4jRealm.class); @@ -319,7 +219,7 @@ public S save(final S realm) { S merged = neo4jTemplate.save(nodeValidator.validate(realm)); if (!fullPathAfter.equals(fullPathBefore)) { - findChildren(merged).forEach(this::save); + realmSearchDAO.findChildren(merged).forEach(this::save); } publisher.publishEvent( @@ -339,7 +239,7 @@ public void delete(final Realm realm) { return; } - findDescendants(realm.getFullPath(), null, Pageable.unpaged()).forEach(toBeDeleted -> { + realmSearchDAO.findDescendants(realm.getFullPath(), null, Pageable.unpaged()).forEach(toBeDeleted -> { roleDAO.findByRealms(toBeDeleted).forEach(role -> role.getRealms().remove(toBeDeleted)); cascadeDelete( diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmSearchDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmSearchDAO.java new file mode 100644 index 0000000000..27b63eacd4 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmSearchDAO.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.core.persistence.neo4j.dao; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.core.persistence.api.dao.MalformedPathException; +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.entity.Realm; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Pageable; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; + +public class Neo4jRealmSearchDAO extends AbstractDAO implements RealmSearchDAO { + + protected static final Logger LOG = LoggerFactory.getLogger(RealmSearchDAO.class); + + protected static StringBuilder buildDescendantQuery( + final String base, + final String keyword, + final Map parameters) { + + StringBuilder queryString = new StringBuilder("MATCH (n:").append(Neo4jRealm.NODE).append(") "). + append("WHERE (n.fullPath = $base OR n.fullPath =~ $like)"); + parameters.put("base", base); + parameters.put("like", SyncopeConstants.ROOT_REALM.equals(base) ? "/.*" : base + "/.*"); + + if (keyword != null) { + queryString.append(" AND toLower(n.name) =~ $name"); + parameters.put("name", keyword.replaceAll("_", "\\\\_").toLowerCase() + ".*"); + } + + return queryString; + } + + public Neo4jRealmSearchDAO(final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient) { + super(neo4jTemplate, neo4jClient); + } + + @Transactional(readOnly = true) + @Override + public Optional findByFullPath(final String fullPath) { + if (StringUtils.isBlank(fullPath) + || (!SyncopeConstants.ROOT_REALM.equals(fullPath) + && !RealmDAO.PATH_PATTERN.matcher(fullPath).matches())) { + + throw new MalformedPathException(fullPath); + } + + return neo4jClient.query( + "MATCH (n:" + Neo4jRealm.NODE + ") WHERE n.fullPath = $fullPath RETURN n.id"). + bindAll(Map.of("fullPath", fullPath)).fetch().one(). + flatMap(toOptional("n.id", Neo4jRealm.class)); + } + + @Override + public List findByName(final String name) { + return toList(neo4jClient.query( + "MATCH (n:" + Neo4jRealm.NODE + ") WHERE n.name = $name RETURN n.id"). + bindAll(Map.of("name", name)).fetch().all(), "n.id", Neo4jRealm.class); + } + + @Override + public List findChildren(final Realm realm) { + return toList(neo4jClient.query( + "MATCH (n:" + Neo4jRealm.NODE + " {id: $id})<-[r:" + Neo4jRealm.PARENT_REL + "]-(c) RETURN c.id"). + bindAll(Map.of("id", realm.getKey())).fetch().all(), "c.id", Neo4jRealm.class); + } + + @Override + public long countDescendants(final String base, final String keyword) { + Map parameters = new HashMap<>(); + + StringBuilder queryString = buildDescendantQuery(base, keyword, parameters).append(" RETURN COUNT(n)"); + return neo4jTemplate.count(queryString.toString(), parameters); + } + + @Override + public List findDescendants(final String base, final String keyword, final Pageable pageable) { + Map parameters = new HashMap<>(); + + StringBuilder queryString = buildDescendantQuery(base, keyword, parameters). + append(" RETURN n.id ORDER BY n.fullPath"); + if (pageable.isPaged()) { + queryString.append(" SKIP ").append(pageable.getPageSize() * pageable.getPageNumber()). + append(" LIMIT ").append(pageable.getPageSize()); + } + + return toList(neo4jClient.query( + queryString.toString()).bindAll(parameters).fetch().all(), "n.id", Neo4jRealm.class); + } + + @Override + public List findDescendants(final String base, final String prefix) { + Map parameters = new HashMap<>(); + + StringBuilder queryString = buildDescendantQuery(base, null, parameters). + append(" AND (n.fullPath = $prefix OR n.fullPath =~ $likePrefix)"). + append(" RETURN n.id ORDER BY n.fullPath"); + parameters.put("prefix", prefix); + parameters.put("likePrefix", SyncopeConstants.ROOT_REALM.equals(prefix) ? "/.*" : prefix + "/.*"); + + return neo4jClient.query(queryString.toString()). + bindAll(parameters).fetch().all().stream(). + map(found -> (String) found.values().iterator().next()).toList(); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskDAO.java index 75e324b3dc..9da976201c 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskDAO.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskDAO.java @@ -33,7 +33,7 @@ import org.apache.syncope.common.lib.types.ExecStatus; import org.apache.syncope.common.lib.types.IdRepoEntitlement; import org.apache.syncope.common.lib.types.TaskType; -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.RemediationDAO; import org.apache.syncope.core.persistence.api.dao.TaskDAO; import org.apache.syncope.core.persistence.api.entity.ExternalResource; @@ -77,7 +77,7 @@ public class Neo4jTaskDAO extends AbstractDAO implements TaskDAO { protected static final Logger LOG = LoggerFactory.getLogger(TaskDAO.class); - protected final RealmDAO realmDAO; + protected final RealmSearchDAO realmSearchDAO; protected final RemediationDAO remediationDAO; @@ -88,7 +88,7 @@ public class Neo4jTaskDAO extends AbstractDAO implements TaskDAO { protected final NodeValidator nodeValidator; public Neo4jTaskDAO( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final RemediationDAO remediationDAO, final TaskUtilsFactory taskUtilsFactory, final SecurityProperties securityProperties, @@ -97,7 +97,7 @@ public Neo4jTaskDAO( final NodeValidator nodeValidator) { super(neo4jTemplate, neo4jClient); - this.realmDAO = realmDAO; + this.realmSearchDAO = realmSearchDAO; this.remediationDAO = remediationDAO; this.taskUtilsFactory = taskUtilsFactory; this.securityProperties = securityProperties; @@ -300,9 +300,10 @@ protected StringBuilder query( && !AuthContextUtils.getUsername().equals(securityProperties.getAdminUser())) { Stream realmKeys = AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.TASK_LIST).stream(). - map(realmDAO::findByFullPath). + map(realmSearchDAO::findByFullPath). filter(Optional::isPresent). - flatMap(r -> realmDAO.findDescendants(r.get().getFullPath(), null, Pageable.unpaged()).stream()). + flatMap(r -> realmSearchDAO.findDescendants(r.get().getFullPath(), null, Pageable.unpaged()). + stream()). map(Realm::getKey). distinct(); 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 752d300416..f83a253b63 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 @@ -29,7 +29,7 @@ import org.apache.syncope.common.lib.SyncopeConstants; 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.RealmDAO; +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.anyobject.APlainAttr; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; @@ -48,7 +48,7 @@ public class AnyObjectTest extends AbstractTest { private AnyObjectDAO anyObjectDAO; @Autowired - private RealmDAO realmDAO; + private RealmSearchDAO realmSearchDAO; @Test public void findAll() { @@ -117,7 +117,7 @@ public void save() { AnyObject anyObject = entityFactory.newEntity(AnyObject.class); anyObject.setName("a name"); anyObject.setType(anyTypeDAO.findById("PRINTER").orElseThrow()); - anyObject.setRealm(realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); + anyObject.setRealm(realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); anyObject = anyObjectDAO.save(anyObject); assertNotNull(anyObject); 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 fb8ba651ea..e0b9ba5184 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 @@ -41,6 +41,7 @@ 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.RealmDAO; +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.AnyCond; @@ -99,6 +100,9 @@ public class AnySearchTest extends AbstractTest { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Autowired private RoleDAO roleDAO; @@ -796,7 +800,7 @@ public void issueSYNCOPE980() { AnyObject anyObject = entityFactory.newEntity(AnyObject.class); anyObject.setName("one"); anyObject.setType(service); - anyObject.setRealm(realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); + anyObject.setRealm(realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); AMembership membership = entityFactory.newEntity(AMembership.class); membership.setRightEnd(citizen); 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 7c418d0877..d9afab454e 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 @@ -26,7 +26,7 @@ import org.apache.syncope.common.lib.SyncopeConstants; 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.RealmDAO; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.neo4j.AbstractTest; import org.junit.jupiter.api.Test; @@ -40,7 +40,7 @@ public class GroupTest extends AbstractTest { private GroupDAO groupDAO; @Autowired - private RealmDAO realmDAO; + private RealmSearchDAO realmSearchDAO; @Autowired private AnyTypeDAO anyTypeDAO; @@ -71,7 +71,7 @@ public void findKeysByNamePattern() { public void save() { Group group = entityFactory.newEntity(Group.class); group.setName("secondChild"); - group.setRealm(realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); + group.setRealm(realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); group = groupDAO.save(group); 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 d2815106fe..ea53f45277 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 @@ -26,6 +26,7 @@ import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; 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.UserDAO; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.neo4j.AbstractTest; @@ -54,6 +55,9 @@ public static void restoreDomain() { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Autowired private UserDAO userDAO; @@ -64,10 +68,12 @@ public void readPlainSchemas() { @Test public void readRealm() { - assertEquals(1, realmDAO.findDescendants(realmDAO.getRoot().getFullPath(), null, Pageable.unpaged()).size()); + assertEquals( + 1, + realmSearchDAO.findDescendants(realmDAO.getRoot().getFullPath(), null, Pageable.unpaged()).size()); assertEquals( realmDAO.getRoot(), - realmDAO.findDescendants(realmDAO.getRoot().getFullPath(), null, Pageable.unpaged()).get(0)); + realmSearchDAO.findDescendants(realmDAO.getRoot().getFullPath(), null, Pageable.unpaged()).get(0)); } @Test 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 efe69a9b79..09b5580753 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 @@ -32,6 +32,7 @@ import org.apache.syncope.core.persistence.api.dao.MalformedPathException; 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.RealmSearchDAO; 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; @@ -48,6 +49,9 @@ public class RealmTest extends AbstractTest { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Autowired private PolicyDAO policyDAO; @@ -68,7 +72,7 @@ public void find() { assertEquals("e4c28e7a-9dbf-4ee7-9441-93812a0d4a28", realm.getParent().getKey()); assertEquals(realmDAO.getRoot(), realm.getParent()); - realm = realmDAO.findByFullPath("/even/two").orElseThrow(); + realm = realmSearchDAO.findByFullPath("/even/two").orElseThrow(); assertEquals("0679e069-7355-4b20-bd11-a5a0a5453c7c", realm.getKey()); assertEquals("two", realm.getName()); assertEquals("/even/two", realm.getFullPath()); @@ -76,36 +80,36 @@ public void find() { @Test public void findInvalidPath() { - assertThrows(MalformedPathException.class, () -> realmDAO.findByFullPath("even/two")); + assertThrows(MalformedPathException.class, () -> realmSearchDAO.findByFullPath("even/two")); } @Test public void findDescendants() { - List found = realmDAO.findDescendants(SyncopeConstants.ROOT_REALM, SyncopeConstants.ROOT_REALM); + List found = realmSearchDAO.findDescendants(SyncopeConstants.ROOT_REALM, SyncopeConstants.ROOT_REALM); assertEquals(4, found.size()); assertTrue(found.stream().allMatch(f -> SyncopeConstants.UUID_PATTERN.matcher(f).matches())); assertEquals( found, - realmDAO.findDescendants(SyncopeConstants.ROOT_REALM, null, Pageable.unpaged()).stream(). + realmSearchDAO.findDescendants(SyncopeConstants.ROOT_REALM, null, Pageable.unpaged()).stream(). map(Realm::getKey).toList()); } @Test public void findChildren() { - List children = realmDAO.findChildren( - realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); + List children = realmSearchDAO.findChildren( + realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); assertEquals(2, children.size()); - assertTrue(children.contains(realmDAO.findByFullPath("/odd").orElseThrow())); - assertTrue(children.contains(realmDAO.findByFullPath("/even").orElseThrow())); + assertTrue(children.contains(realmSearchDAO.findByFullPath("/odd").orElseThrow())); + assertTrue(children.contains(realmSearchDAO.findByFullPath("/even").orElseThrow())); - children = realmDAO.findChildren(realmDAO.findByFullPath("/odd").orElseThrow()); + children = realmSearchDAO.findChildren(realmSearchDAO.findByFullPath("/odd").orElseThrow()); assertTrue(children.isEmpty()); } @Test public void findAll() { - List list = realmDAO.findDescendants(realmDAO.getRoot().getFullPath(), null, Pageable.unpaged()); + List list = realmSearchDAO.findDescendants(realmDAO.getRoot().getFullPath(), null, Pageable.unpaged()); assertNotNull(list); assertFalse(list.isEmpty()); list.forEach(Assertions::assertNotNull); @@ -117,13 +121,13 @@ public void findAll() { public void save() { Realm realm = entityFactory.newEntity(Realm.class); realm.setName("last"); - realm.setParent(realmDAO.findByFullPath("/even/two").orElseThrow()); + realm.setParent(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); Realm actual = realmDAO.save(realm); assertNotNull(actual.getKey()); assertEquals("last", actual.getName()); assertEquals("/even/two/last", actual.getFullPath()); - assertEquals(realmDAO.findByFullPath("/even/two").orElseThrow(), actual.getParent()); + assertEquals(realmSearchDAO.findByFullPath("/even/two").orElseThrow(), actual.getParent()); assertEquals("20ab5a8c-4b0c-432c-b957-f7fb9784d9f7", realm.getAccountPolicy().getKey()); assertEquals("ce93fcda-dc3a-4369-a7b0-a6108c261c85", realm.getPasswordPolicy().getKey()); 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 c5733dd848..bd67f8ecc7 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 @@ -25,6 +25,7 @@ import java.util.List; import org.apache.syncope.common.lib.types.IdRepoEntitlement; 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.RoleDAO; import org.apache.syncope.core.persistence.api.entity.Role; import org.apache.syncope.core.persistence.neo4j.AbstractTest; @@ -42,6 +43,9 @@ public class RoleTest extends AbstractTest { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Test public void find() { Role role = roleDAO.findById("User manager").orElseThrow(); @@ -64,7 +68,7 @@ public void save() { Role role = entityFactory.newEntity(Role.class); role.setKey("new"); role.add(realmDAO.getRoot()); - role.add(realmDAO.findByFullPath("/even/two").orElseThrow()); + role.add(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); role.getEntitlements().add(IdRepoEntitlement.AUDIT_LIST); role.getEntitlements().add(IdRepoEntitlement.AUDIT_SET); 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 c11bddde4e..6653097058 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 @@ -33,6 +33,7 @@ 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.RealmDAO; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; 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.PlainSchema; @@ -59,6 +60,9 @@ public class UserTest extends AbstractTest { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Autowired private ExternalResourceDAO resourceDAO; @@ -189,7 +193,7 @@ public void findMembership() { public void save() { User user = entityFactory.newEntity(User.class); user.setUsername("username"); - user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); user.setCreator("admin"); user.setCreationDate(OffsetDateTime.now()); user.setCipherAlgorithm(CipherAlgorithm.SHA256); @@ -212,7 +216,7 @@ public void delete() { public void issue237() { User user = entityFactory.newEntity(User.class); user.setUsername("username"); - user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); user.setCreator("admin"); user.setCreationDate(OffsetDateTime.now()); @@ -229,7 +233,7 @@ public void issueSYNCOPE391() { user.setUsername("username"); user.setCipherAlgorithm(CipherAlgorithm.AES); user.setPassword(null); - user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); User actual = userDAO.save(user); assertNull(user.getPassword()); @@ -268,7 +272,7 @@ public void passwordGeneratorFailing() { public void issueSYNCOPE1666() { User user = entityFactory.newEntity(User.class); user.setUsername("username"); - user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); user.setCreator("admin"); user.setCreationDate(OffsetDateTime.now()); user.setCipherAlgorithm(CipherAlgorithm.SSHA256); 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 802c04844f..19c3859ac3 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 @@ -33,6 +33,7 @@ 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.RealmDAO; +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.AnyCond; @@ -68,6 +69,9 @@ public class AnySearchTest extends AbstractTest { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Autowired private RoleDAO roleDAO; @@ -83,7 +87,7 @@ public void searchByDynMembership() { Role role = entityFactory.newEntity(Role.class); role.setKey("new"); role.add(realmDAO.getRoot()); - role.add(realmDAO.findByFullPath("/even/two").orElseThrow()); + role.add(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); role.getEntitlements().add(IdRepoEntitlement.AUDIT_LIST); role.getEntitlements().add(IdRepoEntitlement.AUDIT_SET); role.setDynMembershipCond("cool==true"); 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 036a881a03..099bec86e6 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 @@ -40,6 +40,7 @@ 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.RealmDAO; +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.anyobject.ADynGroupMembership; import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttr; @@ -77,6 +78,9 @@ public class GroupTest extends AbstractTest { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Autowired private PlainSchemaDAO plainSchemaDAO; @@ -196,7 +200,7 @@ public void create() { public void createWithInternationalCharacters() { Group group = entityFactory.newEntity(Group.class); group.setName("räksmörgås"); - group.setRealm(realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); + group.setRealm(realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow()); groupDAO.save(group); } @@ -219,7 +223,7 @@ public void udynMembership() { // 0. create user matching the condition below User user = entityFactory.newEntity(User.class); user.setUsername("username"); - user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); user.add(anyTypeClassDAO.findById("other").orElseThrow()); UPlainAttr attr = entityFactory.newEntity(UPlainAttr.class); @@ -287,7 +291,7 @@ public void adynMembership() { AnyObject anyObject = entityFactory.newEntity(AnyObject.class); anyObject.setName("name"); anyObject.setType(anyTypeDAO.findById("PRINTER").orElseThrow()); - anyObject.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + anyObject.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); APlainAttr attr = entityFactory.newEntity(APlainAttr.class); attr.setOwner(anyObject); 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 f03e4aa211..5961605bd2 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 @@ -25,6 +25,7 @@ import java.util.UUID; 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.entity.Realm; import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp; import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy; @@ -45,9 +46,12 @@ public class PolicyTest extends AbstractClientAppTest { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Test public void authPolicyCanBeNull() { - Realm realm = realmDAO.findByFullPath("/odd").orElseThrow(); + Realm realm = realmSearchDAO.findByFullPath("/odd").orElseThrow(); // Create new client app and assign policy OIDCRPClientApp rp = entityFactory.newEntity(OIDCRPClientApp.class); 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 3bb4ca017f..a6b8fb0941 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 @@ -23,6 +23,7 @@ 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.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.RoleDAO; import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.persistence.api.entity.Role; @@ -38,6 +39,9 @@ public class RealmTest extends AbstractTest { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Autowired private RoleDAO roleDAO; @@ -46,7 +50,7 @@ public class RealmTest extends AbstractTest { @Test public void test() { - Realm realm = realmDAO.findByFullPath("/odd").orElseThrow(); + Realm realm = realmSearchDAO.findByFullPath("/odd").orElseThrow(); // need to remove this group in order to remove the realm, which is otherwise empty Group group = groupDAO.findByName("fake").orElseThrow(); 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 aad36bc70e..7de316011a 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 @@ -34,6 +34,7 @@ import org.apache.syncope.core.persistence.api.dao.DelegationDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; 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.RoleDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.Delegation; @@ -54,6 +55,9 @@ public class RoleTest extends AbstractTest { @Autowired private RealmDAO realmDAO; + @Autowired + private RealmSearchDAO realmSearchDAO; + @Autowired private PlainSchemaDAO plainSchemaDAO; @@ -74,7 +78,7 @@ public void dynMembership() { // 0. create user matching the condition below User user = entityFactory.newEntity(User.class); user.setUsername("username"); - user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); user.add(anyTypeClassDAO.findById("other").orElseThrow()); UPlainAttr attr = entityFactory.newEntity(UPlainAttr.class); @@ -91,7 +95,7 @@ public void dynMembership() { Role role = entityFactory.newEntity(Role.class); role.setKey("new"); role.add(realmDAO.getRoot()); - role.add(realmDAO.findByFullPath("/even/two").orElseThrow()); + role.add(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); role.getEntitlements().add(IdRepoEntitlement.AUDIT_LIST); role.getEntitlements().add(IdRepoEntitlement.AUDIT_SET); role.setDynMembershipCond("cool==true"); @@ -134,7 +138,7 @@ public void delete() { Role role = entityFactory.newEntity(Role.class); role.setKey("new"); role.add(realmDAO.getRoot()); - role.add(realmDAO.findByFullPath("/even/two").orElseThrow()); + role.add(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); role.getEntitlements().add(IdRepoEntitlement.AUDIT_LIST); role.getEntitlements().add(IdRepoEntitlement.AUDIT_SET); @@ -144,7 +148,7 @@ public void delete() { // 1. create user and assign that role User user = entityFactory.newEntity(User.class); user.setUsername("username"); - user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); user.add(role); user = userDAO.save(user); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java index 449312ba27..5dee7c7258 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java @@ -31,6 +31,7 @@ import org.apache.syncope.common.lib.types.ConnectorCapability; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; 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.entity.ConnInstance; import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.ExternalResource; @@ -60,6 +61,8 @@ protected static String getBeanName(final ExternalResource resource) { protected final RealmDAO realmDAO; + protected final RealmSearchDAO realmSearchDAO; + protected final ExternalResourceDAO resourceDAO; protected final ConnInstanceDataBinder connInstanceDataBinder; @@ -71,6 +74,7 @@ protected static String getBeanName(final ExternalResource resource) { public DefaultConnectorManager( final ConnIdBundleManager connIdBundleManager, final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final ExternalResourceDAO resourceDAO, final ConnInstanceDataBinder connInstanceDataBinder, final AsyncConnectorFacade asyncFacade, @@ -78,6 +82,7 @@ public DefaultConnectorManager( this.connIdBundleManager = connIdBundleManager; this.realmDAO = realmDAO; + this.realmSearchDAO = realmSearchDAO; this.resourceDAO = resourceDAO; this.connInstanceDataBinder = connInstanceDataBinder; this.asyncFacade = asyncFacade; @@ -106,7 +111,7 @@ public ConnInstance buildConnInstanceOverride( final Optional> capabilitiesOverride) { ConnInstance override = entityFactory.newEntity(ConnInstance.class); - override.setAdminRealm(realmDAO.findByFullPath(connInstance.getAdminRealm()).orElseGet(() -> { + override.setAdminRealm(realmSearchDAO.findByFullPath(connInstance.getAdminRealm()).orElseGet(() -> { LOG.warn("Could not find admin Realm {}, reverting to {}", connInstance.getAdminRealm(), SyncopeConstants.ROOT_REALM); return realmDAO.getRoot(); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java index 697941a179..a344c1e3d9 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java @@ -53,7 +53,7 @@ import org.apache.syncope.core.persistence.api.dao.ApplicationDAO; 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.RealmDAO; +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.UserDAO; import org.apache.syncope.core.persistence.api.entity.Any; @@ -121,7 +121,7 @@ public class DefaultMappingManager implements MappingManager { protected final RelationshipTypeDAO relationshipTypeDAO; - protected final RealmDAO realmDAO; + protected final RealmSearchDAO realmSearchDAO; protected final ApplicationDAO applicationDAO; @@ -143,7 +143,7 @@ public DefaultMappingManager( final AnyObjectDAO anyObjectDAO, final GroupDAO groupDAO, final RelationshipTypeDAO relationshipTypeDAO, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final ApplicationDAO applicationDAO, final ImplementationDAO implementationDAO, final DerAttrHandler derAttrHandler, @@ -157,7 +157,7 @@ public DefaultMappingManager( this.anyObjectDAO = anyObjectDAO; this.groupDAO = groupDAO; this.relationshipTypeDAO = relationshipTypeDAO; - this.realmDAO = realmDAO; + this.realmSearchDAO = realmSearchDAO; this.applicationDAO = applicationDAO; this.implementationDAO = implementationDAO; this.derAttrHandler = derAttrHandler; @@ -1103,7 +1103,7 @@ public void setIntValues(final Item item, final Attribute attr, final RealmTO re case "fullpath" -> { String parentFullPath = StringUtils.substringBeforeLast(values.get(0).toString(), "/"); - realmDAO.findByFullPath(parentFullPath).ifPresentOrElse( + realmSearchDAO.findByFullPath(parentFullPath).ifPresentOrElse( parent -> realmTO.setParent(parent.getFullPath()), () -> LOG.warn("Could not find Realm with path {}, ignoring", parentFullPath)); } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java index 0cbb1be445..c8e32d2ebe 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java @@ -50,6 +50,7 @@ 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.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO; import org.apache.syncope.core.persistence.api.dao.ReportDAO; import org.apache.syncope.core.persistence.api.dao.ReportExecDAO; @@ -361,6 +362,7 @@ public ConnectorManager connectorManager( final EntityFactory entityFactory, final ConnIdBundleManager connIdBundleManager, final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final ExternalResourceDAO resourceDAO, final ConnInstanceDataBinder connInstanceDataBinder, final AsyncConnectorFacade asyncConnectorFacade) { @@ -368,6 +370,7 @@ public ConnectorManager connectorManager( return new DefaultConnectorManager( connIdBundleManager, realmDAO, + realmSearchDAO, resourceDAO, connInstanceDataBinder, asyncConnectorFacade, @@ -389,6 +392,7 @@ public InboundMatcher inboundMatcher( final GroupDAO groupDAO, final AnySearchDAO anySearchDAO, final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final VirSchemaDAO virSchemaDAO, final ImplementationDAO implementationDAO, final VirAttrHandler virAttrHandler, @@ -400,6 +404,7 @@ public InboundMatcher inboundMatcher( groupDAO, anySearchDAO, realmDAO, + realmSearchDAO, virSchemaDAO, implementationDAO, virAttrHandler, @@ -445,7 +450,7 @@ public MappingManager mappingManager( final AnyObjectDAO anyObjectDAO, final GroupDAO groupDAO, final RelationshipTypeDAO relationshipTypeDAO, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final ApplicationDAO applicationDAO, final ImplementationDAO implementationDAO, final DerAttrHandler derAttrHandler, @@ -459,7 +464,7 @@ public MappingManager mappingManager( anyObjectDAO, groupDAO, relationshipTypeDAO, - realmDAO, + realmSearchDAO, applicationDAO, implementationDAO, derAttrHandler, @@ -482,13 +487,13 @@ public ConnObjectUtils connObjectUtils( final AnyUtilsFactory anyUtilsFactory, final MappingManager mappingManager, final TemplateUtils templateUtils, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final UserDAO userDAO, final ExternalResourceDAO resourceDAO) { return new ConnObjectUtils( templateUtils, - realmDAO, + realmSearchDAO, userDAO, resourceDAO, passwordGenerator, @@ -740,7 +745,7 @@ public AnyObjectDataBinder anyObjectDataBinder( final EntityFactory entityFactory, final AnyUtilsFactory anyUtilsFactory, final AnyTypeDAO anyTypeDAO, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final AnyTypeClassDAO anyTypeClassDAO, final AnyObjectDAO anyObjectDAO, final UserDAO userDAO, @@ -758,7 +763,7 @@ public AnyObjectDataBinder anyObjectDataBinder( return new AnyObjectDataBinderImpl( anyTypeDAO, - realmDAO, + realmSearchDAO, anyTypeClassDAO, anyObjectDAO, userDAO, @@ -843,10 +848,10 @@ public AuthProfileDataBinder authProfileDataBinder(final EntityFactory entityFac @Bean public ClientAppDataBinder clientAppDataBinder( final PolicyDAO policyDAO, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final EntityFactory entityFactory) { - return new ClientAppDataBinderImpl(policyDAO, realmDAO, entityFactory); + return new ClientAppDataBinderImpl(policyDAO, realmSearchDAO, entityFactory); } @ConditionalOnMissingBean @@ -855,9 +860,9 @@ public ConnInstanceDataBinder connInstanceDataBinder( final EntityFactory entityFactory, final ConnIdBundleManager connIdBundleManager, final ConnInstanceDAO connInstanceDAO, - final RealmDAO realmDAO) { + final RealmSearchDAO realmSearchDAO) { - return new ConnInstanceDataBinderImpl(connIdBundleManager, connInstanceDAO, realmDAO, entityFactory); + return new ConnInstanceDataBinderImpl(connIdBundleManager, connInstanceDAO, realmSearchDAO, entityFactory); } @ConditionalOnMissingBean @@ -898,7 +903,7 @@ public GroupDataBinder groupDataBinder( final SearchCondVisitor searchCondVisitor, final AnyUtilsFactory anyUtilsFactory, final AnyTypeDAO anyTypeDAO, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final AnyTypeClassDAO anyTypeClassDAO, final AnyObjectDAO anyObjectDAO, final UserDAO userDAO, @@ -916,7 +921,7 @@ public GroupDataBinder groupDataBinder( return new GroupDataBinderImpl( anyTypeDAO, - realmDAO, + realmSearchDAO, anyTypeClassDAO, anyObjectDAO, userDAO, @@ -1051,12 +1056,18 @@ public ResourceDataBinder resourceDataBinder( public RoleDataBinder roleDataBinder( final EntityFactory entityFactory, final SearchCondVisitor searchCondVisitor, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final DynRealmDAO dynRealmDAO, final RoleDAO roleDAO, final ApplicationDAO applicationDAO) { - return new RoleDataBinderImpl(realmDAO, dynRealmDAO, roleDAO, applicationDAO, entityFactory, searchCondVisitor); + return new RoleDataBinderImpl( + realmSearchDAO, + dynRealmDAO, + roleDAO, + applicationDAO, + entityFactory, + searchCondVisitor); } @ConditionalOnMissingBean @@ -1113,7 +1124,7 @@ public SecurityQuestionDataBinder securityQuestionDataBinder(final EntityFactory public TaskDataBinder taskDataBinder( final EntityFactory entityFactory, final TaskUtilsFactory taskUtilsFactory, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final ExternalResourceDAO resourceDAO, final TaskExecDAO taskExecDAO, final AnyTypeDAO anyTypeDAO, @@ -1121,7 +1132,7 @@ public TaskDataBinder taskDataBinder( final SchedulerFactoryBean scheduler) { return new TaskDataBinderImpl( - realmDAO, + realmSearchDAO, resourceDAO, taskExecDAO, anyTypeDAO, @@ -1138,7 +1149,7 @@ public UserDataBinder userDataBinder( final AnyUtilsFactory anyUtilsFactory, final SecurityProperties securityProperties, final AnyTypeDAO anyTypeDAO, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final AnyTypeClassDAO anyTypeClassDAO, final AnyObjectDAO anyObjectDAO, final UserDAO userDAO, @@ -1162,7 +1173,7 @@ public UserDataBinder userDataBinder( return new UserDataBinderImpl( anyTypeDAO, - realmDAO, + realmSearchDAO, anyTypeClassDAO, anyObjectDAO, userDAO, diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java index bddacb5dde..08192dd3d6 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java @@ -55,7 +55,7 @@ import org.apache.syncope.core.persistence.api.dao.GroupDAO; import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; -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.RelationshipTypeDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.Any; @@ -151,7 +151,7 @@ protected static MembershipTO getMembershipTO( protected final AnyTypeDAO anyTypeDAO; - protected final RealmDAO realmDAO; + protected final RealmSearchDAO realmSearchDAO; protected final AnyTypeClassDAO anyTypeClassDAO; @@ -187,7 +187,7 @@ protected static MembershipTO getMembershipTO( protected AbstractAnyDataBinder( final AnyTypeDAO anyTypeDAO, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final AnyTypeClassDAO anyTypeClassDAO, final AnyObjectDAO anyObjectDAO, final UserDAO userDAO, @@ -206,7 +206,7 @@ protected AbstractAnyDataBinder( final PlainAttrValidationManager validator) { this.anyTypeDAO = anyTypeDAO; - this.realmDAO = realmDAO; + this.realmSearchDAO = realmSearchDAO; this.anyTypeClassDAO = anyTypeClassDAO; this.anyObjectDAO = anyObjectDAO; this.userDAO = userDAO; @@ -227,7 +227,7 @@ protected AbstractAnyDataBinder( protected void setRealm(final Any any, final AnyUR anyUR) { if (anyUR.getRealm() != null && StringUtils.isNotBlank(anyUR.getRealm().getValue())) { - realmDAO.findByFullPath(anyUR.getRealm().getValue()).ifPresentOrElse( + realmSearchDAO.findByFullPath(anyUR.getRealm().getValue()).ifPresentOrElse( newRealm -> any.setRealm(newRealm), () -> LOG.debug("Invalid realm specified: {}, ignoring", anyUR.getRealm().getValue())); } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java index ba52f75893..ab4c7ddd9f 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java @@ -46,7 +46,7 @@ import org.apache.syncope.core.persistence.api.dao.GroupDAO; import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; -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.RelationshipTypeDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.AnyType; @@ -76,7 +76,7 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An public AnyObjectDataBinderImpl( final AnyTypeDAO anyTypeDAO, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final AnyTypeClassDAO anyTypeClassDAO, final AnyObjectDAO anyObjectDAO, final UserDAO userDAO, @@ -95,7 +95,7 @@ public AnyObjectDataBinderImpl( final PlainAttrValidationManager validator) { super(anyTypeDAO, - realmDAO, + realmSearchDAO, anyTypeClassDAO, anyObjectDAO, userDAO, @@ -202,7 +202,7 @@ public void create(final AnyObject anyObject, final AnyObjectCR anyObjectCR) { } // realm - Realm realm = realmDAO.findByFullPath(anyObjectCR.getRealm()).orElse(null); + Realm realm = realmSearchDAO.findByFullPath(anyObjectCR.getRealm()).orElse(null); if (realm == null) { SyncopeClientException noRealm = SyncopeClientException.build(ClientExceptionType.InvalidRealm); noRealm.getElements().add("Invalid or null realm specified: " + anyObjectCR.getRealm()); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java index 0cd3ea6456..6610416087 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ClientAppDataBinderImpl.java @@ -26,7 +26,7 @@ import org.apache.syncope.common.lib.to.SAML2SPClientAppTO; import org.apache.syncope.common.lib.types.ClientExceptionType; 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.RealmSearchDAO; 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; @@ -44,17 +44,17 @@ public class ClientAppDataBinderImpl implements ClientAppDataBinder { protected final PolicyDAO policyDAO; - protected final RealmDAO realmDAO; + protected final RealmSearchDAO realmSearchDAO; protected final EntityFactory entityFactory; public ClientAppDataBinderImpl( final PolicyDAO policyDAO, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final EntityFactory entityFactory) { this.policyDAO = policyDAO; - this.realmDAO = realmDAO; + this.realmSearchDAO = realmSearchDAO; this.entityFactory = entityFactory; } @@ -281,7 +281,7 @@ protected CASSPClientAppTO getCASClientAppTO(final CASSPClientApp clientApp) { protected void copyToEntity(final ClientApp clientApp, final ClientAppTO clientAppTO) { Optional.ofNullable(clientAppTO.getRealm()). - flatMap(realmDAO::findByFullPath). + flatMap(realmSearchDAO::findByFullPath). ifPresentOrElse(clientApp::setRealm, () -> clientApp.setRealm(null)); clientApp.setName(clientAppTO.getName()); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java index e4bcf32398..34f0f1020d 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java @@ -30,7 +30,7 @@ import org.apache.syncope.common.lib.types.ConnConfProperty; import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO; import org.apache.syncope.core.persistence.api.dao.NotFoundException; -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.entity.ConnInstance; import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.Realm; @@ -52,19 +52,19 @@ public class ConnInstanceDataBinderImpl implements ConnInstanceDataBinder { protected final ConnInstanceDAO connInstanceDAO; - protected final RealmDAO realmDAO; + protected final RealmSearchDAO realmSearchDAO; protected final EntityFactory entityFactory; public ConnInstanceDataBinderImpl( final ConnIdBundleManager connIdBundleManager, final ConnInstanceDAO connInstanceDAO, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final EntityFactory entityFactory) { this.connIdBundleManager = connIdBundleManager; this.connInstanceDAO = connInstanceDAO; - this.realmDAO = realmDAO; + this.realmSearchDAO = realmSearchDAO; this.entityFactory = entityFactory; } @@ -102,7 +102,7 @@ public ConnInstance getConnInstance(final ConnInstanceTO connInstanceTO) { connInstance.getCapabilities().addAll(connInstanceTO.getCapabilities()); if (connInstanceTO.getAdminRealm() != null) { - connInstance.setAdminRealm(realmDAO.findByFullPath(connInstanceTO.getAdminRealm()). + connInstance.setAdminRealm(realmSearchDAO.findByFullPath(connInstanceTO.getAdminRealm()). orElseThrow(() -> new NotFoundException("Realm " + connInstanceTO.getAdminRealm()))); } if (connInstance.getAdminRealm() == null) { @@ -135,7 +135,7 @@ public ConnInstance update(final ConnInstanceTO connInstanceTO) { connInstance.getCapabilities().addAll(connInstanceTO.getCapabilities()); if (connInstanceTO.getAdminRealm() != null) { - Realm realm = realmDAO.findByFullPath(connInstanceTO.getAdminRealm()). + Realm realm = realmSearchDAO.findByFullPath(connInstanceTO.getAdminRealm()). orElseThrow(() -> { SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm); sce.getElements().add("Invalid or null realm specified: " + connInstanceTO.getAdminRealm()); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java index 89534f5fa4..fdf7617199 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java @@ -42,7 +42,7 @@ import org.apache.syncope.core.persistence.api.dao.GroupDAO; import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; -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.RelationshipTypeDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; @@ -78,7 +78,7 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD public GroupDataBinderImpl( final AnyTypeDAO anyTypeDAO, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final AnyTypeClassDAO anyTypeClassDAO, final AnyObjectDAO anyObjectDAO, final UserDAO userDAO, @@ -98,7 +98,7 @@ public GroupDataBinderImpl( final PlainAttrValidationManager validator) { super(anyTypeDAO, - realmDAO, + realmSearchDAO, anyTypeClassDAO, anyObjectDAO, userDAO, @@ -165,7 +165,7 @@ public void create(final Group group, final GroupCR groupCR) { } // realm - Realm realm = realmDAO.findByFullPath(groupCR.getRealm()).orElse(null); + Realm realm = realmSearchDAO.findByFullPath(groupCR.getRealm()).orElse(null); if (realm == null) { SyncopeClientException noRealm = SyncopeClientException.build(ClientExceptionType.InvalidRealm); noRealm.getElements().add("Invalid or null realm specified: " + groupCR.getRealm()); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java index bae33cd75d..1c681b2353 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java @@ -23,7 +23,7 @@ import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.core.persistence.api.dao.ApplicationDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; -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.RoleDAO; import org.apache.syncope.core.persistence.api.entity.DynRealm; import org.apache.syncope.core.persistence.api.entity.EntityFactory; @@ -40,7 +40,7 @@ public class RoleDataBinderImpl implements RoleDataBinder { protected static final Logger LOG = LoggerFactory.getLogger(RoleDataBinder.class); - protected final RealmDAO realmDAO; + protected final RealmSearchDAO realmSearchDAO; protected final DynRealmDAO dynRealmDAO; @@ -53,14 +53,14 @@ public class RoleDataBinderImpl implements RoleDataBinder { protected final SearchCondVisitor searchCondVisitor; public RoleDataBinderImpl( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final DynRealmDAO dynRealmDAO, final RoleDAO roleDAO, final ApplicationDAO applicationDAO, final EntityFactory entityFactory, final SearchCondVisitor searchCondVisitor) { - this.realmDAO = realmDAO; + this.realmSearchDAO = realmSearchDAO; this.dynRealmDAO = dynRealmDAO; this.roleDAO = roleDAO; this.applicationDAO = applicationDAO; @@ -83,7 +83,7 @@ public Role update(final Role toBeUpdated, final RoleTO roleTO) { role.getRealms().clear(); for (String realmFullPath : roleTO.getRealms()) { - realmDAO.findByFullPath(realmFullPath).ifPresentOrElse( + realmSearchDAO.findByFullPath(realmFullPath).ifPresentOrElse( role::add, () -> LOG.debug("Invalid realm full path {}, ignoring", realmFullPath)); } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java index 09c8e7f8a1..d9d9d40ffa 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java @@ -43,7 +43,7 @@ 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.RealmDAO; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.TaskExecDAO; import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.Implementation; @@ -79,7 +79,7 @@ public class TaskDataBinderImpl extends AbstractExecutableDatabinder implements protected static final String MACRO_RUN_JOB_DELEGATE = "org.apache.syncope.core.logic.job.MacroRunJobDelegate"; - protected final RealmDAO realmDAO; + protected final RealmSearchDAO realmSearchDAO; protected final ExternalResourceDAO resourceDAO; @@ -96,7 +96,7 @@ public class TaskDataBinderImpl extends AbstractExecutableDatabinder implements protected final TaskUtilsFactory taskUtilsFactory; public TaskDataBinderImpl( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final ExternalResourceDAO resourceDAO, final TaskExecDAO taskExecDAO, final AnyTypeDAO anyTypeDAO, @@ -105,7 +105,7 @@ public TaskDataBinderImpl( final SchedulerFactoryBean scheduler, final TaskUtilsFactory taskUtilsFactory) { - this.realmDAO = realmDAO; + this.realmSearchDAO = realmSearchDAO; this.resourceDAO = resourceDAO; this.taskExecDAO = taskExecDAO; this.anyTypeDAO = anyTypeDAO; @@ -135,7 +135,7 @@ protected void fill(final ProvisioningTask provisioningTask, final Provisioni } pushTask.setJobDelegate(jobDelegate); - pushTask.setSourceRealm(realmDAO.findByFullPath(pushTaskTO.getSourceRealm()). + pushTask.setSourceRealm(realmSearchDAO.findByFullPath(pushTaskTO.getSourceRealm()). orElseThrow(() -> new NotFoundException("Realm " + pushTaskTO.getSourceRealm()))); pushTask.setMatchingRule(pushTaskTO.getMatchingRule() == null @@ -178,7 +178,7 @@ protected void fill(final ProvisioningTask provisioningTask, final Provisioni () -> LOG.debug("Invalid Implementation {}, ignoring...", pullTaskTO.getReconFilterBuilder())); } - pullTask.setDestinationRealm(realmDAO.findByFullPath(pullTaskTO.getDestinationRealm()). + pullTask.setDestinationRealm(realmSearchDAO.findByFullPath(pullTaskTO.getDestinationRealm()). orElseThrow(() -> new NotFoundException("Realm " + pullTaskTO.getDestinationRealm()))); pullTask.setMatchingRule(pullTaskTO.getMatchingRule() == null @@ -224,7 +224,7 @@ protected void fill(final ProvisioningTask provisioningTask, final Provisioni } protected void fill(final MacroTask macroTask, final MacroTaskTO macroTaskTO) { - macroTask.setRealm(realmDAO.findByFullPath(macroTaskTO.getRealm()). + macroTask.setRealm(realmSearchDAO.findByFullPath(macroTaskTO.getRealm()). orElseThrow(() -> new NotFoundException("Realm " + macroTaskTO.getRealm()))); macroTaskTO.getCommands(). @@ -288,7 +288,7 @@ public SchedTask createSchedTask(final SchedTaskTO taskTO, final TaskUtils taskU } macroTask.setJobDelegate(jobDelegate); - macroTask.setRealm(realmDAO.findByFullPath(macroTaskTO.getRealm()). + macroTask.setRealm(realmSearchDAO.findByFullPath(macroTaskTO.getRealm()). orElseThrow(() -> new NotFoundException("Realm " + macroTaskTO.getRealm()))); fill(macroTask, macroTaskTO); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java index 86df6cb15d..a0b2d2f7dd 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java @@ -60,7 +60,7 @@ import org.apache.syncope.core.persistence.api.dao.NotFoundException; import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO; import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; -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.RelationshipTypeDAO; import org.apache.syncope.core.persistence.api.dao.RoleDAO; import org.apache.syncope.core.persistence.api.dao.SecurityQuestionDAO; @@ -113,7 +113,7 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat public UserDataBinderImpl( final AnyTypeDAO anyTypeDAO, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final AnyTypeClassDAO anyTypeClassDAO, final AnyObjectDAO anyObjectDAO, final UserDAO userDAO, @@ -139,7 +139,7 @@ public UserDataBinderImpl( final SecurityProperties securityProperties) { super(anyTypeDAO, - realmDAO, + realmSearchDAO, anyTypeClassDAO, anyObjectDAO, userDAO, @@ -326,7 +326,7 @@ public void create(final User user, final UserCR userCR) { () -> LOG.warn("Ignoring unknown role with id {}", roleKey))); // realm - Realm realm = realmDAO.findByFullPath(userCR.getRealm()).orElse(null); + Realm realm = realmSearchDAO.findByFullPath(userCR.getRealm()).orElse(null); if (realm == null) { SyncopeClientException noRealm = SyncopeClientException.build(ClientExceptionType.InvalidRealm); noRealm.getElements().add("Invalid or null realm specified: " + userCR.getRealm()); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/GenerateRandomPasswordPropagationActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/GenerateRandomPasswordPropagationActions.java index 6ba037d7b2..f2f26ded86 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/GenerateRandomPasswordPropagationActions.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/GenerateRandomPasswordPropagationActions.java @@ -23,7 +23,7 @@ import java.util.stream.Collectors; 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.RealmDAO; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.provisioning.api.propagation.PropagationActions; import org.apache.syncope.core.provisioning.api.propagation.PropagationManager; @@ -49,7 +49,7 @@ public class GenerateRandomPasswordPropagationActions implements PropagationActi protected UserDAO userDAO; @Autowired - protected RealmDAO realmDAO; + protected RealmSearchDAO realmSearchDAO; @Autowired protected PasswordGenerator passwordGenerator; @@ -67,9 +67,8 @@ public void before(final PropagationTaskInfo taskInfo) { Set attrs = taskInfo.getPropagationData().getAttributes(); // generate random password - attrs.add(AttributeBuilder.buildPassword(passwordGenerator.generate( - taskInfo.getResource(), - realmDAO.findAncestors(userDAO.findById(taskInfo.getEntityKey()). + attrs.add(AttributeBuilder.buildPassword(passwordGenerator.generate(taskInfo.getResource(), + realmSearchDAO.findAncestors(userDAO.findById(taskInfo.getEntityKey()). orElseThrow(() -> new NotFoundException("User " + taskInfo.getEntityKey())).getRealm())). toCharArray())); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java index 29f132fb23..d1b45aef5a 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java @@ -19,6 +19,7 @@ package org.apache.syncope.core.provisioning.java.pushpull; 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.entity.task.ProvisioningTask; import org.apache.syncope.core.provisioning.api.AuditManager; import org.apache.syncope.core.provisioning.api.data.RealmDataBinder; @@ -41,6 +42,9 @@ public abstract class AbstractRealmResultHandler, @Autowired protected RealmDAO realmDAO; + @Autowired + protected RealmSearchDAO realmSearchDAO; + @Autowired protected RealmDataBinder binder; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java index 119135f72c..7fcb75bbb6 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java @@ -563,7 +563,7 @@ protected Result delete(final SyncDelta delta, final List realms) } try { - if (!realmDAO.findChildren(realm).isEmpty()) { + if (!realmSearchDAO.findChildren(realm).isEmpty()) { throw SyncopeClientException.build(ClientExceptionType.RealmContains); } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java index 1c14e8abaa..3df775cf66 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java @@ -141,7 +141,7 @@ private static void reportPropagation(final ProvisioningReport result, final Pro } private Realm update(final RealmTO realmTO, final ConnectorObject beforeObj, final ProvisioningReport result) { - Realm realm = realmDAO.findByFullPath(realmTO.getFullPath()). + Realm realm = realmSearchDAO.findByFullPath(realmTO.getFullPath()). orElseThrow(() -> new NotFoundException("Realm " + realmTO.getFullPath())); Map, Set> beforeAttrs = propagationManager.prepareAttrs(realm); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java index 2f499514ad..d774d6cebb 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java @@ -38,6 +38,7 @@ 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.RealmDAO; +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.VirSchemaDAO; import org.apache.syncope.core.persistence.api.dao.search.AnyCond; @@ -99,6 +100,8 @@ public class InboundMatcher { protected final RealmDAO realmDAO; + protected final RealmSearchDAO realmSearchDAO; + protected final VirSchemaDAO virSchemaDAO; protected final ImplementationDAO implementationDAO; @@ -117,6 +120,7 @@ public InboundMatcher( final GroupDAO groupDAO, final AnySearchDAO anySearchDAO, final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final VirSchemaDAO virSchemaDAO, final ImplementationDAO implementationDAO, final VirAttrHandler virAttrHandler, @@ -128,6 +132,7 @@ public InboundMatcher( this.groupDAO = groupDAO; this.anySearchDAO = anySearchDAO; this.realmDAO = realmDAO; + this.realmSearchDAO = realmSearchDAO; this.virSchemaDAO = virSchemaDAO; this.implementationDAO = implementationDAO; this.virAttrHandler = virAttrHandler; @@ -487,15 +492,15 @@ public List match(final SyncDelta syncDelta, final OrgUnit orgUnit) { case "name" -> { if (orgUnit.isIgnoreCaseMatch()) { - result.addAll( - realmDAO.findDescendants(SyncopeConstants.ROOT_REALM, connObjectKey, Pageable.unpaged())); + result.addAll(realmSearchDAO.findDescendants( + SyncopeConstants.ROOT_REALM, connObjectKey, Pageable.unpaged())); } else { - result.addAll(realmDAO.findByName(connObjectKey).stream().toList()); + result.addAll(realmSearchDAO.findByName(connObjectKey).stream().toList()); } } case "fullpath" -> { - realmDAO.findByFullPath(connObjectKey).ifPresent(result::add); + realmSearchDAO.findByFullPath(connObjectKey).ifPresent(result::add); } default -> { 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 90285e473a..a2344c27b9 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 @@ -32,7 +32,7 @@ import org.apache.syncope.core.persistence.api.dao.AnyDAO; import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; import org.apache.syncope.core.persistence.api.dao.NotFoundException; -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.search.SearchCond; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.AnyType; @@ -68,7 +68,7 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate i protected AnySearchDAO searchDAO; @Autowired - protected RealmDAO realmDAO; + protected RealmSearchDAO realmSearchDAO; @Autowired protected AnyUtilsFactory anyUtilsFactory; @@ -203,7 +203,7 @@ protected String doExecuteProvisioning( }); // Never push the root realm - List realms = realmDAO.findDescendants( + List realms = realmSearchDAO.findDescendants( profile.getTask().getSourceRealm().getFullPath(), null, Pageable.unpaged()).stream(). filter(realm -> realm.getParent() != null).toList(); boolean result = true; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java index c273aab6d6..74ae55aa96 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java @@ -35,7 +35,7 @@ import org.apache.syncope.common.lib.types.UnmatchingRule; 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.RealmDAO; +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.ExternalResource; import org.apache.syncope.core.persistence.api.entity.VirSchema; @@ -60,7 +60,7 @@ public class SinglePullJobDelegate extends PullJobDelegate implements SyncopeSin protected ImplementationDAO implementationDAO; @Autowired - protected RealmDAO realmDAO; + protected RealmSearchDAO realmSearchDAO; @Override public List pull( @@ -87,7 +87,7 @@ public List pull( task.setPerformUpdate(pullTaskTO.isPerformUpdate()); task.setPerformDelete(pullTaskTO.isPerformDelete()); task.setSyncStatus(pullTaskTO.isSyncStatus()); - task.setDestinationRealm(realmDAO.findByFullPath(pullTaskTO.getDestinationRealm()). + task.setDestinationRealm(realmSearchDAO.findByFullPath(pullTaskTO.getDestinationRealm()). orElseThrow(() -> new NotFoundException("Realm " + pullTaskTO.getDestinationRealm()))); task.setRemediation(pullTaskTO.isRemediation()); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java index ab2cacaf97..2e160619e5 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java @@ -35,7 +35,7 @@ import org.apache.syncope.common.lib.types.TaskType; 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.RealmDAO; +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.AnyUtils; import org.apache.syncope.core.persistence.api.entity.ExternalResource; @@ -63,7 +63,7 @@ public class StreamPullJobDelegate extends PullJobDelegate implements SyncopeStr private ImplementationDAO implementationDAO; @Autowired - private RealmDAO realmDAO; + private RealmSearchDAO realmSearchDAO; private PullPolicy pullPolicy( final AnyType anyType, @@ -178,7 +178,7 @@ public List pull( task.setPerformUpdate(true); task.setPerformDelete(false); task.setSyncStatus(false); - task.setDestinationRealm(realmDAO.findByFullPath(pullTaskTO.getDestinationRealm()). + task.setDestinationRealm(realmSearchDAO.findByFullPath(pullTaskTO.getDestinationRealm()). orElseThrow(() -> new NotFoundException("Realm " + pullTaskTO.getDestinationRealm()))); task.setRemediation(pullTaskTO.isRemediation()); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java index 26121b7547..96018d8a23 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java @@ -42,7 +42,7 @@ import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; -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.UserDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; @@ -137,7 +137,7 @@ public static ConnObject getConnObjectTO(final String fiql, final Set protected final TemplateUtils templateUtils; - protected final RealmDAO realmDAO; + protected final RealmSearchDAO realmSearchDAO; protected final UserDAO userDAO; @@ -151,7 +151,7 @@ public static ConnObject getConnObjectTO(final String fiql, final Set public ConnObjectUtils( final TemplateUtils templateUtils, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final UserDAO userDAO, final ExternalResourceDAO resourceDAO, final PasswordGenerator passwordGenerator, @@ -159,7 +159,7 @@ public ConnObjectUtils( final AnyUtilsFactory anyUtilsFactory) { this.templateUtils = templateUtils; - this.realmDAO = realmDAO; + this.realmSearchDAO = realmSearchDAO; this.userDAO = userDAO; this.resourceDAO = resourceDAO; this.passwordGenerator = passwordGenerator; @@ -207,8 +207,8 @@ public C getAnyCR( forEach(r -> passwordPolicies.add(r.getPasswordPolicy())); // add realm policies - realmDAO.findByFullPath(userCR.getRealm()). - ifPresent(realm -> realmDAO.findAncestors(realm).stream(). + realmSearchDAO.findByFullPath(userCR.getRealm()). + ifPresent(realm -> realmSearchDAO.findAncestors(realm).stream(). filter(ancestor -> ancestor.getPasswordPolicy() != null && !passwordPolicies.contains(ancestor.getPasswordPolicy())). forEach(ancestor -> passwordPolicies.add(ancestor.getPasswordPolicy()))); 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 530764445c..f149b41e76 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 @@ -42,7 +42,7 @@ public class ConnectorManagerTest extends AbstractTest { @BeforeEach public void before() { - connManager = new DefaultConnectorManager(connIdBundleManager, null, resourceDAO, null, null, null); + connManager = new DefaultConnectorManager(connIdBundleManager, null, null, resourceDAO, null, null, null); // Remove any other connector instance bean set up by standard ConnectorManager.load() connManager.unload(); 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 6e6493a32d..02bfd0432f 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 @@ -35,7 +35,7 @@ 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.PlainSchemaDAO; -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.UserDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; @@ -68,7 +68,7 @@ public class DefaultMappingManagerTest extends AbstractTest { private ExternalResourceDAO resourceDAO; @Autowired - private RealmDAO realmDAO; + private RealmSearchDAO realmSearchDAO; @Autowired private GroupDAO groupDAO; @@ -247,7 +247,7 @@ public void issueSYNCOPE1583() { // 0. create user matching the condition below User user = entityFactory.newEntity(User.class); user.setUsername("username"); - user.setRealm(realmDAO.findByFullPath("/even/two").orElseThrow()); + user.setRealm(realmSearchDAO.findByFullPath("/even/two").orElseThrow()); user.add(anyTypeClassDAO.findById("other").orElseThrow()); UPlainAttr cool = entityFactory.newEntity(UPlainAttr.class); diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/policy/DefaultRuleEnforcer.java b/core/spring/src/main/java/org/apache/syncope/core/spring/policy/DefaultRuleEnforcer.java index 4a8e925de5..609ee58ca3 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/policy/DefaultRuleEnforcer.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/policy/DefaultRuleEnforcer.java @@ -24,7 +24,7 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; -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.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Implementation; import org.apache.syncope.core.persistence.api.entity.Realm; @@ -42,14 +42,14 @@ public class DefaultRuleEnforcer implements RuleEnforcer { protected static final Logger LOG = LoggerFactory.getLogger(RuleEnforcer.class); - protected final RealmDAO realmDAO; + protected final RealmSearchDAO realmSearchDAO; protected final Map perContextAccountRules = new ConcurrentHashMap<>(); protected final Map perContextPasswordRules = new ConcurrentHashMap<>(); - public DefaultRuleEnforcer(final RealmDAO realmDAO) { - this.realmDAO = realmDAO; + public DefaultRuleEnforcer(final RealmSearchDAO realmSearchDAO) { + this.realmSearchDAO = realmSearchDAO; } @Transactional(readOnly = true) @@ -64,7 +64,7 @@ public List getAccountPolicies(final Realm realm, final Collectio // add realm policies if (realm != null) { - realmDAO.findAncestors(realm). + realmSearchDAO.findAncestors(realm). forEach(r -> Optional.ofNullable(r.getAccountPolicy()). filter(p -> !policies.contains(p)). ifPresent(policies::add)); @@ -105,7 +105,7 @@ public List getPasswordPolicies(final Realm realm, final Collect // add realm policies if (realm != null) { - realmDAO.findAncestors(realm). + realmSearchDAO.findAncestors(realm). forEach(r -> Optional.ofNullable(r.getPasswordPolicy()). filter(p -> !policies.contains(p)). ifPresent(policies::add)); diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java index 71e50c04aa..50e9ee95b7 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java @@ -44,7 +44,7 @@ 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.GroupDAO; -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.RoleDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.dao.search.AttrCond; @@ -89,7 +89,7 @@ public class AuthDataAccessor { protected final SecurityProperties securityProperties; - protected final RealmDAO realmDAO; + protected final RealmSearchDAO realmSearchDAO; protected final UserDAO userDAO; @@ -115,7 +115,7 @@ public class AuthDataAccessor { public AuthDataAccessor( final SecurityProperties securityProperties, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final UserDAO userDAO, final GroupDAO groupDAO, final AnySearchDAO anySearchDAO, @@ -129,7 +129,7 @@ public AuthDataAccessor( final List jwtSSOProviders) { this.securityProperties = securityProperties; - this.realmDAO = realmDAO; + this.realmSearchDAO = realmSearchDAO; this.userDAO = userDAO; this.groupDAO = groupDAO; this.anySearchDAO = anySearchDAO; @@ -295,7 +295,7 @@ protected Set getPassthroughResources(final User use } // 2. look for realms, pick the ones whose account policy has authentication resources - for (Realm realm : realmDAO.findAncestors(user.getRealm())) { + for (Realm realm : realmSearchDAO.findAncestors(user.getRealm())) { if (realm.getAccountPolicy() != null && !realm.getAccountPolicy().getResources().isEmpty()) { if (result == null) { result = realm.getAccountPolicy().getResources(); diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java index d6d0acc549..16dc35e182 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java @@ -25,7 +25,7 @@ import java.security.spec.InvalidKeySpecException; import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; -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.UserDAO; import org.apache.syncope.core.provisioning.api.rules.RuleEnforcer; import org.apache.syncope.core.spring.ApplicationContextProvider; @@ -130,8 +130,8 @@ public PasswordGenerator passwordGenerator() { @ConditionalOnMissingBean @Bean - public RuleEnforcer ruleEnforcer(final RealmDAO realmDAO) { - return new DefaultRuleEnforcer(realmDAO); + public RuleEnforcer ruleEnforcer(final RealmSearchDAO realmSearchDAO) { + return new DefaultRuleEnforcer(realmSearchDAO); } @Bean diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/WebSecurityContext.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/WebSecurityContext.java index 0b6bb0ba87..0d72a80375 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/WebSecurityContext.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/WebSecurityContext.java @@ -26,7 +26,7 @@ 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.GroupDAO; -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.RoleDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.provisioning.api.AuditManager; @@ -142,7 +142,7 @@ public AccessDeniedHandler accessDeniedHandler() { @Bean public AuthDataAccessor authDataAccessor( final SecurityProperties securityProperties, - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final UserDAO userDAO, final GroupDAO groupDAO, final AnySearchDAO anySearchDAO, @@ -157,7 +157,7 @@ public AuthDataAccessor authDataAccessor( return new AuthDataAccessor( securityProperties, - realmDAO, + realmSearchDAO, userDAO, groupDAO, anySearchDAO, diff --git a/core/starter/src/main/resources/core.properties b/core/starter/src/main/resources/core.properties index e4c74e5bc3..4e3322372d 100644 --- a/core/starter/src/main/resources/core.properties +++ b/core/starter/src/main/resources/core.properties @@ -31,13 +31,15 @@ management.endpoints.web.exposure.include=health,info,loggers,entityCache management.endpoint.health.show-details=ALWAYS management.endpoint.env.show-values=WHEN_AUTHORIZED -service.discovery.address=http://localhost:8080/syncope/rest/ +spring.main.allow-bean-definition-overriding=true spring.threads.virtual.enabled=true server.shutdown=graceful spring.lifecycle.timeout-per-shutdown-phase=30s +service.discovery.address=http://localhost:8080/syncope/rest/ + ############### # Persistence # ############### 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 cb295070a2..762273ddf8 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 @@ -29,7 +29,7 @@ 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.RealmDAO; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.jpa.MasterDomain; import org.apache.syncope.core.persistence.jpa.PersistenceContext; @@ -60,7 +60,7 @@ public TestInitializer testInitializer( } @Bean - public UserDataBinder userDataBinder(final RealmDAO realmDAO) { + public UserDataBinder userDataBinder(final RealmSearchDAO realmSearchDAO) { UserDataBinder dataBinder = mock(UserDataBinder.class); doAnswer(ic -> { @@ -68,7 +68,7 @@ public UserDataBinder userDataBinder(final RealmDAO realmDAO) { UserCR userCR = ic.getArgument(1); user.setUsername(userCR.getUsername()); - user.setRealm(realmDAO.findByFullPath(userCR.getRealm()).orElseThrow()); + user.setRealm(realmSearchDAO.findByFullPath(userCR.getRealm()).orElseThrow()); user.setCreator("admin"); user.setCreationDate(OffsetDateTime.now()); user.setCipherAlgorithm(CipherAlgorithm.SHA256); diff --git a/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/ElasticsearchPersistenceContext.java b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/ElasticsearchPersistenceContext.java similarity index 82% rename from ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/ElasticsearchPersistenceContext.java rename to ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/ElasticsearchPersistenceContext.java index 200e2d0eac..94cfdb7ee2 100644 --- a/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/ElasticsearchPersistenceContext.java +++ b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/ElasticsearchPersistenceContext.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa; +package org.apache.syncope.core.persistence.elasticsearch; import co.elastic.clients.elasticsearch.ElasticsearchClient; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; @@ -27,14 +27,14 @@ 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.RealmDAO; +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.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; -import org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAnySearchDAO; -import org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAuditEntryDAO; -import org.apache.syncope.core.persistence.jpa.dao.ElasticsearchRealmDAO; +import org.apache.syncope.core.persistence.elasticsearch.dao.ElasticsearchAnySearchDAO; +import org.apache.syncope.core.persistence.elasticsearch.dao.ElasticsearchAuditEntryDAO; +import org.apache.syncope.core.persistence.elasticsearch.dao.ElasticsearchRealmDAO; import org.apache.syncope.ext.elasticsearch.client.ElasticsearchProperties; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -46,7 +46,7 @@ public class ElasticsearchPersistenceContext { @ConditionalOnMissingBean(name = "elasticsearchAnySearchDAO") @Bean public AnySearchDAO anySearchDAO( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final @Lazy DynRealmDAO dynRealmDAO, final @Lazy UserDAO userDAO, final @Lazy GroupDAO groupDAO, @@ -59,7 +59,7 @@ public AnySearchDAO anySearchDAO( final ElasticsearchProperties props) { return new ElasticsearchAnySearchDAO( - realmDAO, + realmSearchDAO, dynRealmDAO, userDAO, groupDAO, @@ -72,14 +72,14 @@ public AnySearchDAO anySearchDAO( props.getIndexMaxResultWindow()); } - @ConditionalOnMissingBean(name = { "realmDAO", "elasticsearchRealmDAO" }) - @Bean(name = { "realmDAO", "elasticsearchRealmDAO" }) - public RealmDAO realmDAO( - @Qualifier("delegateRealmDAO") final RealmDAO delegate, + @ConditionalOnMissingBean(name = "elasticsearchRealmSearchDAO") + @Bean + public RealmSearchDAO realmSearchDAO( + final @Lazy RealmDAO realmDAO, final ElasticsearchClient client, final ElasticsearchProperties props) { - return new ElasticsearchRealmDAO(delegate, client, props.getIndexMaxResultWindow()); + return new ElasticsearchRealmDAO(realmDAO, client, props.getIndexMaxResultWindow()); } @ConditionalOnMissingBean(name = "elasticsearchAuditEntryDAO") diff --git a/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAnySearchDAO.java similarity index 98% rename from ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java rename to ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAnySearchDAO.java index a876d399cd..5707c710f5 100644 --- a/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java +++ b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAnySearchDAO.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao; +package org.apache.syncope.core.persistence.elasticsearch.dao; import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.elasticsearch._types.FieldSort; @@ -51,7 +51,7 @@ import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; 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.RealmDAO; +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.AnyCond; import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond; @@ -92,7 +92,7 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO { protected final int indexMaxResultWindow; public ElasticsearchAnySearchDAO( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final DynRealmDAO dynRealmDAO, final UserDAO userDAO, final GroupDAO groupDAO, @@ -105,7 +105,7 @@ public ElasticsearchAnySearchDAO( final int indexMaxResultWindow) { super( - realmDAO, + realmSearchDAO, dynRealmDAO, userDAO, groupDAO, @@ -135,10 +135,10 @@ protected Triple, Set, Set> getAdminRealmsFilter if (goRealm.isPresent()) { groupOwners.add(goRealm.get().getRight()); } else if (realmPath.startsWith("/")) { - Realm realm = realmDAO.findByFullPath(realmPath). + Realm realm = realmSearchDAO.findByFullPath(realmPath). orElseThrow(() -> new IllegalArgumentException("Invalid Realm full path: " + realmPath)); - realmDAO.findDescendants(realm.getFullPath(), base.getFullPath()). + realmSearchDAO.findDescendants(realm.getFullPath(), base.getFullPath()). forEach(descendant -> queries.add( new Query.Builder().term(QueryBuilders.term(). field("realm").value(descendant).build()). @@ -612,7 +612,7 @@ protected Query getQuery(final AttrCond cond, final AnyTypeKind kind) { protected Query getQuery(final AnyCond cond, final AnyTypeKind kind) { if (JAXRSService.PARAM_REALM.equals(cond.getSchema()) && cond.getExpression().startsWith("/")) { - Realm realm = realmDAO.findByFullPath(cond.getExpression()). + Realm realm = realmSearchDAO.findByFullPath(cond.getExpression()). orElseThrow(() -> new IllegalArgumentException("Invalid Realm full path: " + cond.getExpression())); cond.setExpression(realm.getKey()); } diff --git a/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAuditEntryDAO.java b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAuditEntryDAO.java similarity index 99% rename from ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAuditEntryDAO.java rename to ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAuditEntryDAO.java index 3245e7c0cf..627389ec09 100644 --- a/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAuditEntryDAO.java +++ b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAuditEntryDAO.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao; +package org.apache.syncope.core.persistence.elasticsearch.dao; import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.elasticsearch._types.FieldSort; diff --git a/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchRealmDAO.java b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchRealmDAO.java similarity index 80% rename from ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchRealmDAO.java rename to ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchRealmDAO.java index 8a6dc44f3a..2efb090073 100644 --- a/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchRealmDAO.java +++ b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchRealmDAO.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao; +package org.apache.syncope.core.persistence.elasticsearch.dao; import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.elasticsearch._types.ScriptLanguage; @@ -35,19 +35,16 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.core.persistence.api.dao.MalformedPathException; import org.apache.syncope.core.persistence.api.dao.RealmDAO; -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.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.entity.Realm; -import org.apache.syncope.core.persistence.api.entity.policy.Policy; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.transaction.annotation.Transactional; -public class ElasticsearchRealmDAO implements RealmDAO { +public class ElasticsearchRealmDAO implements RealmSearchDAO { protected static final Logger LOG = LoggerFactory.getLogger(RealmDAO.class); @@ -59,18 +56,18 @@ public class ElasticsearchRealmDAO implements RealmDAO { order(SortOrder.Asc)). build()); - protected final RealmDAO delegate; + protected final RealmDAO realmDAO; protected final ElasticsearchClient client; protected final int indexMaxResultWindow; public ElasticsearchRealmDAO( - final RealmDAO delegate, + final RealmDAO realmDAO, final ElasticsearchClient client, final int indexMaxResultWindow) { - this.delegate = delegate; + this.realmDAO = realmDAO; this.client = client; this.indexMaxResultWindow = indexMaxResultWindow; } @@ -79,10 +76,10 @@ public ElasticsearchRealmDAO( @Override public Optional findByFullPath(final String fullPath) { if (SyncopeConstants.ROOT_REALM.equals(fullPath)) { - return Optional.of(getRoot()); + return Optional.of(realmDAO.getRoot()); } - if (StringUtils.isBlank(fullPath) || !PATH_PATTERN.matcher(fullPath).matches()) { + if (StringUtils.isBlank(fullPath) || !RealmDAO.PATH_PATTERN.matcher(fullPath).matches()) { throw new MalformedPathException(fullPath); } @@ -98,7 +95,7 @@ public Optional findByFullPath(final String fullPath) { String result = client.search(request, Void.class).hits().hits().stream().findFirst(). map(Hit::id). orElse(null); - return findById(result).map(Realm.class::cast); + return realmDAO.findById(result).map(Realm.class::cast); } catch (Exception e) { LOG.error("While searching ES for one match", e); } @@ -129,7 +126,7 @@ public List findByName(final String name) { List result = search( new Query.Builder().term(QueryBuilders.term(). field("name").value(name).build()).build()); - return result.stream().map(this::findById). + return result.stream().map(realmDAO::findById). filter(Optional::isPresent).map(Optional::get).map(Realm.class::cast).toList(); } @@ -138,7 +135,7 @@ public List findChildren(final Realm realm) { List result = search( new Query.Builder().term(QueryBuilders.term(). field("parent_id").value(realm.getKey()).build()).build()); - return result.stream().map(this::findById). + return result.stream().map(realmDAO::findById). filter(Optional::isPresent).map(Optional::get).map(Realm.class::cast).toList(); } @@ -211,7 +208,7 @@ public List findDescendants(final String base, final String keyword, fina LOG.error("While searching in Elasticsearch", e); } - return result.stream().map(this::findById). + return result.stream().map(realmDAO::findById). filter(Optional::isPresent).map(Optional::get).map(Realm.class::cast).toList(); } @@ -248,70 +245,4 @@ public List findDescendants(final String base, final String prefix) { } return result; } - - @Override - public Realm getRoot() { - return delegate.getRoot(); - } - - @Override - public List findByResources(final ExternalResource resource) { - return delegate.findByResources(resource); - } - - @Override - public List findByPolicy(final T policy) { - return delegate.findByPolicy(policy); - } - - @Override - public List findByActionsContaining(final Implementation logicActions) { - return delegate.findByActionsContaining(logicActions); - } - - @Override - public List findAncestors(final Realm realm) { - return delegate.findAncestors(realm); - } - - @Override - public Page findAll(final Pageable pageable) { - return delegate.findAll(pageable); - } - - @Override - public boolean existsById(final String key) { - return delegate.existsById(key); - } - - @Transactional(readOnly = true) - @Override - public Optional findById(final String key) { - return delegate.findById(key); - } - - @Override - public long count() { - return delegate.count(); - } - - @Override - public List findAll() { - return delegate.findAll(); - } - - @Override - public S save(final S entity) { - return delegate.save(entity); - } - - @Override - public void delete(final Realm entity) { - delegate.delete(entity); - } - - @Override - public void deleteById(final String key) { - delegate.deleteById(key); - } } diff --git a/ext/elasticsearch/persistence/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ext/elasticsearch/persistence/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index d0c61e5551..7df641c86e 100644 --- a/ext/elasticsearch/persistence/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/ext/elasticsearch/persistence/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -14,4 +14,4 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -org.apache.syncope.core.persistence.jpa.ElasticsearchPersistenceContext +org.apache.syncope.core.persistence.elasticsearch.ElasticsearchPersistenceContext diff --git a/ext/elasticsearch/persistence/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java b/ext/elasticsearch/persistence/src/test/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAnySearchDAOTest.java similarity index 97% rename from ext/elasticsearch/persistence/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java rename to ext/elasticsearch/persistence/src/test/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAnySearchDAOTest.java index d23dcba83c..a836462ebe 100644 --- a/ext/elasticsearch/persistence/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java +++ b/ext/elasticsearch/persistence/src/test/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAnySearchDAOTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao; +package org.apache.syncope.core.persistence.elasticsearch.dao; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -45,6 +45,7 @@ import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; 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.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.search.AnyCond; import org.apache.syncope.core.persistence.api.dao.search.AttrCond; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; @@ -76,6 +77,9 @@ public class ElasticsearchAnySearchDAOTest { @Mock private RealmDAO realmDAO; + @Mock + private RealmSearchDAO realmSearchDAO; + @Mock private DynRealmDAO dynRealmDAO; @@ -96,7 +100,7 @@ public class ElasticsearchAnySearchDAOTest { @BeforeEach protected void setupSearchDAO() { searchDAO = new ElasticsearchAnySearchDAO( - realmDAO, + realmSearchDAO, dynRealmDAO, null, groupDAO, @@ -115,8 +119,9 @@ public void getAdminRealmsFilter4realm() throws IOException { Realm root = mock(Realm.class); when(root.getFullPath()).thenReturn(SyncopeConstants.ROOT_REALM); - when(realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM)).thenAnswer(ic -> Optional.of(root)); - when(realmDAO.findDescendants(eq(SyncopeConstants.ROOT_REALM), anyString())).thenReturn(List.of("rootKey")); + when(realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM)).thenAnswer(ic -> Optional.of(root)); + when(realmSearchDAO.findDescendants(eq(SyncopeConstants.ROOT_REALM), anyString())). + thenReturn(List.of("rootKey")); // 2. test Set adminRealms = Set.of(SyncopeConstants.ROOT_REALM); diff --git a/ext/opensearch/logic/src/main/resources/META-INF/spring.factories b/ext/opensearch/client-opensearch/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports similarity index 86% rename from ext/opensearch/logic/src/main/resources/META-INF/spring.factories rename to ext/opensearch/client-opensearch/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 1171a0276e..b5a522e73c 100644 --- a/ext/opensearch/logic/src/main/resources/META-INF/spring.factories +++ b/ext/opensearch/client-opensearch/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -14,6 +14,4 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. - -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.apache.syncope.core.logic.audit.OpenSearchLogicContext +org.apache.syncope.ext.opensearch.client.OpenSearchClientContext diff --git a/ext/opensearch/client-opensearch/src/main/resources/META-INF/spring.factories b/ext/opensearch/logic/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports similarity index 85% rename from ext/opensearch/client-opensearch/src/main/resources/META-INF/spring.factories rename to ext/opensearch/logic/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 19f4482588..d76bc3eef5 100644 --- a/ext/opensearch/client-opensearch/src/main/resources/META-INF/spring.factories +++ b/ext/opensearch/logic/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -14,6 +14,4 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. - -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.apache.syncope.ext.opensearch.client.OpenSearchClientContext +org.apache.syncope.core.logic.audit.OpenSearchLogicContext diff --git a/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/OpenSearchPersistenceContext.java b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/OpenSearchPersistenceContext.java similarity index 83% rename from ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/OpenSearchPersistenceContext.java rename to ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/OpenSearchPersistenceContext.java index 40422cd551..47e1f31233 100644 --- a/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/OpenSearchPersistenceContext.java +++ b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/OpenSearchPersistenceContext.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa; +package org.apache.syncope.core.persistence.opensearch; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; @@ -26,15 +26,15 @@ 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.RealmDAO; +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.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; -import org.apache.syncope.core.persistence.jpa.dao.OpenSearchAnySearchDAO; -import org.apache.syncope.core.persistence.jpa.dao.OpenSearchAuditEntryDAO; -import org.apache.syncope.core.persistence.jpa.dao.OpenSearchRealmDAO; +import org.apache.syncope.core.persistence.opensearch.dao.OpenSearchAnySearchDAO; +import org.apache.syncope.core.persistence.opensearch.dao.OpenSearchAuditEntryDAO; +import org.apache.syncope.core.persistence.opensearch.dao.OpenSearchRealmDAO; import org.apache.syncope.ext.opensearch.client.OpenSearchProperties; import org.opensearch.client.opensearch.OpenSearchClient; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -46,7 +46,7 @@ public class OpenSearchPersistenceContext { @ConditionalOnMissingBean(name = "openSearchAnySearchDAO") @Bean public AnySearchDAO anySearchDAO( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final @Lazy DynRealmDAO dynRealmDAO, final @Lazy UserDAO userDAO, final @Lazy GroupDAO groupDAO, @@ -59,7 +59,7 @@ public AnySearchDAO anySearchDAO( final OpenSearchProperties props) { return new OpenSearchAnySearchDAO( - realmDAO, + realmSearchDAO, dynRealmDAO, userDAO, groupDAO, @@ -72,14 +72,14 @@ public AnySearchDAO anySearchDAO( props.getIndexMaxResultWindow()); } - @ConditionalOnMissingBean(name = { "realmDAO", "openSearchRealmDAO" }) - @Bean(name = { "realmDAO", "openSearchRealmDAO" }) - public RealmDAO realmDAO( - @Qualifier("delegateRealmDAO") final RealmDAO delegate, + @ConditionalOnMissingBean(name = "openSearchRealmSearchDAO") + @Bean + public RealmSearchDAO realmSearchDAO( + final @Lazy RealmDAO realmDAO, final OpenSearchClient client, final OpenSearchProperties props) { - return new OpenSearchRealmDAO(delegate, client, props.getIndexMaxResultWindow()); + return new OpenSearchRealmDAO(realmDAO, client, props.getIndexMaxResultWindow()); } @ConditionalOnMissingBean(name = "openSearchAuditEntryDAO") diff --git a/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAO.java b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAnySearchDAO.java similarity index 98% rename from ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAO.java rename to ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAnySearchDAO.java index 87851ec0f4..6d4e05c669 100644 --- a/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAO.java +++ b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAnySearchDAO.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao; +package org.apache.syncope.core.persistence.opensearch.dao; import java.lang.reflect.Field; import java.util.ArrayList; @@ -37,7 +37,7 @@ import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; 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.RealmDAO; +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.AnyCond; import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond; @@ -92,7 +92,7 @@ public class OpenSearchAnySearchDAO extends AbstractAnySearchDAO { protected final int indexMaxResultWindow; public OpenSearchAnySearchDAO( - final RealmDAO realmDAO, + final RealmSearchDAO realmSearchDAO, final DynRealmDAO dynRealmDAO, final UserDAO userDAO, final GroupDAO groupDAO, @@ -105,7 +105,7 @@ public OpenSearchAnySearchDAO( final int indexMaxResultWindow) { super( - realmDAO, + realmSearchDAO, dynRealmDAO, userDAO, groupDAO, @@ -135,10 +135,10 @@ protected Triple, Set, Set> getAdminRealmsFilter if (goRealm.isPresent()) { groupOwners.add(goRealm.get().getRight()); } else if (realmPath.startsWith("/")) { - Realm realm = realmDAO.findByFullPath(realmPath). + Realm realm = realmSearchDAO.findByFullPath(realmPath). orElseThrow(() -> new IllegalArgumentException("Invalid Realm full path: " + realmPath)); - realmDAO.findDescendants(realm.getFullPath(), base.getFullPath()). + realmSearchDAO.findDescendants(realm.getFullPath(), base.getFullPath()). forEach(descendant -> queries.add( new Query.Builder().term(QueryBuilders.term(). field("realm").value(FieldValue.of(descendant)).build()). @@ -611,7 +611,7 @@ protected Query getQuery(final AttrCond cond, final AnyTypeKind kind) { protected Query getQuery(final AnyCond cond, final AnyTypeKind kind) { if (JAXRSService.PARAM_REALM.equals(cond.getSchema()) && cond.getExpression().startsWith("/")) { - Realm realm = realmDAO.findByFullPath(cond.getExpression()). + Realm realm = realmSearchDAO.findByFullPath(cond.getExpression()). orElseThrow(() -> new IllegalArgumentException("Invalid Realm full path: " + cond.getExpression())); cond.setExpression(realm.getKey()); } diff --git a/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAuditEntryDAO.java b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAuditEntryDAO.java similarity index 99% rename from ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAuditEntryDAO.java rename to ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAuditEntryDAO.java index 4479f784af..1c7555db5d 100644 --- a/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAuditEntryDAO.java +++ b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAuditEntryDAO.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao; +package org.apache.syncope.core.persistence.opensearch.dao; import com.fasterxml.jackson.databind.node.ObjectNode; import java.io.IOException; diff --git a/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchRealmDAO.java b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchRealmDAO.java similarity index 80% rename from ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchRealmDAO.java rename to ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchRealmDAO.java index a935d7d6db..77bea78de6 100644 --- a/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchRealmDAO.java +++ b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchRealmDAO.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao; +package org.apache.syncope.core.persistence.opensearch.dao; import java.util.List; import java.util.Optional; @@ -24,10 +24,8 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.core.persistence.api.dao.MalformedPathException; import org.apache.syncope.core.persistence.api.dao.RealmDAO; -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.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.entity.Realm; -import org.apache.syncope.core.persistence.api.entity.policy.Policy; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.ext.opensearch.client.OpenSearchUtils; import org.opensearch.client.opensearch.OpenSearchClient; @@ -43,11 +41,10 @@ import org.opensearch.client.opensearch.core.search.Hit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.transaction.annotation.Transactional; -public class OpenSearchRealmDAO implements RealmDAO { +public class OpenSearchRealmDAO implements RealmSearchDAO { protected static final Logger LOG = LoggerFactory.getLogger(RealmDAO.class); @@ -59,18 +56,18 @@ public class OpenSearchRealmDAO implements RealmDAO { order(SortOrder.Asc)). build()); - protected final RealmDAO delegate; + protected final RealmDAO realmDAO; protected final OpenSearchClient client; protected final int indexMaxResultWindow; public OpenSearchRealmDAO( - final RealmDAO delegate, + final RealmDAO realmDAO, final OpenSearchClient client, final int indexMaxResultWindow) { - this.delegate = delegate; + this.realmDAO = realmDAO; this.client = client; this.indexMaxResultWindow = indexMaxResultWindow; } @@ -79,10 +76,10 @@ public OpenSearchRealmDAO( @Override public Optional findByFullPath(final String fullPath) { if (SyncopeConstants.ROOT_REALM.equals(fullPath)) { - return Optional.of(getRoot()); + return Optional.of(realmDAO.getRoot()); } - if (StringUtils.isBlank(fullPath) || !PATH_PATTERN.matcher(fullPath).matches()) { + if (StringUtils.isBlank(fullPath) || !RealmDAO.PATH_PATTERN.matcher(fullPath).matches()) { throw new MalformedPathException(fullPath); } @@ -98,7 +95,7 @@ public Optional findByFullPath(final String fullPath) { String result = client.search(request, Void.class).hits().hits().stream().findFirst(). map(Hit::id). orElse(null); - return findById(result).map(Realm.class::cast); + return realmDAO.findById(result).map(Realm.class::cast); } catch (Exception e) { LOG.error("While searching ES for one match", e); } @@ -129,7 +126,7 @@ public List findByName(final String name) { List result = search( new Query.Builder().term(QueryBuilders.term(). field("name").value(FieldValue.of(name)).build()).build()); - return result.stream().map(this::findById). + return result.stream().map(realmDAO::findById). filter(Optional::isPresent).map(Optional::get).map(Realm.class::cast).toList(); } @@ -138,7 +135,7 @@ public List findChildren(final Realm realm) { List result = search( new Query.Builder().term(QueryBuilders.term(). field("parent_id").value(FieldValue.of(realm.getKey())).build()).build()); - return result.stream().map(this::findById). + return result.stream().map(realmDAO::findById). filter(Optional::isPresent).map(Optional::get).map(Realm.class::cast).toList(); } @@ -211,7 +208,7 @@ public List findDescendants(final String base, final String keyword, fina LOG.error("While searching in OpenSearch", e); } - return result.stream().map(this::findById). + return result.stream().map(realmDAO::findById). filter(Optional::isPresent).map(Optional::get).map(Realm.class::cast).toList(); } @@ -248,70 +245,4 @@ public List findDescendants(final String base, final String prefix) { } return result; } - - @Override - public Realm getRoot() { - return delegate.getRoot(); - } - - @Override - public List findByResources(final ExternalResource resource) { - return delegate.findByResources(resource); - } - - @Override - public List findByPolicy(final T policy) { - return delegate.findByPolicy(policy); - } - - @Override - public List findByActionsContaining(final Implementation logicActions) { - return delegate.findByActionsContaining(logicActions); - } - - @Override - public List findAncestors(final Realm realm) { - return delegate.findAncestors(realm); - } - - @Override - public Page findAll(final Pageable pageable) { - return delegate.findAll(pageable); - } - - @Override - public boolean existsById(final String key) { - return delegate.existsById(key); - } - - @Transactional(readOnly = true) - @Override - public Optional findById(final String key) { - return delegate.findById(key); - } - - @Override - public long count() { - return delegate.count(); - } - - @Override - public List findAll() { - return delegate.findAll(); - } - - @Override - public S save(final S entity) { - return delegate.save(entity); - } - - @Override - public void delete(final Realm entity) { - delegate.delete(entity); - } - - @Override - public void deleteById(final String key) { - delegate.deleteById(key); - } } diff --git a/ext/opensearch/persistence/src/main/resources/META-INF/spring.factories b/ext/opensearch/persistence/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports similarity index 85% rename from ext/opensearch/persistence/src/main/resources/META-INF/spring.factories rename to ext/opensearch/persistence/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 690d72d55e..0ca1d80e20 100644 --- a/ext/opensearch/persistence/src/main/resources/META-INF/spring.factories +++ b/ext/opensearch/persistence/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -14,6 +14,4 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. - -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - org.apache.syncope.core.persistence.jpa.OpenSearchPersistenceContext +org.apache.syncope.core.persistence.opensearch.OpenSearchPersistenceContext diff --git a/ext/opensearch/persistence/src/test/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAOTest.java b/ext/opensearch/persistence/src/test/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAnySearchDAOTest.java similarity index 97% rename from ext/opensearch/persistence/src/test/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAOTest.java rename to ext/opensearch/persistence/src/test/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAnySearchDAOTest.java index a24dffd887..8eb9302422 100644 --- a/ext/opensearch/persistence/src/test/java/org/apache/syncope/core/persistence/jpa/dao/OpenSearchAnySearchDAOTest.java +++ b/ext/opensearch/persistence/src/test/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAnySearchDAOTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.dao; +package org.apache.syncope.core.persistence.opensearch.dao; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -39,6 +39,7 @@ import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; 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.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.search.AnyCond; import org.apache.syncope.core.persistence.api.dao.search.AttrCond; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; @@ -77,6 +78,9 @@ public class OpenSearchAnySearchDAOTest { @Mock private RealmDAO realmDAO; + @Mock + private RealmSearchDAO realmSearchDAO; + @Mock private DynRealmDAO dynRealmDAO; @@ -97,7 +101,7 @@ public class OpenSearchAnySearchDAOTest { @BeforeEach protected void setupSearchDAO() { searchDAO = new OpenSearchAnySearchDAO( - realmDAO, + realmSearchDAO, dynRealmDAO, null, groupDAO, @@ -116,8 +120,9 @@ public void getAdminRealmsFilter4realm() throws IOException { Realm root = mock(Realm.class); when(root.getFullPath()).thenReturn(SyncopeConstants.ROOT_REALM); - when(realmDAO.findByFullPath(SyncopeConstants.ROOT_REALM)).thenAnswer(ic -> Optional.of(root)); - when(realmDAO.findDescendants(eq(SyncopeConstants.ROOT_REALM), anyString())).thenReturn(List.of("rootKey")); + when(realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM)).thenAnswer(ic -> Optional.of(root)); + when(realmSearchDAO.findDescendants(eq(SyncopeConstants.ROOT_REALM), anyString())). + thenReturn(List.of("rootKey")); // 2. test Set adminRealms = Set.of(SyncopeConstants.ROOT_REALM); diff --git a/wa/starter/src/main/resources/wa.properties b/wa/starter/src/main/resources/wa.properties index 501775bf04..1291ccc994 100644 --- a/wa/starter/src/main/resources/wa.properties +++ b/wa/starter/src/main/resources/wa.properties @@ -51,19 +51,16 @@ cas.service-registry.schedule.start-delay=PT30S cas.events.core.enabled=false -## -# Allow configuration classes to override bean definitions from Spring Boot -# spring.main.allow-bean-definition-overriding=true spring.main.lazy-initialization=false -service.discovery.address=http://localhost:8080/syncope-wa/ - spring.threads.virtual.enabled=true server.shutdown=graceful spring.lifecycle.timeout-per-shutdown-phase=30s +service.discovery.address=http://localhost:8080/syncope-wa/ + wa.anonymousUser=${anonymousUser} wa.anonymousKey=${anonymousKey} From 4376a9e8543babf32b045ef8676c88f1e0164fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Thu, 22 Feb 2024 17:12:38 +0100 Subject: [PATCH 05/10] Fix pgjsonb with audit and restore RoleITCase#create --- .../core/persistence/jpa/PGJPAJSONPersistenceContext.java | 1 + .../core/persistence/jpa/entity/AbstractProvidedKeyEntity.java | 2 ++ .../src/test/java/org/apache/syncope/fit/core/RoleITCase.java | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/PGJPAJSONPersistenceContext.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/PGJPAJSONPersistenceContext.java index b495a0e98c..3b8e10e4a7 100644 --- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/PGJPAJSONPersistenceContext.java +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/PGJPAJSONPersistenceContext.java @@ -89,6 +89,7 @@ public AnySearchDAO anySearchDAO( } @ConditionalOnMissingBean(name = "pgJPAJSONAuditEntryDAO") + @Bean public AuditEntryDAO auditEntryDAO(final EntityManager entityManager) { return new PGJPAJSONAuditEntryDAO(entityManager); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractProvidedKeyEntity.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractProvidedKeyEntity.java index 65c269ba18..23504068b9 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractProvidedKeyEntity.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractProvidedKeyEntity.java @@ -20,6 +20,7 @@ import jakarta.persistence.Id; import jakarta.persistence.MappedSuperclass; +import jakarta.validation.constraints.NotNull; import org.apache.syncope.core.persistence.api.entity.ProvidedKeyEntity; @MappedSuperclass @@ -28,6 +29,7 @@ public abstract class AbstractProvidedKeyEntity extends AbstractEntity implement private static final long serialVersionUID = 821537874069666593L; @Id + @NotNull private String id; @Override diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RoleITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RoleITCase.java index 292a663281..4ee3590890 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RoleITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RoleITCase.java @@ -77,7 +77,7 @@ public void create() { createRole(role); fail("This should not happen"); } catch (SyncopeClientException e) { - assertEquals(ClientExceptionType.GenericPersistence, e.getType()); + assertEquals(ClientExceptionType.InvalidRole, e.getType()); } role.setKey("new" + getUUIDString()); From 4310fd77d460fa2eca9b5dcb899a39bc6f289c4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Fri, 23 Feb 2024 06:41:48 +0100 Subject: [PATCH 06/10] upgrades --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index f175f6f3c7..19924e997c 100644 --- a/pom.xml +++ b/pom.xml @@ -413,7 +413,7 @@ under the License. 1.77 9.37.3 - 3.2.2 + 3.2.3 4.1.1 4.0.0 @@ -427,9 +427,9 @@ under the License. 3.4.4 - 8.12.1 + 8.12.2 2.12.0 - 2.8.4 + 2.9.0 3.14.0 3.3 @@ -1663,7 +1663,7 @@ under the License. org.codehaus.mojo exec-maven-plugin - 3.1.1 + 3.2.0 From bbc0a36986acb0669906a24c451946f3a7b0db45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Fri, 23 Feb 2024 07:46:56 +0100 Subject: [PATCH 07/10] How cool is to work with bad assumptions and final classes? --- .../core/persistence/neo4j/MasterDomain.java | 14 -------------- .../persistence/neo4j/Neo4jDomainRegistry.java | 10 +--------- .../core/persistence/neo4j/PersistenceContext.java | 12 +++++++++--- 3 files changed, 10 insertions(+), 26 deletions(-) diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/MasterDomain.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/MasterDomain.java index 196b4981a4..d4dd9c5336 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/MasterDomain.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/MasterDomain.java @@ -33,7 +33,6 @@ import org.springframework.core.io.ResourceLoader; import org.springframework.data.neo4j.core.Neo4jClient; import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager; -import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager; @EnableConfigurationProperties(PersistenceProperties.class) @Configuration(proxyBeanMethods = false) @@ -64,19 +63,6 @@ public Neo4jClient masterNeo4jClient( build(); } - @ConditionalOnMissingBean(name = "MasterNeo4jTransactionManager") - @Bean(name = "MasterNeo4jTransactionManager") - public Neo4jTransactionManager masterNeo4jTransactionManager( - @Qualifier("MasterDriver") - final Driver driver, - final Neo4jBookmarkManager bookmarkManager) { - - return Neo4jTransactionManager. - with(driver). - withBookmarkManager(bookmarkManager). - build(); - } - @Bean(name = "MasterContentXML") public InputStream masterContentXML( final ResourceLoader resourceLoader, diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainRegistry.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainRegistry.java index 0df170a95c..2536193771 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainRegistry.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainRegistry.java @@ -88,14 +88,11 @@ public void register(final Domain domain) { // DomainRoutingNeo4jClient#add beanFactory().getBean(DomainRoutingNeo4jClient.class).add(domain.getKey(), neo4jClient); - // domainNeo4jTransactionManager + // DomainRoutingTransactionManager#add Neo4jTransactionManager transactionManager = Neo4jTransactionManager. with(driver). withBookmarkManager(ctx.getBean(Neo4jBookmarkManager.class)). build(); - registerSingleton(domain.getKey().toLowerCase() + "Neo4jTransactionManager", transactionManager); - - // DomainRoutingTransactionManager#add beanFactory().getBean(DomainRoutingNeo4jTransactionManager.class).add(domain.getKey(), transactionManager); // domainContentXML @@ -121,8 +118,6 @@ public void unregister(final String domain) { unregisterSingleton(domain + "ContentXML"); beanFactory().removeBeanDefinition(domain + "ContentXML"); - // domainTransactionManager - unregisterSingleton(domain + "Neo4jTransactionManager"); // DomainRoutingTransactionManager#remove beanFactory().getBean(DomainRoutingNeo4jTransactionManager.class).remove(domain); @@ -134,9 +129,6 @@ public void unregister(final String domain) { // domainDriver unregisterSingleton(domain + "Driver"); - // domainDataSource - unregisterSingleton(domain + "Driver"); - beanFactory().getBean(DomainHolder.class).getDomains().remove(domain); } } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java index 2c1b6b1cba..a7113d683e 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java @@ -277,11 +277,17 @@ public Neo4jOperations neo4jTemplate( @Bean(Neo4jRepositoryConfigurationExtension.DEFAULT_TRANSACTION_MANAGER_BEAN_NAME) public PlatformTransactionManager transactionManager( - @Qualifier("MasterNeo4jTransactionManager") - final Neo4jTransactionManager masterNeo4jTransactionManager) { + @Qualifier("MasterDriver") + final Driver driver, + final Neo4jBookmarkManager bookmarkManager) { DomainRoutingNeo4jTransactionManager transactionManager = new DomainRoutingNeo4jTransactionManager(); - transactionManager.add(SyncopeConstants.MASTER_DOMAIN, masterNeo4jTransactionManager); + transactionManager.add( + SyncopeConstants.MASTER_DOMAIN, + Neo4jTransactionManager. + with(driver). + withBookmarkManager(bookmarkManager). + build()); return transactionManager; } From d6bbe59757754cb47fe5b6a02b8a36fedf0c8b7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Fri, 23 Feb 2024 12:32:29 +0100 Subject: [PATCH 08/10] Keymaster support for JPA or Neo4j domain definition --- .../console/panels/DomainDirectoryPanel.java | 3 +- .../console/panels/DomainPoolModalPanel.java | 13 +- .../console/panels/DomainWizardBuilder.java | 10 +- .../syncope/client/console/AbstractTest.java | 5 +- .../syncope/client/enduser/AbstractTest.java | 4 +- .../keymaster/client/api/model/Domain.java | 259 ++++-------------- .../keymaster/client/api/model/JPADomain.java | 245 +++++++++++++++++ .../client/api/model/Neo4jDomain.java | 132 +++++++++ ...aultContent.xml => defaultContent.jpa.xml} | 0 .../main/resources/defaultContent.neo4j.xml | 103 +++++++ .../client/zookeeper/ZookeeperDomainOps.java | 16 +- .../zookeeper/ZookeeperDomainOpsITCase.java | 17 +- .../syncope/core/logic/DummyDomainOps.java | 9 +- .../core/logic/IdMLogicTestContext.java | 3 +- .../syncope/core/logic/DummyDomainOps.java | 9 +- .../core/logic/IdRepoLogicTestContext.java | 3 +- .../rest/cxf/SyncopeOpenApiCustomizer.java | 2 +- .../core/persistence/api/DomainRegistry.java | 6 +- .../api/dao/keymaster}/ConfParamDAO.java | 5 +- .../api/dao/keymaster}/DomainDAO.java | 5 +- .../api/dao/keymaster}/NetworkServiceDAO.java | 7 +- .../api/entity/keymaster}/ConfParam.java | 3 +- .../api/entity/keymaster}/DomainEntity.java | 3 +- .../keymaster}/NetworkServiceEntity.java | 3 +- .../common/RuntimeDomainLoader.java | 9 +- .../persistence/jpa/DomainProperties.java | 9 +- .../persistence/jpa/JPADomainRegistry.java | 6 +- .../jpa/JPARuntimeDomainLoader.java | 5 +- .../persistence/jpa/PersistenceContext.java | 41 ++- .../persistence/jpa/StartupDomainLoader.java | 12 +- .../jpa/dao/repo/ConfParamRepo.java | 4 +- .../persistence/jpa/dao/repo/DomainRepo.java | 4 +- .../jpa/dao/repo/NetworkServiceRepo.java | 4 +- .../jpa/dao/repo/NetworkServiceRepoExt.java | 4 +- .../dao/repo/NetworkServiceRepoExtImpl.java | 8 +- .../jpa/entity/JPAEntityFactory.java | 12 + .../jpa/entity/keymaster}/JPAConfParam.java | 5 +- .../jpa/entity/keymaster}/JPADomain.java | 5 +- .../entity/keymaster}/JPANetworkService.java | 5 +- .../DomainRoutingEntityManagerFactory.java | 4 +- .../core/persistence/jpa/DummyDomainOps.java | 9 +- .../jpa/PersistenceTestContext.java | 3 +- .../neo4j/Neo4jDomainRegistry.java | 13 +- .../persistence/neo4j/PersistenceContext.java | 43 ++- .../neo4j/StartupDomainLoader.java | 20 +- .../neo4j/dao/repo/ConfParamRepo.java | 26 ++ .../neo4j/dao/repo/DomainRepo.java} | 12 +- .../neo4j/dao/repo/NetworkServiceRepo.java | 28 ++ .../neo4j/dao/repo/NetworkServiceRepoExt.java | 33 +++ .../dao/repo/NetworkServiceRepoExtImpl.java | 67 +++++ .../neo4j/entity/Neo4jEntityFactory.java | 12 + .../entity/keymaster/Neo4jConfParam.java | 59 ++++ .../neo4j/entity/keymaster/Neo4jDomain.java | 48 ++++ .../entity/keymaster/Neo4jNetworkService.java | 59 ++++ .../persistence/neo4j/DummyDomainOps.java | 9 +- .../neo4j/PersistenceTestContext.java | 3 +- .../src/test/resources/domains/TwoContent.xml | 3 +- .../provisioning/java/DummyDomainOps.java | 9 +- .../java/ProvisioningTestContext.java | 3 +- .../internal/InternalConfParamHelper.java | 12 +- .../syncope/core/logic/DomainLogic.java | 37 ++- .../core/logic/NetworkServiceLogic.java | 12 +- .../entity/JPASelfKeymasterEntityFactory.java | 45 --- .../core/starter/SelfKeymasterContext.java | 48 +--- ...sernamePasswordAuthenticationProvider.java | 34 ++- .../core/workflow/java/DummyDomainOps.java | 9 +- .../workflow/java/WorkflowTestContext.java | 3 +- .../syncope/fit/core/KeymasterITCase.java | 15 +- 68 files changed, 1202 insertions(+), 474 deletions(-) create mode 100644 common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/model/JPADomain.java create mode 100644 common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/model/Neo4jDomain.java rename common/keymaster/client-api/src/main/resources/{defaultContent.xml => defaultContent.jpa.xml} (100%) create mode 100644 common/keymaster/client-api/src/main/resources/defaultContent.neo4j.xml rename core/{self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/dao => persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/keymaster}/ConfParamDAO.java (81%) rename core/{self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/dao => persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/keymaster}/DomainDAO.java (81%) rename core/{self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/dao => persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/keymaster}/NetworkServiceDAO.java (80%) rename core/{self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity => persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster}/ConfParam.java (87%) rename core/{self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity => persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster}/DomainEntity.java (87%) rename core/{self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity => persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster}/NetworkServiceEntity.java (89%) rename core/{self-keymaster-starter => persistence-jpa}/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConfParamRepo.java (87%) rename core/{self-keymaster-starter => persistence-jpa}/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DomainRepo.java (87%) rename core/{self-keymaster-starter => persistence-jpa}/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepo.java (86%) rename core/{self-keymaster-starter => persistence-jpa}/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExt.java (89%) rename core/{self-keymaster-starter => persistence-jpa}/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExtImpl.java (89%) rename core/{self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity => persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster}/JPAConfParam.java (90%) rename core/{self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity => persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster}/JPADomain.java (88%) rename core/{self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity => persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster}/JPANetworkService.java (89%) create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConfParamRepo.java rename core/{self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/SelfKeymasterEntityFactory.java => persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DomainRepo.java} (69%) create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepo.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExt.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExtImpl.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jConfParam.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jDomain.java create mode 100644 core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jNetworkService.java delete mode 100644 core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASelfKeymasterEntityFactory.java diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainDirectoryPanel.java index b279dd0437..562f6cec3e 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainDirectoryPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainDirectoryPanel.java @@ -37,6 +37,7 @@ 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.keymaster.client.api.model.JPADomain; import org.apache.syncope.common.lib.types.IdRepoEntitlement; import org.apache.wicket.PageReference; import org.apache.wicket.ajax.AjaxRequestTarget; @@ -77,7 +78,7 @@ public DomainDirectoryPanel(final String id, final PageReference pageRef) { utilityModal.size(Modal.Size.Small); utilityModal.addSubmitButton(); - addNewItemPanelBuilder(new DomainWizardBuilder(domainOps, new Domain(), pageRef), true); + addNewItemPanelBuilder(new DomainWizardBuilder(domainOps, new JPADomain(), pageRef), true); initResultTable(); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainPoolModalPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainPoolModalPanel.java index f16d222ace..bdfaf2a99c 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainPoolModalPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainPoolModalPanel.java @@ -25,6 +25,8 @@ import org.apache.syncope.client.ui.commons.markup.html.form.AjaxSpinnerFieldPanel; import org.apache.syncope.common.keymaster.client.api.DomainOps; 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.wicket.PageReference; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.model.PropertyModel; @@ -62,8 +64,17 @@ public Domain getItem() { @Override public void onSubmit(final AjaxRequestTarget target) { + int max = 10; + int min = 0; + if (domain instanceof JPADomain jpaDomain) { + max = jpaDomain.getPoolMaxActive(); + min = jpaDomain.getPoolMinIdle(); + } else if (domain instanceof Neo4jDomain neo4jDomain) { + max = neo4jDomain.getMaxConnectionPoolSize(); + } + try { - domainOps.adjustPoolSize(domain.getKey(), domain.getPoolMaxActive(), domain.getPoolMinIdle()); + domainOps.adjustPoolSize(domain.getKey(), max, min); SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED)); this.modal.close(target); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainWizardBuilder.java index afb235e854..50b1c576c4 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainWizardBuilder.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainWizardBuilder.java @@ -32,6 +32,7 @@ import org.apache.syncope.client.ui.commons.markup.html.form.EncryptedFieldPanel; import org.apache.syncope.common.keymaster.client.api.DomainOps; 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.lib.types.CipherAlgorithm; import org.apache.wicket.PageReference; import org.apache.wicket.ajax.AjaxRequestTarget; @@ -119,10 +120,11 @@ public Storage(final Domain domain) { add(new EncryptedFieldPanel( "dbPassword", "dbPassword", new PropertyModel<>(domain, "dbPassword"), false)); - AjaxDropDownChoicePanel transactionIsolation = new AjaxDropDownChoicePanel<>( - "transactionIsolation", "transactionIsolation", - new PropertyModel<>(domain, "transactionIsolation"), false); - transactionIsolation.setChoices(List.of(Domain.TransactionIsolation.values())); + AjaxDropDownChoicePanel transactionIsolation = + new AjaxDropDownChoicePanel<>( + "transactionIsolation", "transactionIsolation", + new PropertyModel<>(domain, "transactionIsolation"), false); + transactionIsolation.setChoices(List.of(JPADomain.TransactionIsolation.values())); transactionIsolation.addRequiredLabel(); transactionIsolation.setNullValid(false); add(transactionIsolation); diff --git a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/AbstractTest.java b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/AbstractTest.java index 76eff0f7b5..4584a7b291 100644 --- a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/AbstractTest.java +++ b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/AbstractTest.java @@ -53,7 +53,7 @@ import org.apache.syncope.client.lib.SyncopeClientFactoryBean; 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.Domain; +import org.apache.syncope.common.keymaster.client.api.model.JPADomain; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.info.NumbersInfo; import org.apache.syncope.common.lib.info.PlatformInfo; @@ -105,7 +105,8 @@ public ServiceOps selfServiceOps() { @Bean public DomainOps domainOps() { DomainOps domainOps = mock(DomainOps.class); - when(domainOps.list()).thenReturn(List.of(new Domain.Builder(SyncopeConstants.MASTER_DOMAIN).build())); + when(domainOps.list()).thenReturn( + List.of(new JPADomain.Builder(SyncopeConstants.MASTER_DOMAIN).build())); return domainOps; } diff --git a/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/AbstractTest.java b/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/AbstractTest.java index 4cff154aed..e522834cfe 100644 --- a/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/AbstractTest.java +++ b/client/idrepo/enduser/src/test/java/org/apache/syncope/client/enduser/AbstractTest.java @@ -41,7 +41,7 @@ import org.apache.syncope.client.lib.SyncopeClientFactoryBean; 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.Domain; +import org.apache.syncope.common.keymaster.client.api.model.JPADomain; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.info.NumbersInfo; import org.apache.syncope.common.lib.info.PlatformInfo; @@ -95,7 +95,7 @@ public ServiceOps selfServiceOps() { @Bean public DomainOps domainOps() { DomainOps domainOps = mock(DomainOps.class); - when(domainOps.list()).thenReturn(List.of(new Domain.Builder(SyncopeConstants.MASTER_DOMAIN).build())); + when(domainOps.list()).thenReturn(List.of(new JPADomain.Builder(SyncopeConstants.MASTER_DOMAIN).build())); return domainOps; } diff --git a/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/model/Domain.java b/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/model/Domain.java index f6fbceee4b..eec05b4b6f 100644 --- a/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/model/Domain.java +++ b/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/model/Domain.java @@ -18,208 +18,97 @@ */ package org.apache.syncope.common.keymaster.client.api.model; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import java.io.IOException; import java.io.Serializable; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.cxf.helpers.IOUtils; import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class Domain implements Serializable { +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "_class") +@JsonPropertyOrder(value = { "_class", "key" }) +public abstract class Domain implements Serializable { private static final long serialVersionUID = -5881851479361505961L; private static final Logger LOG = LoggerFactory.getLogger(Domain.class); - public enum TransactionIsolation { - TRANSACTION_NONE, - TRANSACTION_READ_COMMITTED, - TRANSACTION_READ_UNCOMMITTED, - TRANSACTION_REPEATABLE_READ, - TRANSACTION_SERIALIZABLE + protected abstract static class Builder> { - } - - public static class Builder { - - private final Domain domain; + protected final D domain; - public Builder(final String key) { - this.domain = new Domain(); + Builder(final D domain, final String key) { + this.domain = domain; this.domain.key = key; } - public Builder jdbcDriver(final String jdbcDriver) { - this.domain.jdbcDriver = jdbcDriver; - return this; - } - - public Builder jdbcURL(final String jdbcURL) { - this.domain.jdbcURL = jdbcURL; - return this; - } - - public Builder dbSchema(final String dbSchema) { - if (StringUtils.isNotBlank(dbSchema)) { - this.domain.dbSchema = dbSchema; - } - return this; - } - - public Builder dbUsername(final String dbUsername) { - this.domain.dbUsername = dbUsername; - return this; - } - - public Builder dbPassword(final String dbPassword) { - this.domain.dbPassword = dbPassword; - return this; - } - - public Builder transactionIsolation(final TransactionIsolation transactionIsolation) { - this.domain.transactionIsolation = transactionIsolation; - return this; - } - - public Builder poolMaxActive(final int poolMaxActive) { - this.domain.poolMaxActive = poolMaxActive; - return this; - } - - public Builder poolMinIdle(final int poolMinIdle) { - this.domain.poolMinIdle = poolMinIdle; - return this; - } - - public Builder auditSql(final String auditSql) { - this.domain.auditSql = auditSql; - return this; - } - - public Builder orm(final String orm) { - this.domain.orm = orm; - return this; - } - - public Builder databasePlatform(final String databasePlatform) { - this.domain.databasePlatform = databasePlatform; - return this; - } - - public Builder adminPassword(final String adminPassword) { + @SuppressWarnings("unchecked") + public B adminPassword(final String adminPassword) { this.domain.adminPassword = adminPassword; - return this; + return (B) this; } - public Builder adminCipherAlgorithm(final CipherAlgorithm adminCipherAlgorithm) { + @SuppressWarnings("unchecked") + public B adminCipherAlgorithm(final CipherAlgorithm adminCipherAlgorithm) { this.domain.adminCipherAlgorithm = adminCipherAlgorithm; - return this; + return (B) this; } - public Builder content(final String content) { + @SuppressWarnings("unchecked") + public B content(final String content) { this.domain.content = content; - return this; + return (B) this; } - public Builder keymasterConfParams(final String keymasterConfParams) { + @SuppressWarnings("unchecked") + public B keymasterConfParams(final String keymasterConfParams) { this.domain.keymasterConfParams = keymasterConfParams; - return this; + return (B) this; } - public Domain build() { + public D build() { return this.domain; } } - private String key; - - private String jdbcDriver; - - private String jdbcURL; - - private String dbSchema; - - private String dbUsername; - - private String dbPassword; - - private TransactionIsolation transactionIsolation = TransactionIsolation.TRANSACTION_READ_COMMITTED; - - private int poolMaxActive = 10; - - private int poolMinIdle = 2; - - private String auditSql = "audit.sql"; - - private String orm = "META-INF/spring-orm.xml"; - - private String databasePlatform; - - private String adminPassword; - - private CipherAlgorithm adminCipherAlgorithm = CipherAlgorithm.SHA512; - - private String content; - - private String keymasterConfParams; - - public String getKey() { - return key; - } - - public String getJdbcDriver() { - return jdbcDriver; - } - - public String getJdbcURL() { - return jdbcURL; - } - - public String getDbSchema() { - return dbSchema; - } - - public String getDbUsername() { - return dbUsername; - } + protected static String read(final String filename) { + String read = null; + try { + read = IOUtils.toString(Domain.class.getResourceAsStream('/' + filename)); + } catch (IOException e) { + LOG.error("Could not read {}", filename, e); + } - public String getDbPassword() { - return dbPassword; + return read; } - public TransactionIsolation getTransactionIsolation() { - return transactionIsolation; - } + protected String key; - public int getPoolMaxActive() { - return poolMaxActive; - } + protected String adminPassword; - public void setPoolMaxActive(final int poolMaxActive) { - this.poolMaxActive = poolMaxActive; - } + protected CipherAlgorithm adminCipherAlgorithm = CipherAlgorithm.SHA512; - public int getPoolMinIdle() { - return poolMinIdle; - } + protected String content; - public void setPoolMinIdle(final int poolMinIdle) { - this.poolMinIdle = poolMinIdle; - } + protected String keymasterConfParams; - public String getAuditSql() { - return auditSql; + @JsonProperty("_class") + public String getDiscriminator() { + return getClass().getName(); } - public String getOrm() { - return orm; + public void setDiscriminator(final String discriminator) { + // do nothing } - public String getDatabasePlatform() { - return databasePlatform; + public String getKey() { + return key; } public String getAdminPassword() { @@ -238,20 +127,11 @@ public void setAdminCipherAlgorithm(final CipherAlgorithm adminCipherAlgorithm) this.adminCipherAlgorithm = adminCipherAlgorithm; } - private String read(final String filename) { - String read = null; - try { - read = IOUtils.toString(Domain.class.getResourceAsStream('/' + filename)); - } catch (IOException e) { - LOG.error("Could not read {}", filename, e); - } - - return read; - } + protected abstract String defaultContentFile(); public String getContent() { if (content == null) { - content = read("defaultContent.xml"); + content = read(defaultContentFile()); } return content; @@ -269,17 +149,6 @@ public String getKeymasterConfParams() { public int hashCode() { return new HashCodeBuilder(). append(key). - append(jdbcDriver). - append(jdbcURL). - append(dbSchema). - append(dbUsername). - append(dbPassword). - append(transactionIsolation). - append(poolMaxActive). - append(poolMinIdle). - append(auditSql). - append(orm). - append(databasePlatform). append(adminPassword). append(adminCipherAlgorithm). append(content). @@ -301,17 +170,6 @@ public boolean equals(final Object obj) { final Domain other = (Domain) obj; return new EqualsBuilder(). append(key, other.key). - append(jdbcDriver, other.jdbcDriver). - append(jdbcURL, other.jdbcURL). - append(dbSchema, other.dbSchema). - append(dbUsername, other.dbUsername). - append(dbPassword, other.dbPassword). - append(transactionIsolation, other.transactionIsolation). - append(poolMaxActive, other.poolMaxActive). - append(poolMinIdle, other.poolMinIdle). - append(auditSql, other.auditSql). - append(orm, other.orm). - append(databasePlatform, other.databasePlatform). append(adminPassword, other.adminPassword). append(adminCipherAlgorithm, other.adminCipherAlgorithm). append(content, other.content). @@ -321,23 +179,12 @@ public boolean equals(final Object obj) { @Override public String toString() { - return "Domain{" - + "key=" + key - + ", jdbcDriver=" + jdbcDriver - + ", jdbcURL=" + jdbcURL - + ", dbSchema=" + dbSchema - + ", dbUsername=" + dbUsername - + ", dbPassword=" + dbPassword - + ", transactionIsolation=" + transactionIsolation - + ", poolMaxSize=" + poolMaxActive - + ", poolMinIdle=" + poolMinIdle - + ", auditSql=" + auditSql - + ", orm=" + orm - + ", databasePlatform=" + databasePlatform - + ", adminPassword=" + adminPassword - + ", adminCipherAlgorithm=" + adminCipherAlgorithm - + ", content=" + content - + ", keymasterConfParams=" + keymasterConfParams - + '}'; + return new ToStringBuilder(this). + append(key). + append(adminPassword). + append(adminCipherAlgorithm). + append(content). + append(keymasterConfParams). + build(); } } diff --git a/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/model/JPADomain.java b/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/model/JPADomain.java new file mode 100644 index 0000000000..e7eb80e287 --- /dev/null +++ b/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/model/JPADomain.java @@ -0,0 +1,245 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; + +public class JPADomain extends Domain { + + private static final long serialVersionUID = 18711766451769410L; + + public enum TransactionIsolation { + TRANSACTION_NONE, + TRANSACTION_READ_COMMITTED, + TRANSACTION_READ_UNCOMMITTED, + TRANSACTION_REPEATABLE_READ, + TRANSACTION_SERIALIZABLE + + } + + public static class Builder extends Domain.Builder { + + public Builder(final String key) { + super(new JPADomain(), key); + } + + public Builder jdbcDriver(final String jdbcDriver) { + this.domain.jdbcDriver = jdbcDriver; + return this; + } + + public Builder jdbcURL(final String jdbcURL) { + this.domain.jdbcURL = jdbcURL; + return this; + } + + public Builder dbSchema(final String dbSchema) { + if (StringUtils.isNotBlank(dbSchema)) { + this.domain.dbSchema = dbSchema; + } + return this; + } + + public Builder dbUsername(final String dbUsername) { + this.domain.dbUsername = dbUsername; + return this; + } + + public Builder dbPassword(final String dbPassword) { + this.domain.dbPassword = dbPassword; + return this; + } + + public Builder transactionIsolation(final TransactionIsolation transactionIsolation) { + this.domain.transactionIsolation = transactionIsolation; + return this; + } + + public Builder poolMaxActive(final int poolMaxActive) { + this.domain.poolMaxActive = poolMaxActive; + return this; + } + + public Builder poolMinIdle(final int poolMinIdle) { + this.domain.poolMinIdle = poolMinIdle; + return this; + } + + public Builder auditSql(final String auditSql) { + this.domain.auditSql = auditSql; + return this; + } + + public Builder orm(final String orm) { + this.domain.orm = orm; + return this; + } + + public Builder databasePlatform(final String databasePlatform) { + this.domain.databasePlatform = databasePlatform; + return this; + } + } + + private String jdbcDriver; + + private String jdbcURL; + + private String dbSchema; + + private String dbUsername; + + private String dbPassword; + + private TransactionIsolation transactionIsolation = TransactionIsolation.TRANSACTION_READ_COMMITTED; + + private int poolMaxActive = 10; + + private int poolMinIdle = 2; + + private String auditSql = "audit.sql"; + + private String orm = "META-INF/spring-orm.xml"; + + private String databasePlatform; + + @Override + protected String defaultContentFile() { + return "defaultContent.jpa.xml"; + } + + public String getJdbcDriver() { + return jdbcDriver; + } + + public String getJdbcURL() { + return jdbcURL; + } + + public String getDbSchema() { + return dbSchema; + } + + public String getDbUsername() { + return dbUsername; + } + + public String getDbPassword() { + return dbPassword; + } + + public TransactionIsolation getTransactionIsolation() { + return transactionIsolation; + } + + public int getPoolMaxActive() { + return poolMaxActive; + } + + public void setPoolMaxActive(final int poolMaxActive) { + this.poolMaxActive = poolMaxActive; + } + + public int getPoolMinIdle() { + return poolMinIdle; + } + + public void setPoolMinIdle(final int poolMinIdle) { + this.poolMinIdle = poolMinIdle; + } + + public String getAuditSql() { + return auditSql; + } + + public String getOrm() { + return orm; + } + + public String getDatabasePlatform() { + return databasePlatform; + } + + @Override + public int hashCode() { + return new HashCodeBuilder(). + appendSuper(super.hashCode()). + append(jdbcDriver). + append(jdbcURL). + append(dbSchema). + append(dbUsername). + append(dbPassword). + append(transactionIsolation). + append(poolMaxActive). + append(poolMinIdle). + append(auditSql). + append(orm). + append(databasePlatform). + build(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final JPADomain other = (JPADomain) obj; + return new EqualsBuilder(). + appendSuper(super.equals(obj)). + append(jdbcDriver, other.jdbcDriver). + append(jdbcURL, other.jdbcURL). + append(dbSchema, other.dbSchema). + append(dbUsername, other.dbUsername). + append(dbPassword, other.dbPassword). + append(transactionIsolation, other.transactionIsolation). + append(poolMaxActive, other.poolMaxActive). + append(poolMinIdle, other.poolMinIdle). + append(auditSql, other.auditSql). + append(orm, other.orm). + append(databasePlatform, other.databasePlatform). + build(); + } + + @Override + public String toString() { + return new ToStringBuilder(this). + appendSuper(super.toString()). + append(jdbcDriver). + append(jdbcURL). + append(dbSchema). + append(dbUsername). + append(dbPassword). + append(transactionIsolation). + append(poolMaxActive). + append(poolMinIdle). + append(auditSql). + append(orm). + append(databasePlatform). + build(); + } +} diff --git a/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/model/Neo4jDomain.java b/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/model/Neo4jDomain.java new file mode 100644 index 0000000000..773c9d0ad2 --- /dev/null +++ b/common/keymaster/client-api/src/main/java/org/apache/syncope/common/keymaster/client/api/model/Neo4jDomain.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.keymaster.client.api.model; + +import java.net.URI; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; + +public class Neo4jDomain extends Domain { + + private static final long serialVersionUID = 9223353502929953472L; + + public static class Builder extends Domain.Builder { + + public Builder(final String key) { + super(new Neo4jDomain(), key); + } + + public Builder uri(final URI uri) { + domain.uri = uri; + return this; + } + + public Builder username(final String username) { + domain.username = username; + return this; + } + + public Builder password(final String password) { + domain.password = password; + return this; + } + + public Builder maxConnectionPoolSize(final int maxConnectionPoolSize) { + domain.maxConnectionPoolSize = maxConnectionPoolSize; + return this; + } + } + + private URI uri; + + private String username; + + private String password; + + private int maxConnectionPoolSize = 100; + + @Override + protected String defaultContentFile() { + return "defaultContent.neo4j.xml"; + } + + public URI getUri() { + return uri; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public int getMaxConnectionPoolSize() { + return maxConnectionPoolSize; + } + + public void setMaxConnectionPoolSize(final int maxConnectionPoolSize) { + this.maxConnectionPoolSize = maxConnectionPoolSize; + } + + @Override + public int hashCode() { + return new HashCodeBuilder(). + appendSuper(super.hashCode()). + append(uri). + append(username). + append(password). + append(maxConnectionPoolSize). + build(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Neo4jDomain other = (Neo4jDomain) obj; + return new EqualsBuilder(). + appendSuper(super.equals(obj)). + append(uri, other.uri). + append(username, other.username). + append(password, other.password). + append(maxConnectionPoolSize, other.maxConnectionPoolSize). + build(); + } + + @Override + public String toString() { + return new ToStringBuilder(this). + appendSuper(super.toString()). + append(uri). + append(username). + append(password). + append(maxConnectionPoolSize). + build(); + } +} diff --git a/common/keymaster/client-api/src/main/resources/defaultContent.xml b/common/keymaster/client-api/src/main/resources/defaultContent.jpa.xml similarity index 100% rename from common/keymaster/client-api/src/main/resources/defaultContent.xml rename to common/keymaster/client-api/src/main/resources/defaultContent.jpa.xml diff --git a/common/keymaster/client-api/src/main/resources/defaultContent.neo4j.xml b/common/keymaster/client-api/src/main/resources/defaultContent.neo4j.xml new file mode 100644 index 0000000000..da0ba9913b --- /dev/null +++ b/common/keymaster/client-api/src/main/resources/defaultContent.neo4j.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOps.java b/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOps.java index b6e5ed2d31..bc15acba26 100644 --- a/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOps.java +++ b/common/keymaster/client-zookeeper/src/main/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOps.java @@ -30,6 +30,8 @@ import org.apache.syncope.common.keymaster.client.api.DomainWatcher; import org.apache.syncope.common.keymaster.client.api.KeymasterException; 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.slf4j.Logger; @@ -170,9 +172,19 @@ public void changeAdminPassword( public void adjustPoolSize(final String key, final int poolMaxActive, final int poolMinIdle) { try { Domain domain = read(key); + switch (domain) { + case JPADomain jpaDomain -> { + jpaDomain.setPoolMaxActive(poolMaxActive); + jpaDomain.setPoolMinIdle(poolMinIdle); + } + + case Neo4jDomain neo4jDomain -> + neo4jDomain.setMaxConnectionPoolSize(poolMaxActive); + + default -> { + } + } - domain.setPoolMaxActive(poolMaxActive); - domain.setPoolMinIdle(poolMinIdle); client.setData().forPath(buildDomainPath(key), MAPPER.writeValueAsBytes(domain)); } catch (KeymasterException e) { throw e; diff --git a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOpsITCase.java b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOpsITCase.java index 624ae1a01f..f7d9967a9c 100644 --- a/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOpsITCase.java +++ b/common/keymaster/client-zookeeper/src/test/java/org/apache/syncope/common/keymaster/client/zookeeper/ZookeeperDomainOpsITCase.java @@ -29,6 +29,7 @@ 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.keymaster.client.api.model.JPADomain; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.junit.jupiter.api.Test; @@ -45,19 +46,19 @@ public class ZookeeperDomainOpsITCase { public void crud() { String key = UUID.randomUUID().toString(); - domainOps.create(new Domain.Builder(key). + domainOps.create(new JPADomain.Builder(key). jdbcDriver("org.h2.Driver"). jdbcURL("jdbc:h2:mem:syncopetest;DB_CLOSE_DELAY=-1"). dbUsername("sa"). dbPassword(""). databasePlatform("org.apache.openjpa.jdbc.sql.H2Dictionary"). - transactionIsolation(Domain.TransactionIsolation.TRANSACTION_READ_UNCOMMITTED). + transactionIsolation(JPADomain.TransactionIsolation.TRANSACTION_READ_UNCOMMITTED). adminPassword("password"). adminCipherAlgorithm(CipherAlgorithm.BCRYPT). build()); - Domain domain = domainOps.read(key); - assertEquals(Domain.TransactionIsolation.TRANSACTION_READ_UNCOMMITTED, domain.getTransactionIsolation()); + JPADomain domain = (JPADomain) domainOps.read(key); + assertEquals(JPADomain.TransactionIsolation.TRANSACTION_READ_UNCOMMITTED, domain.getTransactionIsolation()); assertEquals("password", domain.getAdminPassword()); assertEquals(CipherAlgorithm.BCRYPT, domain.getAdminCipherAlgorithm()); assertEquals(10, domain.getPoolMaxActive()); @@ -68,7 +69,7 @@ public void crud() { assertEquals(domain, list.get(0)); try { - domainOps.create(new Domain.Builder(domain.getKey()).build()); + domainOps.create(new JPADomain.Builder(domain.getKey()).build()); fail(); } catch (KeymasterException e) { assertNotNull(e); @@ -76,13 +77,13 @@ public void crud() { domainOps.changeAdminPassword(key, "newpassword", CipherAlgorithm.SSHA512); - domain = domainOps.read(key); + domain = (JPADomain) domainOps.read(key); assertEquals("newpassword", domain.getAdminPassword()); assertEquals(CipherAlgorithm.SSHA512, domain.getAdminCipherAlgorithm()); domainOps.adjustPoolSize(key, 100, 23); - domain = domainOps.read(key); + domain = (JPADomain) domainOps.read(key); assertEquals(100, domain.getPoolMaxActive()); assertEquals(23, domain.getPoolMinIdle()); @@ -96,6 +97,6 @@ public void crud() { @Test public void createMaster() { assertThrows(KeymasterException.class, () -> domainOps.create( - new Domain.Builder(SyncopeConstants.MASTER_DOMAIN).build())); + new JPADomain.Builder(SyncopeConstants.MASTER_DOMAIN).build())); } } diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyDomainOps.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyDomainOps.java index 840c11edb8..f899baea17 100644 --- a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyDomainOps.java +++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyDomainOps.java @@ -21,14 +21,15 @@ import java.util.List; import org.apache.syncope.common.keymaster.client.api.DomainOps; 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.lib.types.CipherAlgorithm; import org.apache.syncope.core.persistence.api.DomainRegistry; public class DummyDomainOps implements DomainOps { - private final DomainRegistry domainRegistry; + private final DomainRegistry domainRegistry; - public DummyDomainOps(final DomainRegistry domainRegistry) { + public DummyDomainOps(final DomainRegistry domainRegistry) { this.domainRegistry = domainRegistry; } @@ -39,12 +40,12 @@ public List list() { @Override public Domain read(final String key) { - return new Domain.Builder(key).build(); + return new JPADomain.Builder(key).build(); } @Override public void create(final Domain domain) { - domainRegistry.register(domain); + domainRegistry.register((JPADomain) domain); } @Override diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/IdMLogicTestContext.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/IdMLogicTestContext.java index e79f3118d8..590650df69 100644 --- a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/IdMLogicTestContext.java +++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/IdMLogicTestContext.java @@ -23,6 +23,7 @@ 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.core.persistence.api.DomainRegistry; import org.apache.syncope.core.persistence.api.content.ContentLoader; import org.apache.syncope.core.persistence.jpa.MasterDomain; @@ -76,7 +77,7 @@ public ConfParamOps confParamOps() { } @Bean - public DomainOps domainOps(final DomainRegistry domainRegistry) { + public DomainOps domainOps(final DomainRegistry domainRegistry) { return new DummyDomainOps(domainRegistry); } diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/DummyDomainOps.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/DummyDomainOps.java index 840c11edb8..f899baea17 100644 --- a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/DummyDomainOps.java +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/DummyDomainOps.java @@ -21,14 +21,15 @@ import java.util.List; import org.apache.syncope.common.keymaster.client.api.DomainOps; 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.lib.types.CipherAlgorithm; import org.apache.syncope.core.persistence.api.DomainRegistry; public class DummyDomainOps implements DomainOps { - private final DomainRegistry domainRegistry; + private final DomainRegistry domainRegistry; - public DummyDomainOps(final DomainRegistry domainRegistry) { + public DummyDomainOps(final DomainRegistry domainRegistry) { this.domainRegistry = domainRegistry; } @@ -39,12 +40,12 @@ public List list() { @Override public Domain read(final String key) { - return new Domain.Builder(key).build(); + return new JPADomain.Builder(key).build(); } @Override public void create(final Domain domain) { - domainRegistry.register(domain); + domainRegistry.register((JPADomain) domain); } @Override diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/IdRepoLogicTestContext.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/IdRepoLogicTestContext.java index 29ec5a8bc2..f70508c929 100644 --- a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/IdRepoLogicTestContext.java +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/IdRepoLogicTestContext.java @@ -23,6 +23,7 @@ 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.core.persistence.api.DomainRegistry; import org.apache.syncope.core.persistence.api.content.ContentLoader; import org.apache.syncope.core.persistence.jpa.MasterDomain; @@ -76,7 +77,7 @@ public ConfParamOps confParamOps() { } @Bean - public DomainOps domainOps(final DomainRegistry domainRegistry) { + public DomainOps domainOps(final DomainRegistry domainRegistry) { return new DummyDomainOps(domainRegistry); } diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/SyncopeOpenApiCustomizer.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/SyncopeOpenApiCustomizer.java index 01bc0f2879..6d796ce8ee 100644 --- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/SyncopeOpenApiCustomizer.java +++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/SyncopeOpenApiCustomizer.java @@ -123,7 +123,7 @@ protected void customizeResponses(final Operation operation, final OperationReso operation.setResponses(responses); } - ApiResponse defaultResponse = responses.getDefault(); + ApiResponse defaultResponse = responses.get(ApiResponses.DEFAULT); if (defaultResponse != null) { responses.remove(ApiResponses.DEFAULT); responses.addApiResponse("200", defaultResponse); diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/DomainRegistry.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/DomainRegistry.java index 59189e8ba4..fb35314395 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/DomainRegistry.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/DomainRegistry.java @@ -22,10 +22,12 @@ /** * Allows to register domains, to make available at runtime. + * + * @param domain persistence type */ -public interface DomainRegistry { +public interface DomainRegistry { - void register(Domain req); + void register(D domain); void unregister(String domain); } diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/dao/ConfParamDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/keymaster/ConfParamDAO.java similarity index 81% rename from core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/dao/ConfParamDAO.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/keymaster/ConfParamDAO.java index 0b09e33172..14969446c0 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/dao/ConfParamDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/keymaster/ConfParamDAO.java @@ -16,9 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.api.dao; +package org.apache.syncope.core.persistence.api.dao.keymaster; -import org.apache.syncope.core.persistence.api.entity.ConfParam; +import org.apache.syncope.core.persistence.api.dao.DAO; +import org.apache.syncope.core.persistence.api.entity.keymaster.ConfParam; public interface ConfParamDAO extends DAO { } diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/dao/DomainDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/keymaster/DomainDAO.java similarity index 81% rename from core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/dao/DomainDAO.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/keymaster/DomainDAO.java index 18b07b455d..b9395a24c3 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/dao/DomainDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/keymaster/DomainDAO.java @@ -16,9 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.api.dao; +package org.apache.syncope.core.persistence.api.dao.keymaster; -import org.apache.syncope.core.persistence.api.entity.DomainEntity; +import org.apache.syncope.core.persistence.api.dao.DAO; +import org.apache.syncope.core.persistence.api.entity.keymaster.DomainEntity; public interface DomainDAO extends DAO { } diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/dao/NetworkServiceDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/keymaster/NetworkServiceDAO.java similarity index 80% rename from core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/dao/NetworkServiceDAO.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/keymaster/NetworkServiceDAO.java index d828d728a9..0334abfa14 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/dao/NetworkServiceDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/keymaster/NetworkServiceDAO.java @@ -16,15 +16,16 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.api.dao; +package org.apache.syncope.core.persistence.api.dao.keymaster; import java.util.List; import org.apache.syncope.common.keymaster.client.api.model.NetworkService; -import org.apache.syncope.core.persistence.api.entity.NetworkServiceEntity; +import org.apache.syncope.core.persistence.api.dao.DAO; +import org.apache.syncope.core.persistence.api.entity.keymaster.NetworkServiceEntity; public interface NetworkServiceDAO extends DAO { List findAll(NetworkService.Type serviceType); - int deleteAll(NetworkService service); + void deleteAll(NetworkService service); } diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/ConfParam.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/ConfParam.java similarity index 87% rename from core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/ConfParam.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/ConfParam.java index 791d40b5b7..be15aa063e 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/ConfParam.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/ConfParam.java @@ -16,9 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.api.entity; +package org.apache.syncope.core.persistence.api.entity.keymaster; import com.fasterxml.jackson.databind.JsonNode; +import org.apache.syncope.core.persistence.api.entity.ProvidedKeyEntity; public interface ConfParam extends ProvidedKeyEntity { diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/DomainEntity.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/DomainEntity.java similarity index 87% rename from core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/DomainEntity.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/DomainEntity.java index e49835c0d7..cb1cb9b6b7 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/DomainEntity.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/DomainEntity.java @@ -16,9 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.api.entity; +package org.apache.syncope.core.persistence.api.entity.keymaster; import org.apache.syncope.common.keymaster.client.api.model.Domain; +import org.apache.syncope.core.persistence.api.entity.ProvidedKeyEntity; public interface DomainEntity extends ProvidedKeyEntity { diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/NetworkServiceEntity.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/NetworkServiceEntity.java similarity index 89% rename from core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/NetworkServiceEntity.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/NetworkServiceEntity.java index d3dbad729d..ff9959697b 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/NetworkServiceEntity.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/keymaster/NetworkServiceEntity.java @@ -16,9 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.api.entity; +package org.apache.syncope.core.persistence.api.entity.keymaster; import org.apache.syncope.common.keymaster.client.api.model.NetworkService; +import org.apache.syncope.core.persistence.api.entity.Entity; public interface NetworkServiceEntity extends Entity { diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/RuntimeDomainLoader.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/RuntimeDomainLoader.java index 70854f703f..1d725f2938 100644 --- a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/RuntimeDomainLoader.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/RuntimeDomainLoader.java @@ -31,17 +31,17 @@ import org.springframework.aop.support.AopUtils; import org.springframework.context.ConfigurableApplicationContext; -public class RuntimeDomainLoader implements DomainWatcher { +public class RuntimeDomainLoader implements DomainWatcher { protected static final Logger LOG = LoggerFactory.getLogger(RuntimeDomainLoader.class); protected final DomainHolder domainHolder; - protected final DomainRegistry domainRegistry; + protected final DomainRegistry domainRegistry; public RuntimeDomainLoader( final DomainHolder domainHolder, - final DomainRegistry domainRegistry, + final DomainRegistry domainRegistry, final ConfigurableApplicationContext ctx) { this.domainHolder = domainHolder; @@ -57,6 +57,7 @@ protected void onAdd(final Domain domain) { // nothing to do } + @SuppressWarnings("unchecked") @Override public void added(final Domain domain) { if (domainHolder.getDomains().containsKey(domain.getKey())) { @@ -64,7 +65,7 @@ public void added(final Domain domain) { } else { LOG.info("Domain {} registration", domain.getKey()); - domainRegistry.register(domain); + domainRegistry.register((D) domain); onAdd(domain); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/DomainProperties.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/DomainProperties.java index da0ea31e50..349992913d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/DomainProperties.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/DomainProperties.java @@ -18,7 +18,7 @@ */ package org.apache.syncope.core.persistence.jpa; -import org.apache.syncope.common.keymaster.client.api.model.Domain; +import org.apache.syncope.common.keymaster.client.api.model.JPADomain; import org.apache.syncope.core.persistence.common.AbstractDomainProperties; public class DomainProperties extends AbstractDomainProperties { @@ -33,7 +33,8 @@ public class DomainProperties extends AbstractDomainProperties { private String dbPassword; - private Domain.TransactionIsolation transactionIsolation = Domain.TransactionIsolation.TRANSACTION_READ_COMMITTED; + private JPADomain.TransactionIsolation transactionIsolation = + JPADomain.TransactionIsolation.TRANSACTION_READ_COMMITTED; private int poolMaxActive = 10; @@ -85,11 +86,11 @@ public void setDbPassword(final String dbPassword) { this.dbPassword = dbPassword; } - public Domain.TransactionIsolation getTransactionIsolation() { + public JPADomain.TransactionIsolation getTransactionIsolation() { return transactionIsolation; } - public void setTransactionIsolation(final Domain.TransactionIsolation transactionIsolation) { + public void setTransactionIsolation(final JPADomain.TransactionIsolation transactionIsolation) { this.transactionIsolation = transactionIsolation; } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java index 78c971f8cd..8df150a39c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java @@ -23,7 +23,7 @@ import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; import javax.sql.DataSource; -import org.apache.syncope.common.keymaster.client.api.model.Domain; +import org.apache.syncope.common.keymaster.client.api.model.JPADomain; import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.DomainRegistry; import org.apache.syncope.core.persistence.jpa.spring.DomainRoutingEntityManagerFactory; @@ -35,7 +35,7 @@ import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import org.springframework.jndi.JndiObjectFactoryBean; -public class JPADomainRegistry implements DomainRegistry { +public class JPADomainRegistry implements DomainRegistry { protected final ConfigurableApplicationContext ctx; @@ -64,7 +64,7 @@ protected DomainHolder domainHolder() { } @Override - public void register(final Domain domain) { + public void register(final JPADomain domain) { HikariConfig hikariConfig = new HikariConfig(); hikariConfig.setDriverClassName(domain.getJdbcDriver()); hikariConfig.setJdbcUrl(domain.getJdbcURL()); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPARuntimeDomainLoader.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPARuntimeDomainLoader.java index 8c729676f8..5ad94dc8c5 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPARuntimeDomainLoader.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPARuntimeDomainLoader.java @@ -19,6 +19,7 @@ package org.apache.syncope.core.persistence.jpa; import org.apache.syncope.common.keymaster.client.api.model.Domain; +import org.apache.syncope.common.keymaster.client.api.model.JPADomain; import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.DomainRegistry; import org.apache.syncope.core.persistence.common.RuntimeDomainLoader; @@ -26,13 +27,13 @@ import org.apache.syncope.core.spring.security.AuthContextUtils; import org.springframework.context.ConfigurableApplicationContext; -public class JPARuntimeDomainLoader extends RuntimeDomainLoader { +public class JPARuntimeDomainLoader extends RuntimeDomainLoader { protected final DomainRoutingEntityManagerFactory entityManagerFactory; public JPARuntimeDomainLoader( final DomainHolder domainHolder, - final DomainRegistry domainRegistry, + final DomainRegistry domainRegistry, final DomainRoutingEntityManagerFactory entityManagerFactory, final ConfigurableApplicationContext ctx) { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java index 03df041d4f..1d52678058 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java @@ -25,6 +25,7 @@ import java.util.Map; import javax.sql.DataSource; 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.SyncopeConstants; import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.DomainRegistry; @@ -78,6 +79,9 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO; import org.apache.syncope.core.persistence.api.dao.WAConfigDAO; +import org.apache.syncope.core.persistence.api.dao.keymaster.ConfParamDAO; +import org.apache.syncope.core.persistence.api.dao.keymaster.DomainDAO; +import org.apache.syncope.core.persistence.api.dao.keymaster.NetworkServiceDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory; @@ -124,6 +128,7 @@ import org.apache.syncope.core.persistence.jpa.dao.repo.CASSPClientAppRepo; import org.apache.syncope.core.persistence.jpa.dao.repo.CASSPClientAppRepoExt; import org.apache.syncope.core.persistence.jpa.dao.repo.CASSPClientAppRepoExtImpl; +import org.apache.syncope.core.persistence.jpa.dao.repo.ConfParamRepo; import org.apache.syncope.core.persistence.jpa.dao.repo.ConnInstanceRepo; import org.apache.syncope.core.persistence.jpa.dao.repo.ConnInstanceRepoExt; import org.apache.syncope.core.persistence.jpa.dao.repo.ConnInstanceRepoExtImpl; @@ -131,6 +136,7 @@ import org.apache.syncope.core.persistence.jpa.dao.repo.DerSchemaRepo; import org.apache.syncope.core.persistence.jpa.dao.repo.DerSchemaRepoExt; import org.apache.syncope.core.persistence.jpa.dao.repo.DerSchemaRepoExtImpl; +import org.apache.syncope.core.persistence.jpa.dao.repo.DomainRepo; import org.apache.syncope.core.persistence.jpa.dao.repo.DynRealmRepo; import org.apache.syncope.core.persistence.jpa.dao.repo.DynRealmRepoExt; import org.apache.syncope.core.persistence.jpa.dao.repo.DynRealmRepoExtImpl; @@ -147,6 +153,9 @@ import org.apache.syncope.core.persistence.jpa.dao.repo.ImplementationRepoExt; import org.apache.syncope.core.persistence.jpa.dao.repo.ImplementationRepoExtImpl; import org.apache.syncope.core.persistence.jpa.dao.repo.MailTemplateRepo; +import org.apache.syncope.core.persistence.jpa.dao.repo.NetworkServiceRepo; +import org.apache.syncope.core.persistence.jpa.dao.repo.NetworkServiceRepoExt; +import org.apache.syncope.core.persistence.jpa.dao.repo.NetworkServiceRepoExtImpl; import org.apache.syncope.core.persistence.jpa.dao.repo.NotificationRepo; import org.apache.syncope.core.persistence.jpa.dao.repo.NotificationRepoExt; import org.apache.syncope.core.persistence.jpa.dao.repo.NotificationRepoExtImpl; @@ -312,15 +321,15 @@ public XMLContentExporter xmlContentExporter( @ConditionalOnMissingBean @Bean - public DomainRegistry domainRegistry(final ConfigurableApplicationContext ctx) { + public DomainRegistry domainRegistry(final ConfigurableApplicationContext ctx) { return new JPADomainRegistry(ctx); } @ConditionalOnMissingBean @Bean - public RuntimeDomainLoader runtimeDomainLoader( + public RuntimeDomainLoader runtimeDomainLoader( final DomainHolder domainHolder, - final DomainRegistry domainRegistry, + final DomainRegistry domainRegistry, final DomainRoutingEntityManagerFactory entityManagerFactory, final ConfigurableApplicationContext ctx) { @@ -334,7 +343,7 @@ public StartupDomainLoader startupDomainLoader( final ResourceLoader resourceLoader, final DomainOps domainOps, final DomainHolder domainHolder, - final DomainRegistry domainRegistry) { + final DomainRegistry domainRegistry) { return new StartupDomainLoader(domainOps, domainHolder, persistenceProperties, resourceLoader, domainRegistry); } @@ -1107,4 +1116,28 @@ public VirSchemaDAO virSchemaDAO( public WAConfigDAO waConfigDAO(final JpaRepositoryFactory jpaRepositoryFactory) { return jpaRepositoryFactory.getRepository(WAConfigRepo.class); } + + @Bean + public ConfParamDAO confParamDAO(final JpaRepositoryFactory jpaRepositoryFactory) { + return jpaRepositoryFactory.getRepository(ConfParamRepo.class); + } + + @Bean + public DomainDAO domainDAO(final JpaRepositoryFactory jpaRepositoryFactory) { + return jpaRepositoryFactory.getRepository(DomainRepo.class); + } + + @ConditionalOnMissingBean + @Bean + public NetworkServiceRepoExt networkServiceRepoExt(final EntityManager entityManager) { + return new NetworkServiceRepoExtImpl(entityManager); + } + + @Bean + public NetworkServiceDAO networkServiceDAO( + final JpaRepositoryFactory jpaRepositoryFactory, + final NetworkServiceRepoExt networkServiceRepoExt) { + + return jpaRepositoryFactory.getRepository(NetworkServiceRepo.class, networkServiceRepoExt); + } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/StartupDomainLoader.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/StartupDomainLoader.java index 8ceae5ec76..ba8d51ab35 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/StartupDomainLoader.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/StartupDomainLoader.java @@ -20,11 +20,11 @@ import java.io.IOException; import java.util.Map; -import java.util.function.Function; import java.util.stream.Collectors; import org.apache.cxf.helpers.IOUtils; import org.apache.syncope.common.keymaster.client.api.DomainOps; 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.lib.SyncopeConstants; import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.DomainRegistry; @@ -46,14 +46,14 @@ public class StartupDomainLoader implements SyncopeCoreLoader { protected final ResourceLoader resourceLoader; - protected final DomainRegistry domainRegistry; + protected final DomainRegistry domainRegistry; public StartupDomainLoader( final DomainOps domainOps, final DomainHolder domainHolder, final PersistenceProperties persistenceProperties, final ResourceLoader resourceLoader, - final DomainRegistry domainRegistry) { + final DomainRegistry domainRegistry) { this.domainOps = domainOps; this.domainHolder = domainHolder; @@ -69,8 +69,8 @@ public int getOrder() { @Override public void load() { - Map keymasterDomains = domainOps.list().stream(). - collect(Collectors.toMap(Domain::getKey, Function.identity())); + Map keymasterDomains = domainOps.list().stream(). + collect(Collectors.toMap(Domain::getKey, domain -> (JPADomain) domain)); persistenceProperties.getDomain().stream(). filter(d -> !SyncopeConstants.MASTER_DOMAIN.equals(d.getKey()) @@ -83,7 +83,7 @@ public void load() { LOG.info("Domain {} successfully inited", domainProps.getKey()); } else { - Domain.Builder builder = new Domain.Builder(domainProps.getKey()). + JPADomain.Builder builder = new JPADomain.Builder(domainProps.getKey()). adminPassword(domainProps.getAdminPassword()). adminCipherAlgorithm(domainProps.getAdminCipherAlgorithm()). jdbcDriver(domainProps.getJdbcDriver()). diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConfParamRepo.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConfParamRepo.java similarity index 87% rename from core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConfParamRepo.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConfParamRepo.java index 0340e2e473..e89599d30a 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConfParamRepo.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ConfParamRepo.java @@ -18,8 +18,8 @@ */ package org.apache.syncope.core.persistence.jpa.dao.repo; -import org.apache.syncope.core.persistence.api.dao.ConfParamDAO; -import org.apache.syncope.core.persistence.jpa.entity.JPAConfParam; +import org.apache.syncope.core.persistence.api.dao.keymaster.ConfParamDAO; +import org.apache.syncope.core.persistence.jpa.entity.keymaster.JPAConfParam; import org.springframework.data.repository.ListCrudRepository; public interface ConfParamRepo extends ListCrudRepository, ConfParamDAO { diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DomainRepo.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DomainRepo.java similarity index 87% rename from core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DomainRepo.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DomainRepo.java index 0f9675a29b..a4c6f06b97 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DomainRepo.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DomainRepo.java @@ -18,8 +18,8 @@ */ package org.apache.syncope.core.persistence.jpa.dao.repo; -import org.apache.syncope.core.persistence.api.dao.DomainDAO; -import org.apache.syncope.core.persistence.jpa.entity.JPADomain; +import org.apache.syncope.core.persistence.api.dao.keymaster.DomainDAO; +import org.apache.syncope.core.persistence.jpa.entity.keymaster.JPADomain; import org.springframework.data.repository.ListCrudRepository; public interface DomainRepo extends ListCrudRepository, DomainDAO { diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepo.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepo.java similarity index 86% rename from core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepo.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepo.java index 88d258c34d..1843cdb3a3 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepo.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepo.java @@ -18,8 +18,8 @@ */ package org.apache.syncope.core.persistence.jpa.dao.repo; -import org.apache.syncope.core.persistence.api.dao.NetworkServiceDAO; -import org.apache.syncope.core.persistence.jpa.entity.JPANetworkService; +import org.apache.syncope.core.persistence.api.dao.keymaster.NetworkServiceDAO; +import org.apache.syncope.core.persistence.jpa.entity.keymaster.JPANetworkService; import org.springframework.data.repository.ListCrudRepository; public interface NetworkServiceRepo diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExt.java similarity index 89% rename from core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExt.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExt.java index 54ab87ad1d..25bcf4ebf7 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExt.java @@ -20,11 +20,11 @@ import java.util.List; import org.apache.syncope.common.keymaster.client.api.model.NetworkService; -import org.apache.syncope.core.persistence.api.entity.NetworkServiceEntity; +import org.apache.syncope.core.persistence.api.entity.keymaster.NetworkServiceEntity; public interface NetworkServiceRepoExt { List findAll(NetworkService.Type serviceType); - int deleteAll(NetworkService service); + void deleteAll(NetworkService service); } diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExtImpl.java similarity index 89% rename from core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExtImpl.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExtImpl.java index dd36cb76fe..8f4b280022 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/NetworkServiceRepoExtImpl.java @@ -23,8 +23,8 @@ import jakarta.persistence.TypedQuery; import java.util.List; import org.apache.syncope.common.keymaster.client.api.model.NetworkService; -import org.apache.syncope.core.persistence.api.entity.NetworkServiceEntity; -import org.apache.syncope.core.persistence.jpa.entity.JPANetworkService; +import org.apache.syncope.core.persistence.api.entity.keymaster.NetworkServiceEntity; +import org.apache.syncope.core.persistence.jpa.entity.keymaster.JPANetworkService; import org.springframework.transaction.annotation.Transactional; public class NetworkServiceRepoExtImpl implements NetworkServiceRepoExt { @@ -46,13 +46,13 @@ public List findAll(final NetworkService.Type serviceType) } @Override - public int deleteAll(final NetworkService service) { + public void deleteAll(final NetworkService service) { Query query = entityManager.createQuery( "DELETE FROM " + JPANetworkService.class.getSimpleName() + " e WHERE e.type=:serviceType AND e.address=:address"); query.setParameter("serviceType", service.getType()); query.setParameter("address", service.getAddress()); - return query.executeUpdate(); + query.executeUpdate(); } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java index 279dabde7d..d84fd8e186 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java @@ -72,6 +72,9 @@ import org.apache.syncope.core.persistence.api.entity.group.GPlainAttrValue; import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.group.TypeExtension; +import org.apache.syncope.core.persistence.api.entity.keymaster.ConfParam; +import org.apache.syncope.core.persistence.api.entity.keymaster.DomainEntity; +import org.apache.syncope.core.persistence.api.entity.keymaster.NetworkServiceEntity; import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy; import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy; @@ -124,6 +127,9 @@ import org.apache.syncope.core.persistence.jpa.entity.group.JPAGPlainAttrValue; import org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup; import org.apache.syncope.core.persistence.jpa.entity.group.JPATypeExtension; +import org.apache.syncope.core.persistence.jpa.entity.keymaster.JPAConfParam; +import org.apache.syncope.core.persistence.jpa.entity.keymaster.JPADomain; +import org.apache.syncope.core.persistence.jpa.entity.keymaster.JPANetworkService; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccessPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAttrReleasePolicy; @@ -325,6 +331,12 @@ public E newEntity(final Class reference) { result = (E) new JPAOIDCJWKS(); } else if (reference.equals(WAConfigEntry.class)) { result = (E) new JPAWAConfigEntry(); + } else if (reference.equals(ConfParam.class)) { + result = (E) new JPAConfParam(); + } else if (reference.equals(DomainEntity.class)) { + result = (E) new JPADomain(); + } else if (reference.equals(NetworkServiceEntity.class)) { + result = (E) new JPANetworkService(); } else { throw new IllegalArgumentException("Could not find a JPA implementation of " + reference.getName()); } diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConfParam.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster/JPAConfParam.java similarity index 90% rename from core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConfParam.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster/JPAConfParam.java index 05b27a1a32..cb768766d2 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConfParam.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster/JPAConfParam.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.entity; +package org.apache.syncope.core.persistence.jpa.entity.keymaster; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -25,7 +25,8 @@ import jakarta.persistence.Lob; import jakarta.persistence.Table; import java.io.IOException; -import org.apache.syncope.core.persistence.api.entity.ConfParam; +import org.apache.syncope.core.persistence.api.entity.keymaster.ConfParam; +import org.apache.syncope.core.persistence.jpa.entity.AbstractProvidedKeyEntity; @Entity @Table(name = JPAConfParam.TABLE) diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADomain.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster/JPADomain.java similarity index 88% rename from core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADomain.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster/JPADomain.java index 17217e8717..f908790e59 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADomain.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster/JPADomain.java @@ -16,14 +16,15 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.entity; +package org.apache.syncope.core.persistence.jpa.entity.keymaster; import jakarta.persistence.Entity; import jakarta.persistence.Lob; import jakarta.persistence.Table; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.keymaster.client.api.model.Domain; -import org.apache.syncope.core.persistence.api.entity.DomainEntity; +import org.apache.syncope.core.persistence.api.entity.keymaster.DomainEntity; +import org.apache.syncope.core.persistence.jpa.entity.AbstractProvidedKeyEntity; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @Entity diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANetworkService.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster/JPANetworkService.java similarity index 89% rename from core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANetworkService.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster/JPANetworkService.java index aae6d533fb..a1ddf5ae03 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANetworkService.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/keymaster/JPANetworkService.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.jpa.entity; +package org.apache.syncope.core.persistence.jpa.entity.keymaster; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; @@ -25,7 +25,8 @@ import jakarta.persistence.UniqueConstraint; import jakarta.validation.constraints.NotNull; import org.apache.syncope.common.keymaster.client.api.model.NetworkService; -import org.apache.syncope.core.persistence.api.entity.NetworkServiceEntity; +import org.apache.syncope.core.persistence.api.entity.keymaster.NetworkServiceEntity; +import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity; @Entity @Table(name = JPANetworkService.TABLE, uniqueConstraints = diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java index 51baabcb7e..dceb3ce314 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java @@ -38,7 +38,7 @@ import org.apache.openjpa.jdbc.meta.MappingTool; import org.apache.openjpa.lib.conf.Configurations; import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI; -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.lib.SyncopeConstants; import org.apache.syncope.core.persistence.jpa.PersistenceProperties; import org.apache.syncope.core.persistence.jpa.openjpa.ConnectorManagerRemoteCommitListener; @@ -89,7 +89,7 @@ public void master( } public void domain( - final Domain domain, + final JPADomain domain, final DataSource dataSource, final String metadataFactory) { diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyDomainOps.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyDomainOps.java index 567e306e5e..8491534651 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyDomainOps.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyDomainOps.java @@ -21,14 +21,15 @@ import java.util.List; import org.apache.syncope.common.keymaster.client.api.DomainOps; 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.lib.types.CipherAlgorithm; import org.apache.syncope.core.persistence.api.DomainRegistry; public class DummyDomainOps implements DomainOps { - private final DomainRegistry domainRegistry; + private final DomainRegistry domainRegistry; - public DummyDomainOps(final DomainRegistry domainRegistry) { + public DummyDomainOps(final DomainRegistry domainRegistry) { this.domainRegistry = domainRegistry; } @@ -39,12 +40,12 @@ public List list() { @Override public Domain read(final String key) { - return new Domain.Builder(key).build(); + return new JPADomain.Builder(key).build(); } @Override public void create(final Domain domain) { - domainRegistry.register(domain); + domainRegistry.register((JPADomain) domain); } @Override diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java index 23f7a972a9..d5ee6024f4 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java @@ -24,6 +24,7 @@ import javax.sql.DataSource; 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.SyncopeConstants; import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.DomainRegistry; @@ -115,7 +116,7 @@ public ConfParamOps confParamOps() { } @Bean - public DomainOps domainOps(final DomainRegistry domainRegistry) { + public DomainOps domainOps(final DomainRegistry domainRegistry) { return new DummyDomainOps(domainRegistry); } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainRegistry.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainRegistry.java index 2536193771..ca8a4974c3 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainRegistry.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/Neo4jDomainRegistry.java @@ -19,8 +19,7 @@ package org.apache.syncope.core.persistence.neo4j; import java.io.ByteArrayInputStream; -import java.net.URI; -import org.apache.syncope.common.keymaster.client.api.model.Domain; +import org.apache.syncope.common.keymaster.client.api.model.Neo4jDomain; import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.DomainRegistry; import org.apache.syncope.core.persistence.neo4j.spring.DomainRoutingNeo4jClient; @@ -37,7 +36,7 @@ import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager; import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager; -public class Neo4jDomainRegistry implements DomainRegistry { +public class Neo4jDomainRegistry implements DomainRegistry { protected final ConfigurableApplicationContext ctx; @@ -66,13 +65,13 @@ protected DomainHolder domainHolder() { } @Override - public void register(final Domain domain) { + public void register(final Neo4jDomain domain) { // domainDriver Driver driver = GraphDatabase.driver( - URI.create(domain.getJdbcURL()), - AuthTokens.basic(domain.getDbUsername(), domain.getDbPassword()), + domain.getUri(), + AuthTokens.basic(domain.getUsername(), domain.getPassword()), Config.builder(). - withMaxConnectionPoolSize(domain.getPoolMaxActive()). + withMaxConnectionPoolSize(domain.getMaxConnectionPoolSize()). withDriverMetrics(). withLogging(Logging.slf4j()).build()); registerSingleton(domain.getKey().toLowerCase() + "Driver", driver); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java index a7113d683e..59f4917dee 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java @@ -21,6 +21,7 @@ import jakarta.validation.Validator; import java.util.Map; import org.apache.syncope.common.keymaster.client.api.DomainOps; +import org.apache.syncope.common.keymaster.client.api.model.Neo4jDomain; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.DomainRegistry; @@ -73,6 +74,9 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO; import org.apache.syncope.core.persistence.api.dao.WAConfigDAO; +import org.apache.syncope.core.persistence.api.dao.keymaster.ConfParamDAO; +import org.apache.syncope.core.persistence.api.dao.keymaster.DomainDAO; +import org.apache.syncope.core.persistence.api.dao.keymaster.NetworkServiceDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory; @@ -118,6 +122,7 @@ import org.apache.syncope.core.persistence.neo4j.dao.repo.CASSPClientAppRepo; import org.apache.syncope.core.persistence.neo4j.dao.repo.CASSPClientAppRepoExt; import org.apache.syncope.core.persistence.neo4j.dao.repo.CASSPClientAppRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.ConfParamRepo; import org.apache.syncope.core.persistence.neo4j.dao.repo.ConnInstanceRepo; import org.apache.syncope.core.persistence.neo4j.dao.repo.ConnInstanceRepoExt; import org.apache.syncope.core.persistence.neo4j.dao.repo.ConnInstanceRepoExtImpl; @@ -127,6 +132,7 @@ import org.apache.syncope.core.persistence.neo4j.dao.repo.DerSchemaRepo; import org.apache.syncope.core.persistence.neo4j.dao.repo.DerSchemaRepoExt; import org.apache.syncope.core.persistence.neo4j.dao.repo.DerSchemaRepoExtImpl; +import org.apache.syncope.core.persistence.neo4j.dao.repo.DomainRepo; import org.apache.syncope.core.persistence.neo4j.dao.repo.DynRealmRepo; import org.apache.syncope.core.persistence.neo4j.dao.repo.DynRealmRepoExt; import org.apache.syncope.core.persistence.neo4j.dao.repo.DynRealmRepoExtImpl; @@ -143,6 +149,9 @@ import org.apache.syncope.core.persistence.neo4j.dao.repo.ImplementationRepoExt; import org.apache.syncope.core.persistence.neo4j.dao.repo.ImplementationRepoExtImpl; import org.apache.syncope.core.persistence.neo4j.dao.repo.MailTemplateRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.NetworkServiceRepo; +import org.apache.syncope.core.persistence.neo4j.dao.repo.NetworkServiceRepoExt; +import org.apache.syncope.core.persistence.neo4j.dao.repo.NetworkServiceRepoExtImpl; import org.apache.syncope.core.persistence.neo4j.dao.repo.NotificationRepo; import org.apache.syncope.core.persistence.neo4j.dao.repo.NotificationRepoExt; import org.apache.syncope.core.persistence.neo4j.dao.repo.NotificationRepoExtImpl; @@ -355,18 +364,18 @@ public XMLContentExporter xmlContentExporter( @ConditionalOnMissingBean @Bean - public DomainRegistry domainRegistry(final ConfigurableApplicationContext ctx) { + public DomainRegistry domainRegistry(final ConfigurableApplicationContext ctx) { return new Neo4jDomainRegistry(ctx); } @ConditionalOnMissingBean @Bean - public RuntimeDomainLoader runtimeDomainLoader( + public RuntimeDomainLoader runtimeDomainLoader( final DomainHolder domainHolder, - final DomainRegistry domainRegistry, + final DomainRegistry domainRegistry, final ConfigurableApplicationContext ctx) { - return new RuntimeDomainLoader(domainHolder, domainRegistry, ctx); + return new RuntimeDomainLoader<>(domainHolder, domainRegistry, ctx); } @ConditionalOnMissingBean @@ -376,7 +385,7 @@ public StartupDomainLoader startupDomainLoader( final ResourceLoader resourceLoader, final DomainOps domainOps, final DomainHolder domainHolder, - final DomainRegistry domainRegistry) { + final DomainRegistry domainRegistry) { return new StartupDomainLoader(domainOps, domainHolder, persistenceProperties, resourceLoader, domainRegistry); } @@ -1251,4 +1260,28 @@ public VirSchemaDAO virSchemaDAO( public WAConfigDAO waConfigDAO(final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory) { return neo4jRepositoryFactory.getRepository(WAConfigRepo.class); } + + @Bean + public ConfParamDAO confParamDAO(final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory) { + return neo4jRepositoryFactory.getRepository(ConfParamRepo.class); + } + + @Bean + public DomainDAO domainDAO(final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory) { + return neo4jRepositoryFactory.getRepository(DomainRepo.class); + } + + @ConditionalOnMissingBean + @Bean + public NetworkServiceRepoExt networkServiceRepoExt(final Neo4jTemplate neo4jTemplate) { + return new NetworkServiceRepoExtImpl(neo4jTemplate); + } + + @Bean + public NetworkServiceDAO networkServiceDAO( + final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory, + final NetworkServiceRepoExt networkServiceRepoExt) { + + return neo4jRepositoryFactory.getRepository(NetworkServiceRepo.class, networkServiceRepoExt); + } } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/StartupDomainLoader.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/StartupDomainLoader.java index 6cdb749906..3aea334716 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/StartupDomainLoader.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/StartupDomainLoader.java @@ -20,11 +20,11 @@ import java.io.IOException; import java.util.Map; -import java.util.function.Function; import java.util.stream.Collectors; import org.apache.cxf.helpers.IOUtils; import org.apache.syncope.common.keymaster.client.api.DomainOps; import org.apache.syncope.common.keymaster.client.api.model.Domain; +import org.apache.syncope.common.keymaster.client.api.model.Neo4jDomain; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.DomainRegistry; @@ -46,14 +46,14 @@ public class StartupDomainLoader implements SyncopeCoreLoader { protected final ResourceLoader resourceLoader; - protected final DomainRegistry domainRegistry; + protected final DomainRegistry domainRegistry; public StartupDomainLoader( final DomainOps domainOps, final DomainHolder domainHolder, final PersistenceProperties persistenceProperties, final ResourceLoader resourceLoader, - final DomainRegistry domainRegistry) { + final DomainRegistry domainRegistry) { this.domainOps = domainOps; this.domainHolder = domainHolder; @@ -69,8 +69,8 @@ public int getOrder() { @Override public void load() { - Map keymasterDomains = domainOps.list().stream(). - collect(Collectors.toMap(Domain::getKey, Function.identity())); + Map keymasterDomains = domainOps.list().stream(). + collect(Collectors.toMap(Domain::getKey, domain -> (Neo4jDomain) domain)); persistenceProperties.getDomain().stream(). filter(d -> !SyncopeConstants.MASTER_DOMAIN.equals(d.getKey()) @@ -83,13 +83,13 @@ public void load() { LOG.info("Domain {} successfully inited", domainProps.getKey()); } else { - Domain.Builder builder = new Domain.Builder(domainProps.getKey()). + Neo4jDomain.Builder builder = new Neo4jDomain.Builder(domainProps.getKey()). adminPassword(domainProps.getAdminPassword()). adminCipherAlgorithm(domainProps.getAdminCipherAlgorithm()). - jdbcURL(domainProps.getUri().toASCIIString()). - dbUsername(domainProps.getUsername()). - dbPassword(domainProps.getPassword()). - poolMaxActive(domainProps.getMaxConnectionPoolSize()); + uri(domainProps.getUri()). + username(domainProps.getUsername()). + password(domainProps.getPassword()). + maxConnectionPoolSize(domainProps.getMaxConnectionPoolSize()); try { builder.content(IOUtils.toString( diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConfParamRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConfParamRepo.java new file mode 100644 index 0000000000..e52175d704 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ConfParamRepo.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.keymaster.ConfParamDAO; +import org.apache.syncope.core.persistence.neo4j.entity.keymaster.Neo4jConfParam; +import org.springframework.data.repository.ListCrudRepository; + +public interface ConfParamRepo extends ListCrudRepository, ConfParamDAO { +} diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/SelfKeymasterEntityFactory.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DomainRepo.java similarity index 69% rename from core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/SelfKeymasterEntityFactory.java rename to core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DomainRepo.java index 7725093b1c..1286e1ac2c 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/api/entity/SelfKeymasterEntityFactory.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DomainRepo.java @@ -16,13 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.syncope.core.persistence.api.entity; +package org.apache.syncope.core.persistence.neo4j.dao.repo; -public interface SelfKeymasterEntityFactory { +import org.apache.syncope.core.persistence.api.dao.keymaster.DomainDAO; +import org.apache.syncope.core.persistence.neo4j.entity.keymaster.Neo4jDomain; +import org.springframework.data.repository.ListCrudRepository; - ConfParam newConfParam(); - - NetworkServiceEntity newNetworkService(); - - DomainEntity newDomainEntity(); +public interface DomainRepo extends ListCrudRepository, DomainDAO { } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepo.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepo.java new file mode 100644 index 0000000000..5ae3f258ff --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepo.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.neo4j.dao.repo; + +import org.apache.syncope.core.persistence.api.dao.keymaster.NetworkServiceDAO; +import org.apache.syncope.core.persistence.neo4j.entity.keymaster.Neo4jNetworkService; +import org.springframework.data.repository.ListCrudRepository; + +public interface NetworkServiceRepo + extends ListCrudRepository, NetworkServiceRepoExt, NetworkServiceDAO { + +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExt.java new file mode 100644 index 0000000000..331a258062 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExt.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.core.persistence.neo4j.dao.repo; + +import java.util.List; +import org.apache.syncope.common.keymaster.client.api.model.NetworkService; +import org.apache.syncope.core.persistence.api.entity.keymaster.NetworkServiceEntity; +import org.apache.syncope.core.persistence.neo4j.entity.keymaster.Neo4jNetworkService; + +public interface NetworkServiceRepoExt { + + List findAll(NetworkService.Type serviceType); + + S save(S service); + + void deleteAll(NetworkService service); +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExtImpl.java new file mode 100644 index 0000000000..1737cda857 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/NetworkServiceRepoExtImpl.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.persistence.neo4j.dao.repo; + +import java.util.List; +import java.util.Map; +import org.apache.syncope.common.keymaster.client.api.model.NetworkService; +import org.apache.syncope.core.persistence.api.dao.DuplicateException; +import org.apache.syncope.core.persistence.api.entity.keymaster.NetworkServiceEntity; +import org.apache.syncope.core.persistence.neo4j.entity.keymaster.Neo4jNetworkService; +import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.transaction.annotation.Transactional; + +public class NetworkServiceRepoExtImpl implements NetworkServiceRepoExt { + + protected final Neo4jTemplate neo4jTemplate; + + protected final NodeValidator nodeValidator; + + public NetworkServiceRepoExtImpl(final Neo4jTemplate neo4jTemplate, final NodeValidator nodeValidator) { + this.neo4jTemplate = neo4jTemplate; + this.nodeValidator = nodeValidator; + } + + @Transactional(readOnly = true) + @Override + public List findAll(final NetworkService.Type serviceType) { + return neo4jTemplate.findAll( + "MATCH (n:" + Neo4jNetworkService.NODE + ") WHERE n.type = $serviceType", + Map.of("serviceType", serviceType), + Neo4jNetworkService.class).stream().map(NetworkServiceEntity.class::cast).toList(); + } + + @Override + public S save(final S service) { + if (findAll(service.getType()).stream().anyMatch(s -> s.getAddress().equals(service.getAddress()))) { + throw new DuplicateException( + "NetworkService with type " + service.getType() + "and address " + service.getAddress()); + } + + return neo4jTemplate.save(nodeValidator.validate(service)); + } + + @Override + public void deleteAll(final NetworkService service) { + findAll(service.getType()).stream(). + filter(s -> s.getAddress().equals(service.getAddress())). + forEach(s -> neo4jTemplate.deleteById(s.getKey(), Neo4jNetworkService.class)); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java index 9ccca1bd05..cc37642f30 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java @@ -72,6 +72,9 @@ import org.apache.syncope.core.persistence.api.entity.group.GPlainAttrValue; import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.group.TypeExtension; +import org.apache.syncope.core.persistence.api.entity.keymaster.ConfParam; +import org.apache.syncope.core.persistence.api.entity.keymaster.DomainEntity; +import org.apache.syncope.core.persistence.api.entity.keymaster.NetworkServiceEntity; import org.apache.syncope.core.persistence.api.entity.policy.AccessPolicy; import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy; @@ -124,6 +127,9 @@ import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGPlainAttrValue; import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jGroup; import org.apache.syncope.core.persistence.neo4j.entity.group.Neo4jTypeExtension; +import org.apache.syncope.core.persistence.neo4j.entity.keymaster.Neo4jConfParam; +import org.apache.syncope.core.persistence.neo4j.entity.keymaster.Neo4jDomain; +import org.apache.syncope.core.persistence.neo4j.entity.keymaster.Neo4jNetworkService; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccessPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccountPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAttrReleasePolicy; @@ -325,6 +331,12 @@ public E newEntity(final Class reference) { result = (E) new Neo4jOIDCJWKS(); } else if (reference.equals(WAConfigEntry.class)) { result = (E) new Neo4jWAConfigEntry(); + } else if (reference.equals(ConfParam.class)) { + result = (E) new Neo4jConfParam(); + } else if (reference.equals(DomainEntity.class)) { + result = (E) new Neo4jDomain(); + } else if (reference.equals(NetworkServiceEntity.class)) { + result = (E) new Neo4jNetworkService(); } else { throw new IllegalArgumentException("Could not find a Neo4j implementation of " + reference.getName()); } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jConfParam.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jConfParam.java new file mode 100644 index 0000000000..aa839f55c8 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jConfParam.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.persistence.neo4j.entity.keymaster; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.json.JsonMapper; +import java.io.IOException; +import org.apache.syncope.core.persistence.api.entity.keymaster.ConfParam; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractProvidedKeyNode; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jConfParam.NODE) +public class Neo4jConfParam extends AbstractProvidedKeyNode implements ConfParam { + + private static final long serialVersionUID = 8742750097008236475L; + + private static final JsonMapper MAPPER = JsonMapper.builder().findAndAddModules().build(); + + public static final String NODE = "ConfParam"; + + private String jsonValue; + + @Override + public JsonNode getValue() { + JsonNode deserialized = null; + try { + deserialized = MAPPER.readTree(jsonValue); + } catch (final IOException e) { + LOG.error("Could not deserialize {}", jsonValue, e); + } + return deserialized; + } + + @Override + public void setValue(final JsonNode value) { + try { + this.jsonValue = MAPPER.writeValueAsString(value); + } catch (JsonProcessingException e) { + LOG.error("Could not serialize {}", value, e); + } + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jDomain.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jDomain.java new file mode 100644 index 0000000000..557d86535f --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jDomain.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.core.persistence.neo4j.entity.keymaster; + +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.keymaster.client.api.model.Domain; +import org.apache.syncope.core.persistence.api.entity.keymaster.DomainEntity; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractProvidedKeyNode; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jDomain.NODE) +public class Neo4jDomain extends AbstractProvidedKeyNode implements DomainEntity { + + private static final long serialVersionUID = -9028021617728866693L; + + public static final String NODE = "SyncopeDomain"; + + private String spec; + + @Override + public Domain get() { + return StringUtils.isBlank(spec) + ? null + : POJOHelper.deserialize(spec, Domain.class); + } + + @Override + public void set(final Domain domain) { + spec = POJOHelper.serialize(domain); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jNetworkService.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jNetworkService.java new file mode 100644 index 0000000000..ec1e24e6ab --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/keymaster/Neo4jNetworkService.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.persistence.neo4j.entity.keymaster; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.common.keymaster.client.api.model.NetworkService; +import org.apache.syncope.core.persistence.api.entity.keymaster.NetworkServiceEntity; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractGeneratedKeyNode; +import org.springframework.data.neo4j.core.schema.Node; + +@Node(Neo4jNetworkService.NODE) +public class Neo4jNetworkService extends AbstractGeneratedKeyNode implements NetworkServiceEntity { + + private static final long serialVersionUID = 8742750097008236475L; + + public static final String NODE = "NetworkService"; + + @NotNull + private NetworkService.Type type; + + @NotNull + private String address; + + @Override + public NetworkService.Type getType() { + return type; + } + + @Override + public void setType(final NetworkService.Type type) { + this.type = type; + } + + @Override + public String getAddress() { + return address; + } + + @Override + public void setAddress(final String address) { + this.address = address; + } +} diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyDomainOps.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyDomainOps.java index 871547f59c..2a0c39d4c6 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyDomainOps.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyDomainOps.java @@ -21,14 +21,15 @@ import java.util.List; import org.apache.syncope.common.keymaster.client.api.DomainOps; import org.apache.syncope.common.keymaster.client.api.model.Domain; +import org.apache.syncope.common.keymaster.client.api.model.Neo4jDomain; import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.apache.syncope.core.persistence.api.DomainRegistry; public class DummyDomainOps implements DomainOps { - private final DomainRegistry domainRegistry; + private final DomainRegistry domainRegistry; - public DummyDomainOps(final DomainRegistry domainRegistry) { + public DummyDomainOps(final DomainRegistry domainRegistry) { this.domainRegistry = domainRegistry; } @@ -39,12 +40,12 @@ public List list() { @Override public Domain read(final String key) { - return new Domain.Builder(key).build(); + return new Neo4jDomain.Builder(key).build(); } @Override public void create(final Domain domain) { - domainRegistry.register(domain); + domainRegistry.register((Neo4jDomain) domain); } @Override diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/PersistenceTestContext.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/PersistenceTestContext.java index 132970878b..19904f044a 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/PersistenceTestContext.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/PersistenceTestContext.java @@ -22,6 +22,7 @@ import java.util.List; 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.Neo4jDomain; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.core.persistence.api.DomainHolder; import org.apache.syncope.core.persistence.api.DomainRegistry; @@ -114,7 +115,7 @@ public ConfParamOps confParamOps() { } @Bean - public DomainOps domainOps(final DomainRegistry domainRegistry) { + public DomainOps domainOps(final DomainRegistry domainRegistry) { return new DummyDomainOps(domainRegistry); } diff --git a/core/persistence-neo4j/src/test/resources/domains/TwoContent.xml b/core/persistence-neo4j/src/test/resources/domains/TwoContent.xml index 297c886364..d851193405 100644 --- a/core/persistence-neo4j/src/test/resources/domains/TwoContent.xml +++ b/core/persistence-neo4j/src/test/resources/domains/TwoContent.xml @@ -29,7 +29,7 @@ under the License. - + domainRegistry; - public DummyDomainOps(final DomainRegistry domainRegistry) { + public DummyDomainOps(final DomainRegistry domainRegistry) { this.domainRegistry = domainRegistry; } @@ -39,12 +40,12 @@ public List list() { @Override public Domain read(final String key) { - return new Domain.Builder(key).build(); + return new JPADomain.Builder(key).build(); } @Override public void create(final Domain domain) { - domainRegistry.register(domain); + domainRegistry.register((JPADomain) domain); } @Override diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ProvisioningTestContext.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ProvisioningTestContext.java index 23636cd7fd..c6ab221219 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ProvisioningTestContext.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ProvisioningTestContext.java @@ -22,6 +22,7 @@ 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.core.persistence.api.DomainRegistry; import org.apache.syncope.core.persistence.api.content.ContentLoader; import org.apache.syncope.core.persistence.jpa.MasterDomain; @@ -68,7 +69,7 @@ public ConfParamOps confParamOps() { } @Bean - public DomainOps domainOps(final DomainRegistry domainRegistry) { + public DomainOps domainOps(final DomainRegistry domainRegistry) { return new DummyDomainOps(domainRegistry); } } diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/InternalConfParamHelper.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/InternalConfParamHelper.java index 01f2847bc9..b256438298 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/InternalConfParamHelper.java +++ b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/keymaster/internal/InternalConfParamHelper.java @@ -23,10 +23,10 @@ import com.fasterxml.jackson.databind.json.JsonMapper; import java.util.Map; import java.util.TreeMap; -import org.apache.syncope.core.persistence.api.dao.ConfParamDAO; import org.apache.syncope.core.persistence.api.dao.NotFoundException; -import org.apache.syncope.core.persistence.api.entity.ConfParam; -import org.apache.syncope.core.persistence.api.entity.SelfKeymasterEntityFactory; +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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.transaction.annotation.Transactional; @@ -39,9 +39,9 @@ public class InternalConfParamHelper { protected final ConfParamDAO confParamDAO; - protected final SelfKeymasterEntityFactory entityFactory; + protected final EntityFactory entityFactory; - public InternalConfParamHelper(final ConfParamDAO confParamDAO, final SelfKeymasterEntityFactory entityFactory) { + public InternalConfParamHelper(final ConfParamDAO confParamDAO, final EntityFactory entityFactory) { this.confParamDAO = confParamDAO; this.entityFactory = entityFactory; } @@ -72,7 +72,7 @@ public void set(final String key, final JsonNode value) { ConfParam param = confParamDAO.findById(key).orElse(null); if (param == null) { - param = entityFactory.newConfParam(); + param = entityFactory.newEntity(ConfParam.class); param.setKey(key); } param.setValue(value); diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/DomainLogic.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/DomainLogic.java index fc1ff5ca0c..832cee9dd7 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/DomainLogic.java +++ b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/DomainLogic.java @@ -24,27 +24,29 @@ import org.apache.syncope.common.keymaster.client.api.DomainWatcher; import org.apache.syncope.common.keymaster.client.api.KeymasterException; 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.to.EntityTO; import org.apache.syncope.common.lib.types.CipherAlgorithm; -import org.apache.syncope.core.persistence.api.dao.DomainDAO; 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.DomainEntity; -import org.apache.syncope.core.persistence.api.entity.SelfKeymasterEntityFactory; +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.springframework.security.access.prepost.PreAuthorize; public class DomainLogic extends AbstractTransactionalLogic { protected final DomainDAO domainDAO; - protected final SelfKeymasterEntityFactory entityFactory; + protected final EntityFactory entityFactory; protected final DomainWatcher domainWatcher; public DomainLogic( final DomainDAO domainDAO, - final SelfKeymasterEntityFactory entityFactory, + final EntityFactory entityFactory, final DomainWatcher domainWatcher) { this.domainDAO = domainDAO; @@ -75,7 +77,7 @@ public Domain create(final Domain domain) { throw new DuplicateException("Domain " + domain.getKey() + " already existing"); } - DomainEntity domainEntity = entityFactory.newDomainEntity(); + DomainEntity domainEntity = entityFactory.newEntity(DomainEntity.class); domainEntity.setKey(domain.getKey()); domainEntity.set(domain); domainEntity = domainDAO.save(domainEntity); @@ -99,14 +101,25 @@ public void changeAdminPassword(final String key, final String password, final C @PreAuthorize("@environment.getProperty('keymaster.username') == authentication.name") public void adjustPoolSize(final String key, final int poolMaxActive, final int poolMinIdle) { - DomainEntity domain = domainDAO.findById(key). + DomainEntity domainEntity = domainDAO.findById(key). orElseThrow(() -> new NotFoundException("Domain " + key)); - Domain domainObj = domain.get(); - domainObj.setPoolMaxActive(poolMaxActive); - domainObj.setPoolMinIdle(poolMinIdle); - domain.set(domainObj); - domainDAO.save(domain); + Domain domain = domainEntity.get(); + switch (domain) { + case JPADomain jpaDomain -> { + jpaDomain.setPoolMaxActive(poolMaxActive); + jpaDomain.setPoolMinIdle(poolMinIdle); + } + + case Neo4jDomain neo4jDomain -> + neo4jDomain.setMaxConnectionPoolSize(poolMaxActive); + + default -> { + } + } + domainEntity.set(domain); + + domainDAO.save(domainEntity); } @PreAuthorize("@environment.getProperty('keymaster.username') == authentication.name") diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/NetworkServiceLogic.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/NetworkServiceLogic.java index ab04b1d9be..290f396678 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/NetworkServiceLogic.java +++ b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/logic/NetworkServiceLogic.java @@ -22,10 +22,10 @@ import java.util.List; import org.apache.syncope.common.keymaster.client.api.model.NetworkService; import org.apache.syncope.common.lib.to.EntityTO; -import org.apache.syncope.core.persistence.api.dao.NetworkServiceDAO; import org.apache.syncope.core.persistence.api.dao.NotFoundException; -import org.apache.syncope.core.persistence.api.entity.NetworkServiceEntity; -import org.apache.syncope.core.persistence.api.entity.SelfKeymasterEntityFactory; +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.apache.syncope.core.spring.security.SecureRandomUtils; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.transaction.annotation.Transactional; @@ -34,9 +34,9 @@ public class NetworkServiceLogic extends AbstractTransactionalLogic { protected final NetworkServiceDAO serviceDAO; - protected final SelfKeymasterEntityFactory entityFactory; + protected final EntityFactory entityFactory; - public NetworkServiceLogic(final NetworkServiceDAO serviceDAO, final SelfKeymasterEntityFactory entityFactory) { + public NetworkServiceLogic(final NetworkServiceDAO serviceDAO, final EntityFactory entityFactory) { this.serviceDAO = serviceDAO; this.entityFactory = entityFactory; } @@ -76,7 +76,7 @@ public void register(final NetworkService networkService) { if (serviceDAO.findAll(networkService.getType()).stream(). noneMatch(s -> s.getAddress().equals(networkService.getAddress()))) { - NetworkServiceEntity service = entityFactory.newNetworkService(); + NetworkServiceEntity service = entityFactory.newEntity(NetworkServiceEntity.class); service.setType(networkService.getType()); service.setAddress(networkService.getAddress()); serviceDAO.save(service); diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASelfKeymasterEntityFactory.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASelfKeymasterEntityFactory.java deleted file mode 100644 index 0c85078fd3..0000000000 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASelfKeymasterEntityFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.entity; - -import org.apache.syncope.core.persistence.api.entity.ConfParam; -import org.apache.syncope.core.persistence.api.entity.DomainEntity; -import org.apache.syncope.core.persistence.api.entity.NetworkServiceEntity; -import org.apache.syncope.core.persistence.api.entity.SelfKeymasterEntityFactory; -import org.apache.syncope.core.spring.security.SecureRandomUtils; - -public class JPASelfKeymasterEntityFactory implements SelfKeymasterEntityFactory { - - @Override - public ConfParam newConfParam() { - return new JPAConfParam(); - } - - @Override - public NetworkServiceEntity newNetworkService() { - JPANetworkService service = new JPANetworkService(); - service.setKey(SecureRandomUtils.generateRandomUUID().toString()); - return service; - } - - @Override - public DomainEntity newDomainEntity() { - return new JPADomain(); - } -} diff --git a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/starter/SelfKeymasterContext.java b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/starter/SelfKeymasterContext.java index 95cdbe2173..9f23bb4d12 100644 --- a/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/starter/SelfKeymasterContext.java +++ b/core/self-keymaster-starter/src/main/java/org/apache/syncope/core/starter/SelfKeymasterContext.java @@ -21,7 +21,6 @@ import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import io.swagger.v3.oas.integration.api.OpenAPIConfiguration; import io.swagger.v3.oas.models.security.SecurityScheme; -import jakarta.persistence.EntityManager; import java.util.List; import java.util.Map; import java.util.Set; @@ -57,15 +56,10 @@ import org.apache.syncope.core.logic.ConfParamLogic; import org.apache.syncope.core.logic.DomainLogic; import org.apache.syncope.core.logic.NetworkServiceLogic; -import org.apache.syncope.core.persistence.api.dao.ConfParamDAO; -import org.apache.syncope.core.persistence.api.dao.DomainDAO; -import org.apache.syncope.core.persistence.api.dao.NetworkServiceDAO; -import org.apache.syncope.core.persistence.api.entity.SelfKeymasterEntityFactory; -import org.apache.syncope.core.persistence.jpa.dao.repo.ConfParamRepo; -import org.apache.syncope.core.persistence.jpa.dao.repo.DomainRepo; -import org.apache.syncope.core.persistence.jpa.dao.repo.NetworkServiceRepo; -import org.apache.syncope.core.persistence.jpa.dao.repo.NetworkServiceRepoExtImpl; -import org.apache.syncope.core.persistence.jpa.entity.JPASelfKeymasterEntityFactory; +import org.apache.syncope.core.persistence.api.dao.keymaster.ConfParamDAO; +import org.apache.syncope.core.persistence.api.dao.keymaster.DomainDAO; +import org.apache.syncope.core.persistence.api.dao.keymaster.NetworkServiceDAO; +import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.provisioning.api.UserProvisioningManager; import org.apache.syncope.core.rest.cxf.JavaDocUtils; import org.apache.syncope.core.rest.cxf.RestServiceExceptionMapper; @@ -87,7 +81,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; -import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; @EnableConfigurationProperties(KeymasterProperties.class) @Configuration(proxyBeanMethods = false) @@ -210,7 +203,7 @@ public UsernamePasswordAuthenticationProvider usernamePasswordAuthenticationProv @Bean public InternalConfParamHelper internalConfParamHelper( final ConfParamDAO confParamDAO, - final SelfKeymasterEntityFactory entityFactory) { + final EntityFactory entityFactory) { return new InternalConfParamHelper(confParamDAO, entityFactory); } @@ -241,7 +234,7 @@ public ConfParamLogic confParamLogic(final InternalConfParamHelper helper) { @Bean public DomainLogic domainLogic( final DomainDAO domainDAO, - final SelfKeymasterEntityFactory selfKeymasterEntityFactory, + final EntityFactory selfKeymasterEntityFactory, final DomainWatcher domainWatcher) { return new DomainLogic(domainDAO, selfKeymasterEntityFactory, domainWatcher); @@ -250,34 +243,9 @@ public DomainLogic domainLogic( @Bean public NetworkServiceLogic networkServiceLogic( final NetworkServiceDAO serviceDAO, - final SelfKeymasterEntityFactory selfKeymasterEntityFactory) { + final EntityFactory entityFactory) { - return new NetworkServiceLogic(serviceDAO, selfKeymasterEntityFactory); - } - - @Bean - public SelfKeymasterEntityFactory selfKeymasterEntityFactory() { - return new JPASelfKeymasterEntityFactory(); - } - - @Bean - public ConfParamDAO confParamDAO(final JpaRepositoryFactory jpaRepositoryFactory) { - return jpaRepositoryFactory.getRepository(ConfParamRepo.class); - } - - @Bean - public DomainDAO domainDAO(final JpaRepositoryFactory jpaRepositoryFactory) { - return jpaRepositoryFactory.getRepository(DomainRepo.class); - } - - @Bean - public NetworkServiceDAO networkServiceDAO( - final JpaRepositoryFactory jpaRepositoryFactory, - final EntityManager entityManager) { - - return jpaRepositoryFactory.getRepository( - NetworkServiceRepo.class, - new NetworkServiceRepoExtImpl(entityManager)); + return new NetworkServiceLogic(serviceDAO, entityFactory); } @Bean diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java index 6ee9ba3f3a..11be34d705 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java @@ -18,6 +18,7 @@ */ package org.apache.syncope.core.spring.security; +import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.tuple.Triple; @@ -70,18 +71,19 @@ public UsernamePasswordAuthenticationProvider( @Override public Authentication authenticate(final Authentication authentication) { - Domain domain; + String domainKey; + Optional domain; if (SyncopeConstants.MASTER_DOMAIN.equals( SyncopeAuthenticationDetails.class.cast(authentication.getDetails()).getDomain())) { - domain = new Domain.Builder(SyncopeConstants.MASTER_DOMAIN).build(); + domainKey = SyncopeConstants.MASTER_DOMAIN; + domain = Optional.empty(); } else { + domainKey = SyncopeAuthenticationDetails.class.cast(authentication.getDetails()).getDomain(); try { - domain = domainOps.read( - SyncopeAuthenticationDetails.class.cast(authentication.getDetails()).getDomain()); + domain = Optional.of(domainOps.read(domainKey)); } catch (NotFoundException | KeymasterException e) { - throw new BadCredentialsException("Could not find domain " - + SyncopeAuthenticationDetails.class.cast(authentication.getDetails()).getDomain(), e); + throw new BadCredentialsException("Could not find domain " + domainKey, e); } } @@ -95,27 +97,30 @@ public Authentication authenticate(final Authentication authentication) { authenticated = authentication.getCredentials().toString().equals(securityProperties.getAnonymousKey()); } else if (securityProperties.getAdminUser().equals(authentication.getName())) { username.set(securityProperties.getAdminUser()); - if (SyncopeConstants.MASTER_DOMAIN.equals(domain.getKey())) { + if (SyncopeConstants.MASTER_DOMAIN.equals(domainKey)) { credentialChecker.checkIsDefaultAdminPasswordInUse(); authenticated = ENCRYPTOR.verify( authentication.getCredentials().toString(), securityProperties.getAdminPasswordAlgorithm(), securityProperties.getAdminPassword()); - } else { + } else if (domain.isPresent()) { authenticated = ENCRYPTOR.verify( authentication.getCredentials().toString(), - domain.getAdminCipherAlgorithm(), - domain.getAdminPassword()); + domain.get().getAdminCipherAlgorithm(), + domain.get().getAdminPassword()); + } else { + LOG.error("Could not read admin credentials for domain {}", domainKey); + authenticated = false; } } else { - Triple authResult = AuthContextUtils.callAsAdmin(domain.getKey(), - () -> dataAccessor.authenticate(domain.getKey(), authentication)); + Triple authResult = AuthContextUtils.callAsAdmin(domainKey, + () -> dataAccessor.authenticate(domainKey, authentication)); authenticated = authResult.getMiddle(); if (authResult.getLeft() != null && authResult.getMiddle() != null) { username.set(authResult.getLeft().getUsername()); if (!authenticated) { - AuthContextUtils.runAsAdmin(domain.getKey(), () -> provisioningManager.internalSuspend( + AuthContextUtils.runAsAdmin(domainKey, () -> provisioningManager.internalSuspend( authResult.getLeft().getKey(), securityProperties.getAdminUser(), "Failed authentication")); } } @@ -125,8 +130,7 @@ public Authentication authenticate(final Authentication authentication) { username.set(authentication.getPrincipal().toString()); } - return finalizeAuthentication( - authenticated, domain.getKey(), username.get(), delegationKey.get(), authentication); + return finalizeAuthentication(authenticated, domainKey, username.get(), delegationKey.get(), authentication); } protected Authentication finalizeAuthentication( diff --git a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/DummyDomainOps.java b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/DummyDomainOps.java index 759518b000..54f4f1338e 100644 --- a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/DummyDomainOps.java +++ b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/DummyDomainOps.java @@ -21,14 +21,15 @@ import java.util.List; import org.apache.syncope.common.keymaster.client.api.DomainOps; 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.lib.types.CipherAlgorithm; import org.apache.syncope.core.persistence.api.DomainRegistry; public class DummyDomainOps implements DomainOps { - private final DomainRegistry domainRegistry; + private final DomainRegistry domainRegistry; - public DummyDomainOps(final DomainRegistry domainRegistry) { + public DummyDomainOps(final DomainRegistry domainRegistry) { this.domainRegistry = domainRegistry; } @@ -39,12 +40,12 @@ public List list() { @Override public Domain read(final String key) { - return new Domain.Builder(key).build(); + return new JPADomain.Builder(key).build(); } @Override public void create(final Domain domain) { - domainRegistry.register(domain); + domainRegistry.register((JPADomain) domain); } @Override 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 762273ddf8..1b4c586dec 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 @@ -25,6 +25,7 @@ import java.time.OffsetDateTime; 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.UserCR; import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.apache.syncope.core.persistence.api.DomainRegistry; @@ -103,7 +104,7 @@ public ConfParamOps confParamOps() { } @Bean - public DomainOps domainOps(final DomainRegistry domainRegistry) { + public DomainOps domainOps(final DomainRegistry domainRegistry) { return new DummyDomainOps(domainRegistry); } } diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/KeymasterITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/KeymasterITCase.java index f4b6147b4e..3df305e6ca 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/KeymasterITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/KeymasterITCase.java @@ -42,6 +42,7 @@ import org.apache.syncope.client.lib.SyncopeClientFactoryBean; import org.apache.syncope.common.keymaster.client.api.KeymasterException; 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.NetworkService; import org.apache.syncope.common.keymaster.client.self.SelfKeymasterDomainOps; import org.apache.syncope.common.lib.SyncopeConstants; @@ -217,19 +218,19 @@ public void domainCRUD() throws Exception { // 1. create new domain String key = UUID.randomUUID().toString(); - domainOps.create(new Domain.Builder(key). + domainOps.create(new JPADomain.Builder(key). jdbcDriver("org.h2.Driver"). jdbcURL("jdbc:h2:mem:syncopetest" + key + ";DB_CLOSE_DELAY=-1"). dbUsername("sa"). dbPassword(""). databasePlatform("org.apache.openjpa.jdbc.sql.H2Dictionary"). - transactionIsolation(Domain.TransactionIsolation.TRANSACTION_READ_UNCOMMITTED). + transactionIsolation(JPADomain.TransactionIsolation.TRANSACTION_READ_UNCOMMITTED). adminPassword(Encryptor.getInstance().encode("password", CipherAlgorithm.BCRYPT)). adminCipherAlgorithm(CipherAlgorithm.BCRYPT). build()); - Domain domain = domainOps.read(key); - assertEquals(Domain.TransactionIsolation.TRANSACTION_READ_UNCOMMITTED, domain.getTransactionIsolation()); + JPADomain domain = (JPADomain) domainOps.read(key); + assertEquals(JPADomain.TransactionIsolation.TRANSACTION_READ_UNCOMMITTED, domain.getTransactionIsolation()); assertEquals(CipherAlgorithm.BCRYPT, domain.getAdminCipherAlgorithm()); assertEquals(10, domain.getPoolMaxActive()); assertEquals(2, domain.getPoolMinIdle()); @@ -239,7 +240,7 @@ public void domainCRUD() throws Exception { // 2. update domain domainOps.adjustPoolSize(key, 100, 23); - domain = domainOps.read(key); + domain = (JPADomain) domainOps.read(key); assertEquals(100, domain.getPoolMaxActive()); assertEquals(23, domain.getPoolMinIdle()); @@ -306,12 +307,12 @@ public void domainCRUD() throws Exception { public void domainCreateMaster() { assertThrows( KeymasterException.class, - () -> domainOps.create(new Domain.Builder(SyncopeConstants.MASTER_DOMAIN).build())); + () -> domainOps.create(new JPADomain.Builder(SyncopeConstants.MASTER_DOMAIN).build())); } @Test public void domainCreateDuplicateKey() { - assertThrows(KeymasterException.class, () -> domainOps.create(new Domain.Builder("Two").build())); + assertThrows(KeymasterException.class, () -> domainOps.create(new JPADomain.Builder("Two").build())); } @Test From bd21613428aaa712404ca80890d40956fce74274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Fri, 23 Feb 2024 12:37:53 +0100 Subject: [PATCH 09/10] fix --- .../syncope/core/persistence/neo4j/PersistenceContext.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java index 59f4917dee..cceea022e0 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java @@ -1273,8 +1273,11 @@ public DomainDAO domainDAO(final SyncopeNeo4jRepositoryFactory neo4jRepositoryFa @ConditionalOnMissingBean @Bean - public NetworkServiceRepoExt networkServiceRepoExt(final Neo4jTemplate neo4jTemplate) { - return new NetworkServiceRepoExtImpl(neo4jTemplate); + public NetworkServiceRepoExt networkServiceRepoExt( + final Neo4jTemplate neo4jTemplate, + final NodeValidator nodeValidator) { + + return new NetworkServiceRepoExtImpl(neo4jTemplate, nodeValidator); } @Bean From 7174fae2a7723519a9641241b908a7a991ebaff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Fri, 23 Feb 2024 14:58:03 +0100 Subject: [PATCH 10/10] Support for Neo4j domain definition in Console --- .../clientapps/ClientAppDirectoryPanel.java | 2 +- .../console/panels/DomainDirectoryPanel.java | 43 ++++++++++++--- .../console/panels/DomainPoolModalPanel.java | 19 ++++++- .../console/panels/DomainWizardBuilder.java | 54 +++++++++++++++++-- .../panels/DomainDirectoryPanel.properties | 1 + .../panels/DomainDirectoryPanel_it.properties | 1 + .../panels/DomainDirectoryPanel_ja.properties | 1 + .../DomainDirectoryPanel_pt_BR.properties | 1 + .../panels/DomainDirectoryPanel_ru.properties | 1 + ...ml => DomainWizardBuilder$JPAStorage.html} | 0 .../DomainWizardBuilder$Neo4jStorage.html | 26 +++++++++ .../client/lib/SyncopeAnonymousClient.java | 2 +- .../CommonEntityManagerFactoryConf.java | 2 +- pom.xml | 6 +++ 14 files changed, 143 insertions(+), 16 deletions(-) rename client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/{DomainWizardBuilder$Storage.html => DomainWizardBuilder$JPAStorage.html} (100%) create mode 100644 client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainWizardBuilder$Neo4jStorage.html diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.java b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.java index 0b3e90eae1..29bc6a846c 100644 --- a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.java +++ b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppDirectoryPanel.java @@ -218,7 +218,7 @@ protected void restore(final String json, final AjaxRequestTarget target) { SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED)); } catch (Exception e) { - LOG.error("While restoring ClientApp {}", ((ClientAppTO) model.getObject()).getKey(), e); + LOG.error("While restoring ClientApp {}", model.getObject().getKey(), e); SyncopeConsoleSession.get().onException(e); } ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainDirectoryPanel.java index 562f6cec3e..6565ca4b1a 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainDirectoryPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainDirectoryPanel.java @@ -18,6 +18,7 @@ */ package org.apache.syncope.client.console.panels; +import com.fasterxml.jackson.databind.JsonNode; import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal; import java.util.ArrayList; import java.util.Collection; @@ -38,6 +39,7 @@ import org.apache.syncope.common.keymaster.client.api.KeymasterException; 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.types.IdRepoEntitlement; import org.apache.wicket.PageReference; import org.apache.wicket.ajax.AjaxRequestTarget; @@ -59,6 +61,8 @@ public class DomainDirectoryPanel extends DirectoryPanel utilityModal = new BaseModal<>(Constants.OUTER); + private Class domainClass = JPADomain.class; + public DomainDirectoryPanel(final String id, final PageReference pageRef) { super(id, null, pageRef); disableCheckBoxes(); @@ -78,7 +82,24 @@ public DomainDirectoryPanel(final String id, final PageReference pageRef) { utilityModal.size(Modal.Size.Small); utilityModal.addSubmitButton(); - addNewItemPanelBuilder(new DomainWizardBuilder(domainOps, new JPADomain(), pageRef), true); + Domain newDomain; + try { + JsonNode info = SyncopeConsoleSession.get().getAnonymousClient().info(); + if (info.has("persistence") && info.get("persistence").has("vendor") + && "OpenJPA".equals(info.get("persistence").get("vendor").asText())) { + + domainClass = JPADomain.class; + } else { + domainClass = Neo4jDomain.class; + } + + newDomain = domainClass.getConstructor().newInstance(); + } catch (Exception e) { + LOG.error("Could not instantiate {}", domainClass.getName(), e); + newDomain = new JPADomain(); + } + + addNewItemPanelBuilder(new DomainWizardBuilder(domainOps, newDomain, pageRef), true); initResultTable(); @@ -89,12 +110,20 @@ public DomainDirectoryPanel(final String id, final PageReference pageRef) { protected List> getColumns() { List> columns = new ArrayList<>(); columns.add(new PropertyColumn<>(new StringResourceModel("key", this), "key", "key")); - columns.add(new PropertyColumn<>( - new StringResourceModel("jdbcURL", this), "jdbcURL", "jdbcURL")); - columns.add(new PropertyColumn<>( - new StringResourceModel("poolMaxActive", this), "poolMaxActive", "poolMaxActive")); - columns.add(new PropertyColumn<>( - new StringResourceModel("poolMinIdle", this), "poolMinIdle", "poolMinIdle")); + if (JPADomain.class.equals(domainClass)) { + columns.add(new PropertyColumn<>( + new StringResourceModel("jdbcURL", this), "jdbcURL", "jdbcURL")); + columns.add(new PropertyColumn<>( + new StringResourceModel("poolMaxActive", this), "poolMaxActive", "poolMaxActive")); + columns.add(new PropertyColumn<>( + new StringResourceModel("poolMinIdle", this), "poolMinIdle", "poolMinIdle")); + } else { + columns.add(new PropertyColumn<>( + new StringResourceModel("uri", this), "uri", "uri")); + columns.add(new PropertyColumn<>( + new StringResourceModel("poolMaxActive", this), + "maxConnectionPoolSize", "maxConnectionPoolSize")); + } return columns; } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainPoolModalPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainPoolModalPanel.java index bdfaf2a99c..2da3262561 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainPoolModalPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainPoolModalPanel.java @@ -29,6 +29,8 @@ import org.apache.syncope.common.keymaster.client.api.model.Neo4jDomain; import org.apache.wicket.PageReference; import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.spring.injection.annot.SpringBean; @@ -45,16 +47,29 @@ public DomainPoolModalPanel(final Domain domain, final BaseModal modal, super(modal, pageRef); this.domain = domain; + IModel poolMaxActiveModel; + IModel poolMinIdleModel; + if (domain instanceof JPADomain) { + poolMaxActiveModel = new PropertyModel<>(domain, "poolMaxActive"); + poolMinIdleModel = new PropertyModel<>(domain, "poolMinIdle"); + } else { + poolMaxActiveModel = new PropertyModel<>(domain, "maxConnectionPoolSize"); + poolMinIdleModel = new Model<>(); + } + add(new AjaxSpinnerFieldPanel.Builder().min(0).build( "poolMaxActive", "poolMaxActive", Integer.class, - new PropertyModel<>(domain, "poolMaxActive")).setRequired(true)); + poolMaxActiveModel).setRequired(true)); add(new AjaxSpinnerFieldPanel.Builder().min(0).build( "poolMinIdle", "poolMinIdle", Integer.class, - new PropertyModel<>(domain, "poolMinIdle")).setRequired(true)); + poolMinIdleModel). + setRequired(domain instanceof JPADomain). + setOutputMarkupPlaceholderTag(true). + setVisible(domain instanceof JPADomain)); } @Override diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainWizardBuilder.java index 50b1c576c4..6869e0d7e1 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainWizardBuilder.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DomainWizardBuilder.java @@ -19,7 +19,9 @@ package org.apache.syncope.client.console.panels; import java.io.Serializable; +import java.net.URI; import java.util.List; +import java.util.Optional; import org.apache.commons.lang3.BooleanUtils; import org.apache.syncope.client.console.wicket.markup.html.form.JsonEditorPanel; import org.apache.syncope.client.console.wicket.markup.html.form.XMLEditorPanel; @@ -33,12 +35,14 @@ import org.apache.syncope.common.keymaster.client.api.DomainOps; 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.types.CipherAlgorithm; import org.apache.wicket.PageReference; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.extensions.wizard.WizardModel; import org.apache.wicket.extensions.wizard.WizardStep; +import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; @@ -79,18 +83,18 @@ protected Serializable onApplyInternal(final Domain domain) { @Override protected WizardModel buildModelSteps(final Domain domain, final WizardModel wizardModel) { - wizardModel.add(new Storage(domain)); + wizardModel.add(domain instanceof Neo4jDomain ? new Neo4jStorage(domain) : new JPAStorage(domain)); wizardModel.add(new AdminCredentials(domain)); wizardModel.add(new Content(domain)); wizardModel.add(new KeymasterConfParams(domain, pageRef)); return wizardModel; } - public static class Storage extends WizardStep { + public static class JPAStorage extends WizardStep { private static final long serialVersionUID = 3671044119870133102L; - public Storage(final Domain domain) { + public JPAStorage(final Domain domain) { add(new AjaxTextFieldPanel( "key", "key", @@ -118,7 +122,9 @@ public Storage(final Domain domain) { "dbUsername", new PropertyModel<>(domain, "dbUsername")).addRequiredLabel()); add(new EncryptedFieldPanel( - "dbPassword", "dbPassword", new PropertyModel<>(domain, "dbPassword"), false)); + "dbPassword", + "dbPassword", + new PropertyModel<>(domain, "dbPassword"), false)); AjaxDropDownChoicePanel transactionIsolation = new AjaxDropDownChoicePanel<>( @@ -159,6 +165,46 @@ public Storage(final Domain domain) { } } + public static class Neo4jStorage extends WizardStep { + + private static final long serialVersionUID = 3671044119870133102L; + + public Neo4jStorage(final Domain domain) { + add(new AjaxTextFieldPanel( + "key", + "key", + new PropertyModel<>(domain, "key")).addRequiredLabel()); + + PropertyModel uriModel = new PropertyModel<>(domain, "uri"); + add(new AjaxTextFieldPanel( + "uri", + "uri", + new IModel<>() { + + private static final long serialVersionUID = 807008909842554829L; + + @Override + public String getObject() { + return Optional.ofNullable(uriModel.getObject()).map(URI::toASCIIString).orElse(null); + } + + @Override + public void setObject(final String object) { + uriModel.setObject(URI.create(object)); + } + }).addRequiredLabel()); + + add(new AjaxTextFieldPanel( + "username", + "username", + new PropertyModel<>(domain, "username")).addRequiredLabel()); + add(new EncryptedFieldPanel( + "password", + "password", + new PropertyModel<>(domain, "password"), false)); + } + } + public static class AdminCredentials extends WizardStep { private static final long serialVersionUID = -7472243942630790243L; diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel.properties index 2b3d92886e..e2ff97f458 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel.properties @@ -38,3 +38,4 @@ content=Initial content keymasterConfParams=Initial conf params defaultContent=Leave default content defaultKeymasterConfParams=Leave default conf params +uri=URI diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel_it.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel_it.properties index a84d8d6dc7..8682f69bb2 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel_it.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel_it.properties @@ -38,3 +38,4 @@ content=Contenuto iniziale keymasterConfParams=Parametri iniziali defaultContent=Contenuto iniziale predefinito defaultKeymasterConfParams=Parametri iniziali predefiniti +uri=URI diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel_ja.properties index 5a93b190e4..621a4f29a5 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel_ja.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel_ja.properties @@ -38,3 +38,4 @@ content=\u521d\u671f\u306e\u5185\u5bb9 keymasterConfParams=\u521d\u671f conf \u30d1\u30e9\u30e1\u30fc\u30bf defaultContent=\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u5185\u5bb9\u306e\u307e\u307e\u306b\u3059\u308b defaultKeymasterConfParams=\u30c7\u30d5\u30a9\u30eb\u30c8\u306e conf \u30d1\u30e9\u30e1\u30fc\u30bf\u306e\u307e\u307e\u306b\u3059\u308b +uri=URI diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel_pt_BR.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel_pt_BR.properties index 2b3d92886e..e2ff97f458 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel_pt_BR.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel_pt_BR.properties @@ -38,3 +38,4 @@ content=Initial content keymasterConfParams=Initial conf params defaultContent=Leave default content defaultKeymasterConfParams=Leave default conf params +uri=URI diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel_ru.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel_ru.properties index 2b3d92886e..e2ff97f458 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel_ru.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainDirectoryPanel_ru.properties @@ -38,3 +38,4 @@ content=Initial content keymasterConfParams=Initial conf params defaultContent=Leave default content defaultKeymasterConfParams=Leave default conf params +uri=URI diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainWizardBuilder$Storage.html b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainWizardBuilder$JPAStorage.html similarity index 100% rename from client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainWizardBuilder$Storage.html rename to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainWizardBuilder$JPAStorage.html diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainWizardBuilder$Neo4jStorage.html b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainWizardBuilder$Neo4jStorage.html new file mode 100644 index 0000000000..ab6075061e --- /dev/null +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/DomainWizardBuilder$Neo4jStorage.html @@ -0,0 +1,26 @@ + + + +
+
+
+
+ + diff --git a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeAnonymousClient.java b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeAnonymousClient.java index d49b0424d8..74acf7c22a 100644 --- a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeAnonymousClient.java +++ b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeAnonymousClient.java @@ -59,7 +59,7 @@ public SyncopeAnonymousClient( this.anonymousAuthHandler = anonymousAuthHandler; } - protected JsonNode info() throws IOException { + public JsonNode info() throws IOException { WebClient webClient = WebClient.create( StringUtils.removeEnd(restClientFactory.getAddress().replace("/rest", "/actuator/info"), "/")). accept(MediaType.APPLICATION_JSON_TYPE). diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java index 7c4283ca44..d48b8ec334 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java @@ -59,7 +59,7 @@ public Map getHealthInfo() { domains.forEach((domain, dataSource) -> { try (Connection conn = DataSourceUtils.getConnection(dataSource)) { - healthInfo.put(domain, !conn.isValid(0)); + healthInfo.put(domain, conn.isValid(0)); } catch (Exception e) { healthInfo.put(domain, false); LOG.debug("When attempting to connect to Domain {}", domain, e); diff --git a/pom.xml b/pom.xml index 19924e997c..60eec3b65b 100644 --- a/pom.xml +++ b/pom.xml @@ -1200,6 +1200,12 @@ under the License. org.webjars.npm bootstrap5-toggle ${bootstrap5-toggle.version} + + + org.webjars.npm + bootstrap + + org.webjars