You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2018/03/15 13:10:36 UTC
[1/2] syncope git commit: [SYNCOPE-1821] Implementation on Core
completed; still missing console + doc
Repository: syncope
Updated Branches:
refs/heads/master 5724a50ca -> 425f9b9ed
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
----------------------------------------------------------------------
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 30a1f47..728c360 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
@@ -42,6 +42,7 @@ import org.apache.syncope.core.persistence.api.entity.Role;
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.persistence.api.entity.DynRealm;
+import org.apache.syncope.core.persistence.api.entity.Privilege;
@Entity
@Table(name = JPARole.TABLE)
@@ -83,6 +84,14 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role {
@Lob
private String consoleLayoutInfo;
+ @ManyToMany(fetch = FetchType.EAGER)
+ @JoinTable(joinColumns =
+ @JoinColumn(name = "role_id"),
+ inverseJoinColumns =
+ @JoinColumn(name = "privilege_id"))
+ @Valid
+ private Set<JPAPrivilege> privileges = new HashSet<>();
+
@Override
public Set<String> getEntitlements() {
return entitlements;
@@ -131,4 +140,15 @@ public class JPARole extends AbstractProvidedKeyEntity implements Role {
this.consoleLayoutInfo = consoleLayoutInfo;
}
+ @Override
+ public boolean add(final Privilege privilege) {
+ checkType(privilege, JPAPrivilege.class);
+ return privileges.add((JPAPrivilege) privilege);
+ }
+
+ @Override
+ public Set<? extends Privilege> getPrivileges() {
+ return privileges;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-jpa/src/main/resources/views.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/views.xml b/core/persistence-jpa/src/main/resources/views.xml
index 88b4bb8..37fd431 100644
--- a/core/persistence-jpa/src/main/resources/views.xml
+++ b/core/persistence-jpa/src/main/resources/views.xml
@@ -111,6 +111,20 @@ under the License.
SELECT ss.user_id AS any_id, ss.role_id AS role_id
FROM SyncopeUser_SyncopeRole ss
</entry>
+ <entry key="user_search_priv">
+ CREATE VIEW user_search_priv AS
+
+ SELECT ss.user_id AS any_id, sp.privilege_id AS privilege_id
+ FROM SyncopeUser_SyncopeRole ss, SyncopeRole_Privilege sp
+ WHERE ss.role_id = sp.role_id
+ </entry>
+ <entry key="user_search_dynpriv">
+ CREATE VIEW user_search_dynpriv AS
+
+ SELECT any_id, privilege_id
+ FROM DynRoleMembers drm, SyncopeRole_Privilege rp
+ WHERE drm.role_id = rp.role_id
+ </entry>
<entry key="user_search_resource">
CREATE VIEW user_search_resource AS
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java
----------------------------------------------------------------------
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 480b959..bac5f53 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
@@ -46,6 +46,7 @@ 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.AssignableCond;
import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
+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.entity.AnyType;
@@ -110,6 +111,10 @@ public class AnySearchTest extends AbstractTest {
roleCond.setRole("Other");
assertTrue(searchDAO.matches(user, SearchCond.getLeafCond(roleCond)));
+ PrivilegeCond privilegeCond = new PrivilegeCond();
+ privilegeCond.setPrivilege("postMighty");
+ assertTrue(searchDAO.matches(user, SearchCond.getLeafCond(privilegeCond)));
+
user = userDAO.find("c9b2dec2-00a7-4855-97c0-d854842b4b24");
assertNotNull(user);
@@ -303,6 +308,16 @@ public class AnySearchTest extends AbstractTest {
}
@Test
+ public void searchByPrivilege() {
+ PrivilegeCond privilegeCond = new PrivilegeCond();
+ privilegeCond.setPrivilege("postMighty");
+
+ List<User> users = searchDAO.search(SearchCond.getLeafCond(privilegeCond), AnyTypeKind.USER);
+ assertNotNull(users);
+ assertEquals(1, users.size());
+ }
+
+ @Test
public void searchByIsNull() {
AttributeCond coolLeafCond = new AttributeCond(AttributeCond.Type.ISNULL);
coolLeafCond.setSchema("cool");
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ApplicationTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ApplicationTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ApplicationTest.java
new file mode 100644
index 0000000..ab5da92
--- /dev/null
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ApplicationTest.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.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.assertNull;
+
+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.jpa.AbstractTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional("Master")
+public class ApplicationTest extends AbstractTest {
+
+ @Autowired
+ private ApplicationDAO applicationDAO;
+
+ @Test
+ public void findAll() {
+ List<Application> applications = applicationDAO.findAll();
+ assertFalse(applications.isEmpty());
+ assertEquals(1, applications.size());
+ }
+
+ @Test
+ public void find() {
+ Application mightyApp = applicationDAO.find("mightyApp");
+ assertNotNull(mightyApp);
+ assertEquals(2, mightyApp.getPrivileges().size());
+
+ Privilege getMighty = applicationDAO.findPrivilege("getMighty");
+ assertNotNull(getMighty);
+ 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.setSpecMimeType("application/xml");
+ privilege.setSpec("<one/>".getBytes());
+ application.add(privilege);
+
+ String privilege2Key = UUID.randomUUID().toString();
+ privilege = entityFactory.newEntity(Privilege.class);
+ privilege.setKey(privilege2Key);
+ privilege.setSpecMimeType("application/xml");
+ privilege.setSpec("<one><two/></one>".getBytes());
+ application.add(privilege);
+
+ String privilege3Key = UUID.randomUUID().toString();
+ privilege = entityFactory.newEntity(Privilege.class);
+ privilege.setKey(privilege3Key);
+ privilege.setSpecMimeType("application/xml");
+ privilege.setSpec("<one><two><three/></two></one>".getBytes());
+ 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);
+ priv3.setApplication(null);
+ application.getPrivileges().remove(priv3);
+ assertEquals(2, application.getPrivileges().size());
+
+ applicationDAO.save(application);
+
+ applicationDAO.flush();
+
+ application = applicationDAO.find(application.getKey());
+ assertNotNull(application);
+ assertNotNull(application.getDescription());
+ assertEquals(2, application.getPrivileges().size());
+
+ // 3. delete application
+ applicationDAO.delete(application);
+
+ applicationDAO.flush();
+
+ assertNull(applicationDAO.find(application.getKey()));
+ assertNull(applicationDAO.findPrivilege(privilege1Key));
+ assertNull(applicationDAO.findPrivilege(privilege2Key));
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index a7e4478..f68e9aa 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -235,6 +235,16 @@ under the License.
left_anyObject_id="fc6dbc3a-6c07-4965-8781-921e7401a4a5"
right_anyObject_id="8559d14d-58c2-46eb-a2d4-a7d35161e8f8"/>
+ <Application id="mightyApp" description="A cool application"/>
+ <Privilege id="postMighty" description="Ability to POST"
+ spec="7B20226D6574686F64223A2022504F5354222C202275726C223A20222F612F622F6322207D"
+ specmimetype="application/json"
+ application_id="mightyApp"/>
+ <Privilege id="getMighty" description="Ability to GET"
+ spec="7B20226D6574686F64223A2022474554222C202275726C223A20222F612F622F6322207D"
+ specmimetype="application/json"
+ application_id="mightyApp"/>
+
<SyncopeRole id="User reviewer"/>
<SyncopeRole_entitlements entitlement="USER_READ" role_id="User reviewer"/>
<SyncopeRole_entitlements entitlement="USER_LIST" role_id="User reviewer"/>
@@ -263,6 +273,7 @@ under the License.
<SyncopeRole_entitlements entitlement="GROUP_READ" role_id="Other"/>
<SyncopeRole_entitlements entitlement="WORKFLOW_FORM_CLAIM" role_id="Other"/>
<SyncopeRole_Realm role_id="Other" realm_id="722f3d84-9c2b-4525-8f6e-e4b82c55a36c"/>
+ <SyncopeRole_Privilege role_id="Other" privilege_id="postMighty"/>
<SyncopeRole id="Search for realm evenTwo"/>
<SyncopeRole_entitlements entitlement="USER_READ" role_id="Search for realm evenTwo"/>
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ApplicationDataBinder.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ApplicationDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ApplicationDataBinder.java
new file mode 100644
index 0000000..c8b7795
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/ApplicationDataBinder.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api.data;
+
+import org.apache.syncope.common.lib.to.ApplicationTO;
+import org.apache.syncope.common.lib.to.PrivilegeTO;
+import org.apache.syncope.core.persistence.api.entity.Application;
+import org.apache.syncope.core.persistence.api.entity.Privilege;
+
+public interface ApplicationDataBinder {
+
+ Application create(ApplicationTO applicationTO);
+
+ Application update(Application application, ApplicationTO applicationTO);
+
+ PrivilegeTO getPrivilegeTO(Privilege privilege);
+
+ ApplicationTO getApplicationTO(Application application);
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ApplicationDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ApplicationDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ApplicationDataBinderImpl.java
new file mode 100644
index 0000000..635ba57
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ApplicationDataBinderImpl.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.data;
+
+import java.util.Base64;
+import java.util.Iterator;
+import java.util.stream.Collectors;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.ApplicationTO;
+import org.apache.syncope.common.lib.to.PrivilegeTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+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.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.Privilege;
+import org.apache.syncope.core.provisioning.api.data.ApplicationDataBinder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ApplicationDataBinderImpl implements ApplicationDataBinder {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ApplicationDataBinder.class);
+
+ @Autowired
+ private ApplicationDAO applicationDAO;
+
+ @Autowired
+ private EntityFactory entityFactory;
+
+ @Override
+ public Application create(final ApplicationTO applicationTO) {
+ return update(entityFactory.newEntity(Application.class), applicationTO);
+ }
+
+ @Override
+ public Application update(final Application toBeUpdated, final ApplicationTO applicationTO) {
+ toBeUpdated.setKey(applicationTO.getKey());
+ Application application = applicationDAO.save(toBeUpdated);
+
+ application.setDescription(applicationTO.getDescription());
+
+ // 1. add or update all (valid) privileges from TO
+ applicationTO.getPrivileges().forEach(privilegeTO -> {
+ if (privilegeTO == null) {
+ LOG.error("Null {}", PrivilegeTO.class.getSimpleName());
+ } else {
+ Privilege privilege = applicationDAO.findPrivilege(privilegeTO.getKey());
+ if (privilege == null) {
+ privilege = entityFactory.newEntity(Privilege.class);
+ privilege.setKey(privilegeTO.getKey());
+ privilege.setApplication(application);
+
+ application.add(privilege);
+ } else if (!application.equals(privilege.getApplication())) {
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidPrivilege);
+ sce.getElements().add(
+ "Privilege " + privilege.getKey() + " already owned by " + privilege.getApplication());
+ throw sce;
+ }
+
+ privilege.setDescription(privilegeTO.getDescription());
+ privilege.setSpecMimeType(privilegeTO.getSpecMimeType());
+ privilege.setSpec(Base64.getDecoder().decode(privilegeTO.getSpec()));
+ }
+ });
+
+ // 2. remove all privileges not contained in the TO
+ for (Iterator<? extends Privilege> itor = application.getPrivileges().iterator(); itor.hasNext();) {
+ Privilege privilege = itor.next();
+ if (!applicationTO.getPrivileges().stream().
+ anyMatch(privilegeTO -> privilege.getKey().equals(privilegeTO.getKey()))) {
+
+ privilege.setApplication(null);
+ itor.remove();
+ }
+ }
+
+ return application;
+ }
+
+ @Override
+ public PrivilegeTO getPrivilegeTO(final Privilege privilege) {
+ PrivilegeTO privilegeTO = new PrivilegeTO();
+ privilegeTO.setKey(privilege.getKey());
+ privilegeTO.setDescription(privilege.getDescription());
+ privilegeTO.setApplication(privilege.getApplication().getKey());
+ privilegeTO.setSpecMimeType(privilege.getSpecMimeType());
+ privilegeTO.setSpec(Base64.getEncoder().encodeToString(privilege.getSpec()));
+ return privilegeTO;
+ }
+
+ @Override
+ public ApplicationTO getApplicationTO(final Application application) {
+ ApplicationTO applicationTO = new ApplicationTO();
+
+ applicationTO.setKey(application.getKey());
+ applicationTO.setDescription(application.getDescription());
+ applicationTO.getPrivileges().addAll(
+ application.getPrivileges().stream().map(privilege -> getPrivilegeTO(privilege)).
+ collect(Collectors.toList()));
+
+ return applicationTO;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java
----------------------------------------------------------------------
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 f15011e..25717d3 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
@@ -22,13 +22,16 @@ import java.util.stream.Collectors;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.to.RoleTO;
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.search.SearchCondConverter;
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.Entity;
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;
@@ -53,6 +56,9 @@ public class RoleDataBinderImpl implements RoleDataBinder {
private RoleDAO roleDAO;
@Autowired
+ private ApplicationDAO applicationDAO;
+
+ @Autowired
private EntityFactory entityFactory;
private void setDynMembership(final Role role, final String dynMembershipFIQL) {
@@ -123,6 +129,16 @@ public class RoleDataBinderImpl implements RoleDataBinder {
setDynMembership(role, roleTO.getDynMembershipCond());
}
+ role.getPrivileges().clear();
+ for (String key : roleTO.getPrivileges()) {
+ Privilege privilege = applicationDAO.findPrivilege(key);
+ if (privilege == null) {
+ LOG.debug("Invalid privilege {}, ignoring", key);
+ } else {
+ role.add(privilege);
+ }
+ }
+
return role;
}
@@ -134,15 +150,18 @@ public class RoleDataBinderImpl implements RoleDataBinder {
roleTO.getEntitlements().addAll(role.getEntitlements());
roleTO.getRealms().addAll(role.getRealms().stream().
- map(r -> r.getFullPath()).collect(Collectors.toList()));
+ map(Realm::getFullPath).collect(Collectors.toList()));
roleTO.getDynRealms().addAll(role.getDynRealms().stream().
- map(r -> r.getKey()).collect(Collectors.toList()));
+ map(Entity::getKey).collect(Collectors.toList()));
if (role.getDynMembership() != null) {
roleTO.setDynMembershipCond(role.getDynMembership().getFIQLCond());
}
+ roleTO.getPrivileges().addAll(role.getPrivileges().stream().
+ map(Entity::getKey).collect(Collectors.toList()));
+
return roleTO;
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
----------------------------------------------------------------------
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 c082555..6cffa6b 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
@@ -603,7 +603,15 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
userTO.getDynRealms().addAll(userDAO.findDynRealms(user.getKey()));
// roles
- userTO.getRoles().addAll(user.getRoles().stream().map(r -> r.getKey()).collect(Collectors.toList()));
+ userTO.getRoles().addAll(user.getRoles().stream().map(Entity::getKey).collect(Collectors.toList()));
+
+ // dynamic roles
+ userTO.getDynRoles().addAll(
+ userDAO.findDynRoles(user.getKey()).stream().map(Entity::getKey).collect(Collectors.toList()));
+
+ // privileges
+ userTO.getPrivileges().addAll(userDAO.findAllRoles(user).stream().
+ flatMap(role -> role.getPrivileges().stream()).map(Entity::getKey).collect(Collectors.toSet()));
// relationships
userTO.getRelationships().addAll(
@@ -622,9 +630,6 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
}).collect(Collectors.toList()));
// dynamic memberships
- userTO.getDynRoles().addAll(
- userDAO.findDynRoles(user.getKey()).stream().map(Entity::getKey).collect(Collectors.toList()));
-
userTO.getDynMemberships().addAll(
userDAO.findDynGroups(user.getKey()).stream().map(group -> {
return new MembershipTO.Builder().
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ApplicationServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ApplicationServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ApplicationServiceImpl.java
new file mode 100644
index 0000000..ed944da
--- /dev/null
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ApplicationServiceImpl.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.rest.cxf.service;
+
+import java.net.URI;
+import java.util.List;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.to.ApplicationTO;
+import org.apache.syncope.common.lib.to.PrivilegeTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.ApplicationService;
+import org.apache.syncope.core.logic.ApplicationLogic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ApplicationServiceImpl extends AbstractServiceImpl implements ApplicationService {
+
+ @Autowired
+ private ApplicationLogic logic;
+
+ @Override
+ public List<ApplicationTO> list() {
+ return logic.list();
+ }
+
+ @Override
+ public ApplicationTO read(final String key) {
+ return logic.read(key);
+ }
+
+ @Override
+ public PrivilegeTO readPrivilege(final String key) {
+ return logic.readPrivilege(key);
+ }
+
+ @Override
+ public Response create(final ApplicationTO applicationTO) {
+ ApplicationTO created = logic.create(applicationTO);
+ URI location = uriInfo.getAbsolutePathBuilder().path(created.getKey()).build();
+ return Response.created(location).
+ header(RESTHeaders.RESOURCE_KEY, created.getKey()).
+ build();
+ }
+
+ @Override
+ public void update(final ApplicationTO applicationTO) {
+ logic.update(applicationTO);
+ }
+
+ @Override
+ public void delete(final String key) {
+ logic.delete(key);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserSelfServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserSelfServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserSelfServiceImpl.java
index 82d9cbc..4a08852 100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserSelfServiceImpl.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/UserSelfServiceImpl.java
@@ -59,9 +59,9 @@ public class UserSelfServiceImpl extends AbstractServiceImpl implements UserSelf
public Response read() {
Pair<String, UserTO> self = logic.selfRead();
return Response.ok().
- header(RESTHeaders.RESOURCE_KEY, self.getValue().getKey()).
- header(RESTHeaders.OWNED_ENTITLEMENTS, self.getKey()).
- entity(self.getValue()).
+ header(RESTHeaders.RESOURCE_KEY, self.getRight().getKey()).
+ header(RESTHeaders.OWNED_ENTITLEMENTS, self.getLeft()).
+ entity(self.getRight()).
build();
}
@@ -74,7 +74,7 @@ public class UserSelfServiceImpl extends AbstractServiceImpl implements UserSelf
@Override
public Response update(final UserTO user) {
Pair<String, UserTO> self = logic.selfRead();
- return update(AnyOperations.diff(user, self.getValue(), false));
+ return update(AnyOperations.diff(user, self.getRight(), false));
}
@Override
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
index e51b53f..c3f296f 100644
--- a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
+++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
@@ -28,6 +28,7 @@ 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.Any;
+import org.apache.syncope.core.persistence.api.entity.Entity;
import org.apache.syncope.core.persistence.api.entity.PlainAttr;
import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
import org.apache.syncope.core.persistence.api.entity.group.Group;
@@ -147,6 +148,10 @@ public class ElasticsearchUtils {
map(r -> r.getKey()).collect(Collectors.toList());
builder = builder.field("roles", roles);
+ Set<Object> privileges = userDAO.findAllRoles(user).stream().
+ flatMap(role -> role.getPrivileges().stream()).map(Entity::getKey).collect(Collectors.toSet());
+ builder = builder.field("privileges", privileges);
+
List<Object> memberships = new ArrayList<>(userDAO.findAllGroupKeys(user));
builder = builder.field("memberships", memberships);
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
index 2a60c6a..de070f1 100644
--- a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
+++ b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
@@ -39,6 +39,7 @@ 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.OrderByClause;
+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;
@@ -211,6 +212,8 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
builder = getQueryBuilder(cond.getAssignableCond());
} else if (cond.getRoleCond() != null && AnyTypeKind.USER == kind) {
builder = getQueryBuilder(cond.getRoleCond());
+ } else if (cond.getPrivilegeCond() != null && AnyTypeKind.USER == kind) {
+ builder = getQueryBuilder(cond.getPrivilegeCond());
} else if (cond.getDynRealmCond() != null) {
builder = getQueryBuilder(cond.getDynRealmCond());
} else if (cond.getMemberCond() != null && AnyTypeKind.GROUP == kind) {
@@ -306,6 +309,10 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
return QueryBuilders.termQuery("roles", cond.getRole());
}
+ private QueryBuilder getQueryBuilder(final PrivilegeCond cond) {
+ return QueryBuilders.termQuery("privileges", cond.getPrivilege());
+ }
+
private QueryBuilder getQueryBuilder(final DynRealmCond cond) {
return QueryBuilders.termQuery("dynRealms", cond.getDynRealm());
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/fit/core-reference/pom.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index 5284b9e..05555a9 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -199,6 +199,30 @@ under the License.
<artifactId>bcpkix-jdk15on</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.seleniumhq.selenium</groupId>
+ <artifactId>selenium-java</artifactId>
+ <scope>test</scope>
+ <version>2.44.0</version>
+ </dependency>
+ <dependency>
+ <groupId>com.opera</groupId>
+ <artifactId>operadriver</artifactId>
+ <scope>test</scope>
+ <version>1.5</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.seleniumhq.selenium</groupId>
+ <artifactId>selenium-remote-driver</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ <version>4.11</version>
+ </dependency>
</dependencies>
<build>
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index 08d626b..7143545 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -69,6 +69,7 @@ import org.apache.syncope.common.rest.api.RESTHeaders;
import org.apache.syncope.common.rest.api.service.AnyObjectService;
import org.apache.syncope.common.rest.api.service.AnyTypeClassService;
import org.apache.syncope.common.rest.api.service.AnyTypeService;
+import org.apache.syncope.common.rest.api.service.ApplicationService;
import org.apache.syncope.common.rest.api.service.CamelRouteService;
import org.apache.syncope.common.rest.api.service.ConfigurationService;
import org.apache.syncope.common.rest.api.service.ConnectorHistoryService;
@@ -184,6 +185,8 @@ public abstract class AbstractITCase {
protected static DomainService domainService;
+ protected static ApplicationService applicationService;
+
protected static AnyTypeClassService anyTypeClassService;
protected static AnyTypeService anyTypeService;
@@ -280,6 +283,7 @@ public abstract class AbstractITCase {
syncopeService = adminClient.getService(SyncopeService.class);
domainService = adminClient.getService(DomainService.class);
+ applicationService = adminClient.getService(ApplicationService.class);
anyTypeClassService = adminClient.getService(AnyTypeClassService.class);
anyTypeService = adminClient.getService(AnyTypeService.class);
relationshipTypeService = adminClient.getService(RelationshipTypeService.class);
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ApplicationITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ApplicationITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ApplicationITCase.java
new file mode 100644
index 0000000..5cc79b0
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ApplicationITCase.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.fit.core;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.Base64;
+import java.util.UUID;
+import java.util.stream.Collectors;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.ApplicationTO;
+import org.apache.syncope.common.lib.to.EntityTO;
+import org.apache.syncope.common.lib.to.PrivilegeTO;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.rest.api.service.ApplicationService;
+import org.apache.syncope.common.rest.api.service.RoleService;
+import org.apache.syncope.fit.AbstractITCase;
+import org.junit.jupiter.api.Test;
+
+public class ApplicationITCase extends AbstractITCase {
+
+ @Test
+ public void read() {
+ ApplicationTO mightyApp = applicationService.read("mightyApp");
+ assertNotNull(mightyApp);
+ assertEquals(2, mightyApp.getPrivileges().size());
+ assertTrue(mightyApp.getPrivileges().stream().anyMatch(privilege -> "postMighty".equals(privilege.getKey())));
+
+ PrivilegeTO getMighty = applicationService.readPrivilege("getMighty");
+ assertNotNull(getMighty);
+ assertEquals("mightyApp", getMighty.getApplication());
+
+ RoleTO role = roleService.read("Other");
+ assertFalse(role.getPrivileges().isEmpty());
+ assertEquals(1, role.getPrivileges().size());
+ assertTrue(role.getPrivileges().stream().anyMatch(privilege -> "postMighty".equals(privilege)));
+ }
+
+ @Test
+ public void crud() {
+ // 1. create application
+ ApplicationTO application = new ApplicationTO();
+ application.setKey(UUID.randomUUID().toString());
+
+ PrivilegeTO privilegeTO = new PrivilegeTO();
+ privilegeTO.setKey(UUID.randomUUID().toString());
+ privilegeTO.setSpecMimeType("application/xml");
+ privilegeTO.setSpec(Base64.getEncoder().encodeToString("<one/>".getBytes()));
+ application.getPrivileges().add(privilegeTO);
+
+ privilegeTO = new PrivilegeTO();
+ privilegeTO.setKey(UUID.randomUUID().toString());
+ privilegeTO.setSpecMimeType("application/xml");
+ privilegeTO.setSpec(Base64.getEncoder().encodeToString("<one><two/></one>".getBytes()));
+ application.getPrivileges().add(privilegeTO);
+
+ privilegeTO = new PrivilegeTO();
+ privilegeTO.setKey(UUID.randomUUID().toString());
+ privilegeTO.setSpecMimeType("application/xml");
+ privilegeTO.setSpec(Base64.getEncoder().encodeToString("<one><two><three/></two></one>".getBytes()));
+ application.getPrivileges().add(privilegeTO);
+
+ Response response = applicationService.create(application);
+ assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatusInfo().getStatusCode());
+
+ application = getObject(response.getLocation(), ApplicationService.class, ApplicationTO.class);
+ assertNotNull(application);
+ assertNull(application.getDescription());
+ assertEquals(3, application.getPrivileges().size());
+
+ // 2. update application
+ application.setDescription("A description");
+ application.getPrivileges().remove(1);
+
+ applicationService.update(application);
+
+ application = applicationService.read(application.getKey());
+ assertNotNull(application);
+ assertNotNull(application.getDescription());
+ assertEquals(2, application.getPrivileges().size());
+
+ // 3. assign application's privileges to a new role
+ RoleTO role = new RoleTO();
+ role.setKey("privileged");
+ role.getPrivileges().addAll(
+ application.getPrivileges().stream().map(EntityTO::getKey).collect(Collectors.toList()));
+
+ response = roleService.create(role);
+ assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatusInfo().getStatusCode());
+
+ role = getObject(response.getLocation(), RoleService.class, RoleTO.class);
+ assertNotNull(role);
+ assertEquals(2, role.getPrivileges().size());
+
+ // 4. delete application => delete privileges
+ applicationService.delete(application.getKey());
+
+ try {
+ applicationService.read(application.getKey());
+ fail("This should not happen");
+ } catch (SyncopeClientException e) {
+ assertEquals(ClientExceptionType.NotFound, e.getType());
+ }
+
+ role = roleService.read(role.getKey());
+ assertNotNull(role);
+ assertTrue(role.getPrivileges().isEmpty());
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthenticationITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthenticationITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthenticationITCase.java
index 6951a60..1c3eba7 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthenticationITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuthenticationITCase.java
@@ -104,21 +104,21 @@ public class AuthenticationITCase extends AbstractITCase {
// 2. as anonymous
Pair<Map<String, Set<String>>, UserTO> self = clientFactory.create(
new AnonymousAuthenticationHandler(ANONYMOUS_UNAME, ANONYMOUS_KEY)).self();
- assertEquals(1, self.getKey().size());
- assertTrue(self.getKey().keySet().contains(StandardEntitlement.ANONYMOUS));
- assertEquals(ANONYMOUS_UNAME, self.getValue().getUsername());
+ assertEquals(1, self.getLeft().size());
+ assertTrue(self.getLeft().keySet().contains(StandardEntitlement.ANONYMOUS));
+ assertEquals(ANONYMOUS_UNAME, self.getRight().getUsername());
// 3. as admin
self = adminClient.self();
- assertEquals(syncopeService.platform().getEntitlements().size(), self.getKey().size());
- assertFalse(self.getKey().keySet().contains(StandardEntitlement.ANONYMOUS));
- assertEquals(ADMIN_UNAME, self.getValue().getUsername());
+ assertEquals(syncopeService.platform().getEntitlements().size(), self.getLeft().size());
+ assertFalse(self.getLeft().keySet().contains(StandardEntitlement.ANONYMOUS));
+ assertEquals(ADMIN_UNAME, self.getRight().getUsername());
// 4. as user
self = clientFactory.create("bellini", ADMIN_PWD).self();
- assertFalse(self.getKey().isEmpty());
- assertFalse(self.getKey().keySet().contains(StandardEntitlement.ANONYMOUS));
- assertEquals("bellini", self.getValue().getUsername());
+ assertFalse(self.getLeft().isEmpty());
+ assertFalse(self.getLeft().keySet().contains(StandardEntitlement.ANONYMOUS));
+ assertEquals("bellini", self.getRight().getUsername());
}
@Test
@@ -404,7 +404,7 @@ public class AuthenticationITCase extends AbstractITCase {
assertEquals("active", userTO.getStatus());
SyncopeClient goodPwdClient = clientFactory.create(userTO.getUsername(), "password123");
- assertEquals(0, goodPwdClient.self().getValue().getFailedLogins().intValue());
+ assertEquals(0, goodPwdClient.self().getRight().getFailedLogins().intValue());
}
@Test
@@ -507,8 +507,8 @@ public class AuthenticationITCase extends AbstractITCase {
Pair<Map<String, Set<String>>, UserTO> self =
clientFactory.create(userTO.getUsername(), "password123").self();
assertNotNull(self);
- assertNotNull(self.getKey());
- assertNotNull(self.getValue());
+ assertNotNull(self.getLeft());
+ assertNotNull(self.getRight());
}
@Test
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RoleITCase.java
----------------------------------------------------------------------
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 730c7fa..b349d60 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
@@ -29,6 +29,7 @@ import javax.ws.rs.core.Response;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.common.lib.types.StandardEntitlement;
import org.apache.syncope.common.rest.api.service.RoleService;
@@ -122,20 +123,26 @@ public class RoleITCase extends AbstractITCase {
@Test
public void dynMembership() {
- assertTrue(userService.read("c9b2dec2-00a7-4855-97c0-d854842b4b24").getDynRoles().isEmpty());
+ UserTO bellini = userService.read("bellini");
+ assertTrue(bellini.getDynRoles().isEmpty());
+ assertTrue(bellini.getPrivileges().isEmpty());
RoleTO role = getSampleRoleTO("dynMembership");
+ role.getPrivileges().add("getMighty");
role.setDynMembershipCond("cool==true");
Response response = roleService.create(role);
role = getObject(response.getLocation(), RoleService.class, RoleTO.class);
assertNotNull(role);
- assertTrue(userService.read(
- "c9b2dec2-00a7-4855-97c0-d854842b4b24").getDynRoles().contains(role.getKey()));
+ bellini = userService.read("bellini");
+ assertTrue(bellini.getDynRoles().contains(role.getKey()));
+ assertTrue(bellini.getPrivileges().contains("getMighty"));
role.setDynMembershipCond("cool==false");
roleService.update(role);
- assertTrue(userService.read("c9b2dec2-00a7-4855-97c0-d854842b4b24").getDynMemberships().isEmpty());
+ bellini = userService.read("bellini");
+ assertTrue(bellini.getDynMemberships().isEmpty());
+ assertTrue(bellini.getPrivileges().isEmpty());
}
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
index e159778..bc743ab 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
@@ -171,6 +171,19 @@ public class SearchITCase extends AbstractITCase {
}
@Test
+ public void searchByPrivilege() {
+ PagedResult<UserTO> matchingUsers = userService.search(
+ new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
+ fiql(SyncopeClient.getUserSearchConditionBuilder().withPrivileges("postMighty").query()).
+ build());
+ assertNotNull(matchingUsers);
+ assertFalse(matchingUsers.getResult().isEmpty());
+
+ assertTrue(matchingUsers.getResult().stream().
+ anyMatch(user -> "1417acbe-cbf6-4277-9372-e75e04f97000".equals(user.getKey())));
+ }
+
+ @Test
public void searchByDynRole() {
RoleTO role = RoleITCase.getSampleRoleTO("dynMembership");
role.setDynMembershipCond("cool==true");
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserITCase.java
index f962361..490859b 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserITCase.java
@@ -122,6 +122,13 @@ public class UserITCase extends AbstractITCase {
}
@Test
+ public void readPrivileges() {
+ Set<String> privileges = userService.read("rossini").getPrivileges();
+ assertNotNull(privileges);
+ assertEquals(1, privileges.size());
+ }
+
+ @Test
public void createUserWithNoPropagation() {
// create a new user
UserTO userTO = getUniqueSampleTO("xxx@xxx.xxx");
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java
index 565b043..541f905 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java
@@ -141,7 +141,7 @@ public class UserSelfITCase extends AbstractITCase {
}
Pair<Map<String, Set<String>>, UserTO> self = clientFactory.create("rossini", ADMIN_PWD).self();
- assertEquals("rossini", self.getValue().getUsername());
+ assertEquals("rossini", self.getRight().getUsername());
}
@Test
@@ -152,7 +152,7 @@ public class UserSelfITCase extends AbstractITCase {
assertNotNull(userId);
Pair<Map<String, Set<String>>, UserTO> self = clientFactory.create(userId, ADMIN_PWD).self();
- assertEquals(rossini.getUsername(), self.getValue().getUsername());
+ assertEquals(rossini.getUsername(), self.getRight().getUsername());
}
@Test
@@ -248,7 +248,7 @@ public class UserSelfITCase extends AbstractITCase {
@Test
public void issueSYNCOPE373() {
- UserTO userTO = adminClient.self().getValue();
+ UserTO userTO = adminClient.self().getRight();
assertEquals(ADMIN_UNAME, userTO.getUsername());
}
@@ -272,7 +272,7 @@ public class UserSelfITCase extends AbstractITCase {
// 2. verify that new user is able to authenticate
SyncopeClient authClient = clientFactory.create(user.getUsername(), "password123");
- UserTO read = authClient.self().getValue();
+ UserTO read = authClient.self().getRight();
assertNotNull(read);
// 3. request password reset (as anonymous) providing the expected security answer
@@ -301,7 +301,7 @@ public class UserSelfITCase extends AbstractITCase {
// 6. verify that password was reset and token removed
authClient = clientFactory.create(user.getUsername(), "newPassword123");
- read = authClient.self().getValue();
+ read = authClient.self().getRight();
assertNotNull(read);
assertNull(read.getToken());
@@ -323,7 +323,7 @@ public class UserSelfITCase extends AbstractITCase {
// 2. verify that new user is able to authenticate
SyncopeClient authClient = clientFactory.create(user.getUsername(), "password123");
- UserTO read = authClient.self().getValue();
+ UserTO read = authClient.self().getRight();
assertNotNull(read);
// 3. request password reset (as anonymous) with no security answer
@@ -346,7 +346,7 @@ public class UserSelfITCase extends AbstractITCase {
// 6. verify that password was reset and token removed
authClient = clientFactory.create(user.getUsername(), "newPassword123");
- read = authClient.self().getValue();
+ read = authClient.self().getRight();
assertNotNull(read);
assertNull(read.getToken());
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/src/main/asciidoc/reference-guide/concepts/concepts.adoc
----------------------------------------------------------------------
diff --git a/src/main/asciidoc/reference-guide/concepts/concepts.adoc b/src/main/asciidoc/reference-guide/concepts/concepts.adoc
index b5ac17d..a4c1fd8 100644
--- a/src/main/asciidoc/reference-guide/concepts/concepts.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/concepts.adoc
@@ -28,6 +28,8 @@ include::realms.adoc[]
include::entitlements.adoc[]
+include::privileges.adoc[]
+
include::roles.adoc[]
include::provisioning/provisioning.adoc[]
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/src/main/asciidoc/reference-guide/concepts/entitlements.adoc
----------------------------------------------------------------------
diff --git a/src/main/asciidoc/reference-guide/concepts/entitlements.adoc b/src/main/asciidoc/reference-guide/concepts/entitlements.adoc
index 89b9b3f..7bfa9e6 100644
--- a/src/main/asciidoc/reference-guide/concepts/entitlements.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/entitlements.adoc
@@ -18,7 +18,7 @@
//
=== Entitlements
-Entitlements are basically strings describing the right to perform an operation.
+Entitlements are basically strings describing the right to perform an operation on Syncope.
The components in the <<logic,logic layer>> are annotated with
http://projects.spring.io/spring-security/[Spring Security^] to implement declarative security; in the following
@@ -60,4 +60,6 @@ ifeval::["{snapshotOrRelease}" == "snapshot"]
https://github.com/apache/syncope/blob/master/ext/camel/common-lib/src/main/java/org/apache/syncope/common/lib/types/CamelEntitlement.java[enlarge the initial list^]
endif::[]
: this is because entitlements are the pillars of the internal security model and are not meant for external usage.
+
+If you need to model the rights that Users own on external applications, look at <<privileges,privileges>>, instead.
====
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/src/main/asciidoc/reference-guide/concepts/privileges.adoc
----------------------------------------------------------------------
diff --git a/src/main/asciidoc/reference-guide/concepts/privileges.adoc b/src/main/asciidoc/reference-guide/concepts/privileges.adoc
new file mode 100644
index 0000000..77fbfa5
--- /dev/null
+++ b/src/main/asciidoc/reference-guide/concepts/privileges.adoc
@@ -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.
+//
+=== Privileges
+
+Privileges model the rights that Users own on external applications.
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/src/main/asciidoc/reference-guide/concepts/roles.adoc
----------------------------------------------------------------------
diff --git a/src/main/asciidoc/reference-guide/concepts/roles.adoc b/src/main/asciidoc/reference-guide/concepts/roles.adoc
index f45c0a1..f1fe2f5 100644
--- a/src/main/asciidoc/reference-guide/concepts/roles.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/roles.adoc
@@ -21,6 +21,8 @@
Roles map a set of <<entitlements,entitlements>> to a set of <<realms,realms>> and / or
<<dynamic-realms, dynamic realms>>.
+In addition, Roles can be used to assign <<privileges,privileges>> to Users.
+
[TIP]
.Static and Dynamic Memberships
====
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/src/main/asciidoc/reference-guide/workingwithapachesyncope/restfulservices.adoc
----------------------------------------------------------------------
diff --git a/src/main/asciidoc/reference-guide/workingwithapachesyncope/restfulservices.adoc b/src/main/asciidoc/reference-guide/workingwithapachesyncope/restfulservices.adoc
index 426338a..e12dfc7 100644
--- a/src/main/asciidoc/reference-guide/workingwithapachesyncope/restfulservices.adoc
+++ b/src/main/asciidoc/reference-guide/workingwithapachesyncope/restfulservices.adoc
@@ -56,7 +56,7 @@ based on owned <<entitlements,entitlements>>.
[NOTE]
Users can examine their own entitlements looking at the `<<x-syncope-entitlements,X-Syncope-Entitlements>>`
-header value.
+header value, and their own privileges looking at the `<<x-syncope-privileges,X-Syncope-Privileges>>` header value.
[TIP]
====
@@ -196,6 +196,11 @@ Groups and Any Objects operations.
When invoking the REST endpoint `/users/self` in `GET`, the `X-Syncope-Entitlements` response header will list all
the <<entitlements,entitlements>> owned by the requesting user.
+===== X-Syncope-Privileges
+
+When invoking the REST endpoint `/users/self` in `GET`, the `X-Syncope-Privileges` response header will list all
+the <<privileges,privileges>> owned by the requesting user.
+
==== Bulk Operations
Some REST endpoints feature the _bulk mode_, e.g. the capability to perform a given operation onto several items at the
[2/2] syncope git commit: [SYNCOPE-1821] Implementation on Core
completed; still missing console + doc
Posted by il...@apache.org.
[SYNCOPE-1821] Implementation on Core completed; still missing console + doc
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/425f9b9e
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/425f9b9e
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/425f9b9e
Branch: refs/heads/master
Commit: 425f9b9ed1a26fa016d8799ccb611c24cf25a04a
Parents: 5724a50
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Mar 15 14:10:26 2018 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Mar 15 14:10:26 2018 +0100
----------------------------------------------------------------------
.../syncope/client/lib/SyncopeClient.java | 7 +-
.../syncope/common/lib/search/SpecialAttr.java | 4 +
.../search/UserFiqlSearchConditionBuilder.java | 24 ++++
.../syncope/common/lib/search/UserProperty.java | 4 +
.../syncope/common/lib/to/ApplicationTO.java | 69 ++++++++++
.../syncope/common/lib/to/PrivilegeTO.java | 83 +++++++++++
.../apache/syncope/common/lib/to/RoleTO.java | 9 ++
.../apache/syncope/common/lib/to/UserTO.java | 11 ++
.../common/lib/types/ClientExceptionType.java | 1 +
.../common/lib/types/StandardEntitlement.java | 10 ++
.../rest/api/service/ApplicationService.java | 132 ++++++++++++++++++
.../rest/api/service/UserSelfService.java | 47 ++++---
.../common/rest/api/service/UserService.java | 24 ++--
.../syncope/core/logic/ApplicationLogic.java | 138 +++++++++++++++++++
.../apache/syncope/core/logic/UserLogic.java | 3 +-
.../persistence/api/dao/ApplicationDAO.java | 39 ++++++
.../core/persistence/api/dao/RoleDAO.java | 3 +
.../api/dao/search/PrivilegeCond.java | 39 ++++++
.../persistence/api/dao/search/SearchCond.java | 25 +++-
.../persistence/api/entity/Application.java | 35 +++++
.../core/persistence/api/entity/Privilege.java | 38 +++++
.../core/persistence/api/entity/Role.java | 4 +
.../api/search/SearchCondVisitor.java | 7 +
.../api/search/SearchCondConverterTest.java | 13 ++
.../jpa/content/XMLContentLoader.java | 4 +-
.../persistence/jpa/dao/JPAAnySearchDAO.java | 43 +++++-
.../persistence/jpa/dao/JPAApplicationDAO.java | 84 +++++++++++
.../core/persistence/jpa/dao/JPARoleDAO.java | 46 ++++---
.../core/persistence/jpa/dao/SearchSupport.java | 8 ++
.../persistence/jpa/entity/JPAApplication.java | 71 ++++++++++
.../jpa/entity/JPAEntityFactory.java | 6 +
.../persistence/jpa/entity/JPAPrivilege.java | 90 ++++++++++++
.../core/persistence/jpa/entity/JPARole.java | 20 +++
.../src/main/resources/views.xml | 14 ++
.../persistence/jpa/inner/AnySearchTest.java | 15 ++
.../persistence/jpa/inner/ApplicationTest.java | 119 ++++++++++++++++
.../test/resources/domains/MasterContent.xml | 11 ++
.../api/data/ApplicationDataBinder.java | 35 +++++
.../java/data/ApplicationDataBinderImpl.java | 124 +++++++++++++++++
.../java/data/RoleDataBinderImpl.java | 23 +++-
.../java/data/UserDataBinderImpl.java | 13 +-
.../cxf/service/ApplicationServiceImpl.java | 72 ++++++++++
.../rest/cxf/service/UserSelfServiceImpl.java | 8 +-
.../client/ElasticsearchUtils.java | 5 +
.../jpa/dao/ElasticsearchAnySearchDAO.java | 7 +
fit/core-reference/pom.xml | 24 ++++
.../org/apache/syncope/fit/AbstractITCase.java | 4 +
.../syncope/fit/core/ApplicationITCase.java | 132 ++++++++++++++++++
.../syncope/fit/core/AuthenticationITCase.java | 24 ++--
.../org/apache/syncope/fit/core/RoleITCase.java | 15 +-
.../apache/syncope/fit/core/SearchITCase.java | 13 ++
.../org/apache/syncope/fit/core/UserITCase.java | 7 +
.../apache/syncope/fit/core/UserSelfITCase.java | 14 +-
.../reference-guide/concepts/concepts.adoc | 2 +
.../reference-guide/concepts/entitlements.adoc | 4 +-
.../reference-guide/concepts/privileges.adoc | 21 +++
.../reference-guide/concepts/roles.adoc | 2 +
.../restfulservices.adoc | 7 +-
58 files changed, 1758 insertions(+), 98 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
----------------------------------------------------------------------
diff --git a/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java b/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
index 2a66f14..7675f0a 100644
--- a/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
+++ b/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
@@ -57,6 +57,8 @@ public class SyncopeClient {
private static final String HEADER_SPLIT_PROPERTY = "org.apache.cxf.http.header.split";
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
private final MediaType mediaType;
private final JAXRSClientFactoryBean restClientFactory;
@@ -244,7 +246,6 @@ public class SyncopeClient {
}
}
- @SuppressWarnings("unchecked")
public Pair<Map<String, Set<String>>, UserTO> self() {
// Explicitly disable header value split because it interferes with JSON deserialization below
UserSelfService service = getService(UserSelfService.class);
@@ -260,9 +261,9 @@ public class SyncopeClient {
try {
return Pair.of(
- (Map<String, Set<String>>) new ObjectMapper().readValue(
+ OBJECT_MAPPER.readValue(
response.getHeaderString(RESTHeaders.OWNED_ENTITLEMENTS),
- new TypeReference<HashMap<String, Set<String>>>() {
+ new TypeReference<Map<String, Set<String>>>() {
}),
response.readEntity(UserTO.class));
} catch (IOException e) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java b/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
index 1a90376..80858e8 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
@@ -55,6 +55,10 @@ public enum SpecialAttr {
*/
ROLES("$roles"),
/**
+ * Applies to users.
+ */
+ PRIVILEGES("$privileges"),
+ /**
* Applies to users, groups and any objects.
*/
DYNREALMS("$dynRealms"),
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java b/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java
index 8cd7421..88cdc6b 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserFiqlSearchConditionBuilder.java
@@ -88,6 +88,18 @@ public class UserFiqlSearchConditionBuilder extends AbstractFiqlSearchConditionB
notInRoles(role, moreRoles);
}
+ public CompleteCondition withPrivileges(final String privilege, final String... morePrivileges) {
+ return newBuilderInstance().
+ is(SpecialAttr.PRIVILEGES.toString()).
+ withPrivileges(privilege, morePrivileges);
+ }
+
+ public CompleteCondition withoutPrivileges(final String privilege, final String... morePrivileges) {
+ return newBuilderInstance().
+ is(SpecialAttr.PRIVILEGES.toString()).
+ withoutPrivileges(privilege, morePrivileges);
+ }
+
protected static class Builder extends AbstractFiqlSearchConditionBuilder.Builder
implements UserProperty, CompleteCondition {
@@ -153,5 +165,17 @@ public class UserFiqlSearchConditionBuilder extends AbstractFiqlSearchConditionB
this.result = SpecialAttr.ROLES.toString();
return condition(FiqlParser.NEQ, role, (Object[]) moreRoles);
}
+
+ @Override
+ public CompleteCondition withPrivileges(final String privilege, final String... morePrivileges) {
+ this.result = SpecialAttr.PRIVILEGES.toString();
+ return condition(FiqlParser.EQ, privilege, (Object[]) morePrivileges);
+ }
+
+ @Override
+ public CompleteCondition withoutPrivileges(final String privilege, final String... morePrivileges) {
+ this.result = SpecialAttr.PRIVILEGES.toString();
+ return condition(FiqlParser.NEQ, privilege, (Object[]) morePrivileges);
+ }
}
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserProperty.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserProperty.java b/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserProperty.java
index 4c717f9..7ac5be6 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserProperty.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/search/UserProperty.java
@@ -38,4 +38,8 @@ public interface UserProperty extends SyncopeProperty {
CompleteCondition notInRoles(String role, String... moreRoles);
+ CompleteCondition withPrivileges(String privilege, String... morePrivileges);
+
+ CompleteCondition withoutPrivileges(String privilege, String... morePrivileges);
+
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/common/lib/src/main/java/org/apache/syncope/common/lib/to/ApplicationTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/ApplicationTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ApplicationTO.java
new file mode 100644
index 0000000..798c0be
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/ApplicationTO.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.common.lib.to;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.ArrayList;
+import java.util.List;
+import javax.ws.rs.PathParam;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+
+@XmlRootElement(name = "application")
+@XmlType
+public class ApplicationTO extends AbstractBaseBean implements EntityTO {
+
+ private static final long serialVersionUID = -4117796727736925215L;
+
+ private String key;
+
+ private String description;
+
+ private final List<PrivilegeTO> privileges = new ArrayList<>();
+
+ @Override
+ public String getKey() {
+ return key;
+ }
+
+ @PathParam("key")
+ @Override
+ public void setKey(final String key) {
+ this.key = key;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(final String description) {
+ this.description = description;
+ }
+
+ @XmlElementWrapper(name = "privileges")
+ @XmlElement(name = "privilege")
+ @JsonProperty("privileges")
+ public List<PrivilegeTO> getPrivileges() {
+ return privileges;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/common/lib/src/main/java/org/apache/syncope/common/lib/to/PrivilegeTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/PrivilegeTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/PrivilegeTO.java
new file mode 100644
index 0000000..5c9ed89
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/PrivilegeTO.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.common.lib.to;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+
+@XmlRootElement(name = "privilege")
+@XmlType
+public class PrivilegeTO extends AbstractBaseBean implements EntityTO {
+
+ private static final long serialVersionUID = 5461846770586031758L;
+
+ private String key;
+
+ private String description;
+
+ private String application;
+
+ private String specMimeType;
+
+ private String spec;
+
+ @Override
+ public String getKey() {
+ return key;
+ }
+
+ @Override
+ public void setKey(final String key) {
+ this.key = key;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(final String description) {
+ this.description = description;
+ }
+
+ public String getApplication() {
+ return application;
+ }
+
+ public void setApplication(final String application) {
+ this.application = application;
+ }
+
+ public String getSpecMimeType() {
+ return specMimeType;
+ }
+
+ public void setSpecMimeType(final String specMimeType) {
+ this.specMimeType = specMimeType;
+ }
+
+ public String getSpec() {
+ return spec;
+ }
+
+ public void setSpec(final String specification) {
+ this.spec = specification;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java
index 9313349..a474b6b 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/RoleTO.java
@@ -46,6 +46,8 @@ public class RoleTO extends AbstractBaseBean implements EntityTO {
private String dynMembershipCond;
+ private final Set<String> privileges = new HashSet<>();
+
@Override
public String getKey() {
return key;
@@ -86,4 +88,11 @@ public class RoleTO extends AbstractBaseBean implements EntityTO {
this.dynMembershipCond = dynMembershipCond;
}
+ @XmlElementWrapper(name = "privileges")
+ @XmlElement(name = "privilege")
+ @JsonProperty("privileges")
+ public Set<String> getPrivileges() {
+ return privileges;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/common/lib/src/main/java/org/apache/syncope/common/lib/to/UserTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/UserTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/UserTO.java
index 0341f48..66873ee 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/UserTO.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/UserTO.java
@@ -23,8 +23,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.ArrayList;
import java.util.Date;
+import java.util.HashSet;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
@@ -46,6 +48,8 @@ public class UserTO extends AnyTO implements GroupableRelatableTO {
private final List<String> dynRoles = new ArrayList<>();
+ private final Set<String> privileges = new HashSet<>();
+
private String token;
private Date tokenExpireTime;
@@ -112,6 +116,13 @@ public class UserTO extends AnyTO implements GroupableRelatableTO {
return dynRoles;
}
+ @XmlElementWrapper(name = "privileges")
+ @XmlElement(name = "privilege")
+ @JsonProperty("privileges")
+ public Set<String> getPrivileges() {
+ return privileges;
+ }
+
@Schema(readOnly = true)
public String getToken() {
return token;
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
index 7807272..ada4a1e 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
@@ -31,6 +31,7 @@ public enum ClientExceptionType {
EntityExists(Response.Status.CONFLICT),
GenericPersistence(Response.Status.BAD_REQUEST),
HasChildren(Response.Status.BAD_REQUEST),
+ InvalidPrivilege(Response.Status.BAD_REQUEST),
InvalidImplementation(Response.Status.BAD_REQUEST),
InvalidSecurityAnswer(Response.Status.BAD_REQUEST),
InvalidEntity(Response.Status.BAD_REQUEST),
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
index 12066f8..00a6638 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
@@ -86,6 +86,16 @@ public final class StandardEntitlement {
public static final String ROLE_DELETE = "ROLE_DELETE";
+ public static final String APPLICATION_LIST = "APPLICATION_LIST";
+
+ public static final String APPLICATION_CREATE = "APPLICATION_CREATE";
+
+ public static final String APPLICATION_READ = "APPLICATION_READ";
+
+ public static final String APPLICATION_UPDATE = "APPLICATION_UPDATE";
+
+ public static final String APPLICATION_DELETE = "APPLICATION_DELETE";
+
public static final String DYNREALM_CREATE = "DYNREALM_CREATE";
public static final String DYNREALM_READ = "DYNREALM_READ";
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ApplicationService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ApplicationService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ApplicationService.java
new file mode 100644
index 0000000..5f3c893
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ApplicationService.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.rest.api.service;
+
+import io.swagger.v3.oas.annotations.headers.Header;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import io.swagger.v3.oas.annotations.security.SecurityRequirements;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.to.ApplicationTO;
+import org.apache.syncope.common.lib.to.PrivilegeTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+
+/**
+ * REST operations for applications.
+ */
+@Tag(name = "Applications")
+@SecurityRequirements({
+ @SecurityRequirement(name = "BasicAuthentication")
+ ,
+ @SecurityRequirement(name = "Bearer") })
+@Path("applications")
+public interface ApplicationService extends JAXRSService {
+
+ /**
+ * Returns a list of all applications.
+ *
+ * @return list of all applications.
+ */
+ @GET
+ @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ List<ApplicationTO> list();
+
+ /**
+ * Returns application with matching key.
+ *
+ * @param key application key to be read
+ * @return application with matching key
+ */
+ @GET
+ @Path("{key}")
+ @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ ApplicationTO read(@NotNull @PathParam("key") String key);
+
+ /**
+ * Returns privilege with matching key.
+ *
+ * @param key privilege key to be read
+ * @return privilege with matching key
+ */
+ @GET
+ @Path("privileges/{key}")
+ @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ PrivilegeTO readPrivilege(@NotNull @PathParam("key") String key);
+
+ /**
+ * Creates a new application.
+ *
+ * @param applicationTO application to be created
+ * @return Response object featuring Location header of created application
+ */
+ @ApiResponses(
+ @ApiResponse(responseCode = "201",
+ description = "Application successfully created", headers = {
+ @Header(name = RESTHeaders.RESOURCE_KEY, schema =
+ @Schema(type = "string"),
+ description = "Key value for the entity created")
+ ,
+ @Header(name = HttpHeaders.LOCATION, schema =
+ @Schema(type = "string"),
+ description = "URL of the entity created") }))
+ @POST
+ @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ Response create(@NotNull ApplicationTO applicationTO);
+
+ /**
+ * Updates the application matching the provided key.
+ *
+ * @param applicationTO application to be stored
+ */
+ @ApiResponses(
+ @ApiResponse(responseCode = "204", description = "Operation was successful"))
+ @PUT
+ @Path("{key}")
+ @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ void update(@NotNull ApplicationTO applicationTO);
+
+ /**
+ * Deletes the application matching the provided key.
+ *
+ * @param key application key to be deleted
+ */
+ @ApiResponses(
+ @ApiResponse(responseCode = "204", description = "Operation was successful"))
+ @DELETE
+ @Path("{key}")
+ @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ void delete(@NotNull @PathParam("key") String key);
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.java
index 17ff01e..8b4db6f 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserSelfService.java
@@ -61,8 +61,8 @@ public interface UserSelfService extends JAXRSService {
* @return calling user data, including own UUID and entitlements
*/
@Operation(security = {
- @SecurityRequirement(name = "BasicAuthentication")
- , @SecurityRequirement(name = "Bearer") })
+ @SecurityRequirement(name = "BasicAuthentication"),
+ @SecurityRequirement(name = "Bearer") })
@ApiResponses(
@ApiResponse(responseCode = "200", description = "Calling user data, including own UUID and entitlements",
content =
@@ -70,8 +70,7 @@ public interface UserSelfService extends JAXRSService {
@Schema(implementation = UserTO.class)), headers = {
@Header(name = RESTHeaders.RESOURCE_KEY, schema =
@Schema(type = "string"),
- description = "UUID of the calling user")
- ,
+ description = "UUID of the calling user"),
@Header(name = RESTHeaders.OWNED_ENTITLEMENTS, schema =
@Schema(type = "string"),
description = "List of entitlements owned by the calling user")
@@ -101,11 +100,11 @@ public interface UserSelfService extends JAXRSService {
@Schema(implementation = ProvisioningResult.class)), headers = {
@Header(name = RESTHeaders.RESOURCE_KEY, schema =
@Schema(type = "string"),
- description = "UUID generated for the user created")
- , @Header(name = HttpHeaders.LOCATION, schema =
+ description = "UUID generated for the user created"),
+ @Header(name = HttpHeaders.LOCATION, schema =
@Schema(type = "string"),
- description = "URL of the user created")
- , @Header(name = RESTHeaders.PREFERENCE_APPLIED, schema =
+ description = "URL of the user created"),
+ @Header(name = RESTHeaders.PREFERENCE_APPLIED, schema =
@Schema(type = "string"),
description = "Allows the server to inform the "
+ "client about the fact that a specified preference was applied") }))
@@ -122,8 +121,8 @@ public interface UserSelfService extends JAXRSService {
* @return Response object featuring the updated user
*/
@Operation(security = {
- @SecurityRequirement(name = "BasicAuthentication")
- , @SecurityRequirement(name = "Bearer") })
+ @SecurityRequirement(name = "BasicAuthentication"),
+ @SecurityRequirement(name = "Bearer") })
@Parameter(name = RESTHeaders.PREFER, in = ParameterIn.HEADER,
description = "Allows client to specify a preference for the result to be returned from the server",
allowEmptyValue = true, schema =
@@ -133,8 +132,8 @@ public interface UserSelfService extends JAXRSService {
description = "User successfully updated enriched with propagation status information, as Entity",
content =
@Content(schema =
- @Schema(implementation = ProvisioningResult.class)))
- , @ApiResponse(responseCode = "204",
+ @Schema(implementation = ProvisioningResult.class))),
+ @ApiResponse(responseCode = "204",
description = "No content if 'Prefer: return-no-content' was specified", headers =
@Header(name = RESTHeaders.PREFERENCE_APPLIED, schema =
@Schema(type = "string"),
@@ -153,8 +152,8 @@ public interface UserSelfService extends JAXRSService {
* @return Response object featuring the updated user
*/
@Operation(security = {
- @SecurityRequirement(name = "BasicAuthentication")
- , @SecurityRequirement(name = "Bearer") })
+ @SecurityRequirement(name = "BasicAuthentication"),
+ @SecurityRequirement(name = "Bearer") })
@Parameter(name = RESTHeaders.PREFER, in = ParameterIn.HEADER,
description = "Allows client to specify a preference for the result to be returned from the server",
allowEmptyValue = true, schema =
@@ -164,8 +163,8 @@ public interface UserSelfService extends JAXRSService {
description = "User successfully updated enriched with propagation status information, as Entity",
content =
@Content(schema =
- @Schema(implementation = ProvisioningResult.class)))
- , @ApiResponse(responseCode = "204",
+ @Schema(implementation = ProvisioningResult.class))),
+ @ApiResponse(responseCode = "204",
description = "No content if 'Prefer: return-no-content' was specified", headers =
@Header(name = RESTHeaders.PREFERENCE_APPLIED, schema =
@Schema(type = "string"),
@@ -184,8 +183,8 @@ public interface UserSelfService extends JAXRSService {
* @return Response object featuring the updated user enriched with propagation status information
*/
@Operation(security = {
- @SecurityRequirement(name = "BasicAuthentication")
- , @SecurityRequirement(name = "Bearer") })
+ @SecurityRequirement(name = "BasicAuthentication"),
+ @SecurityRequirement(name = "Bearer") })
@Parameter(name = RESTHeaders.PREFER, in = ParameterIn.HEADER,
description = "Allows client to specify a preference for the result to be returned from the server",
allowEmptyValue = true, schema =
@@ -195,8 +194,8 @@ public interface UserSelfService extends JAXRSService {
description = "User successfully updated enriched with propagation status information, as Entity",
content =
@Content(schema =
- @Schema(implementation = ProvisioningResult.class)))
- , @ApiResponse(responseCode = "204",
+ @Schema(implementation = ProvisioningResult.class))),
+ @ApiResponse(responseCode = "204",
description = "No content if 'Prefer: return-no-content' was specified", headers =
@Header(name = RESTHeaders.PREFERENCE_APPLIED, schema =
@Schema(type = "string"),
@@ -214,8 +213,8 @@ public interface UserSelfService extends JAXRSService {
* @return Response object featuring the deleted user
*/
@Operation(security = {
- @SecurityRequirement(name = "BasicAuthentication")
- , @SecurityRequirement(name = "Bearer") })
+ @SecurityRequirement(name = "BasicAuthentication"),
+ @SecurityRequirement(name = "Bearer") })
@DELETE
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
Response delete();
@@ -228,8 +227,8 @@ public interface UserSelfService extends JAXRSService {
* @return Response object featuring the updated user
*/
@Operation(security = {
- @SecurityRequirement(name = "BasicAuthentication")
- , @SecurityRequirement(name = "Bearer") })
+ @SecurityRequirement(name = "BasicAuthentication"),
+ @SecurityRequirement(name = "Bearer") })
@POST
@Path("changePassword")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java
index 7c4e33a..3bb0148 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java
@@ -54,7 +54,8 @@ import org.apache.syncope.common.rest.api.beans.AnyQuery;
@Tag(name = "Users")
@SecurityRequirements({
@SecurityRequirement(name = "BasicAuthentication")
- , @SecurityRequirement(name = "Bearer") })
+ ,
+ @SecurityRequirement(name = "Bearer") })
@Path("users")
public interface UserService extends AnyService<UserTO> {
@@ -99,10 +100,12 @@ public interface UserService extends AnyService<UserTO> {
@Header(name = RESTHeaders.RESOURCE_KEY, schema =
@Schema(type = "string"),
description = "UUID generated for the user created")
- , @Header(name = HttpHeaders.LOCATION, schema =
+ ,
+ @Header(name = HttpHeaders.LOCATION, schema =
@Schema(type = "string"),
description = "URL of the user created")
- , @Header(name = RESTHeaders.PREFERENCE_APPLIED, schema =
+ ,
+ @Header(name = RESTHeaders.PREFERENCE_APPLIED, schema =
@Schema(type = "string"),
description = "Allows the server to inform the "
+ "client about the fact that a specified preference was applied") }))
@@ -139,13 +142,15 @@ public interface UserService extends AnyService<UserTO> {
content =
@Content(schema =
@Schema(implementation = ProvisioningResult.class)))
- , @ApiResponse(responseCode = "204",
+ ,
+ @ApiResponse(responseCode = "204",
description = "No content if 'Prefer: return-no-content' was specified", headers =
@Header(name = RESTHeaders.PREFERENCE_APPLIED, schema =
@Schema(type = "string"),
description = "Allows the server to inform the "
+ "client about the fact that a specified preference was applied"))
- , @ApiResponse(responseCode = "412",
+ ,
+ @ApiResponse(responseCode = "412",
description = "The ETag value provided via the 'If-Match' header does not match the latest modification"
+ " date of the entity") })
@PATCH
@@ -188,7 +193,8 @@ public interface UserService extends AnyService<UserTO> {
@Schema(type = "string"),
description = "Allows the server to inform the "
+ "client about the fact that a specified preference was applied"))
- , @ApiResponse(responseCode = "412",
+ ,
+ @ApiResponse(responseCode = "412",
description = "The ETag value provided via the 'If-Match' header does not match the latest modification"
+ " date of the entity") })
@PUT
@@ -224,13 +230,15 @@ public interface UserService extends AnyService<UserTO> {
content =
@Content(schema =
@Schema(implementation = ProvisioningResult.class)))
- , @ApiResponse(responseCode = "204",
+ ,
+ @ApiResponse(responseCode = "204",
description = "No content if 'Prefer: return-no-content' was specified", headers =
@Header(name = RESTHeaders.PREFERENCE_APPLIED, schema =
@Schema(type = "string"),
description = "Allows the server to inform the "
+ "client about the fact that a specified preference was applied"))
- , @ApiResponse(responseCode = "412",
+ ,
+ @ApiResponse(responseCode = "412",
description = "The ETag value provided via the 'If-Match' header does not match the latest modification"
+ " date of the entity") })
@POST
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/logic/src/main/java/org/apache/syncope/core/logic/ApplicationLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ApplicationLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ApplicationLogic.java
new file mode 100644
index 0000000..e25f532
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ApplicationLogic.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.to.ApplicationTO;
+import org.apache.syncope.common.lib.to.PrivilegeTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.syncope.core.persistence.api.dao.ApplicationDAO;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.entity.Application;
+import org.apache.syncope.core.persistence.api.entity.Privilege;
+import org.apache.syncope.core.provisioning.api.data.ApplicationDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class ApplicationLogic extends AbstractTransactionalLogic<ApplicationTO> {
+
+ @Autowired
+ private ApplicationDataBinder binder;
+
+ @Autowired
+ private ApplicationDAO applicationDAO;
+
+ @PreAuthorize("hasRole('" + StandardEntitlement.APPLICATION_READ + "')")
+ @Transactional(readOnly = true)
+ public ApplicationTO read(final String key) {
+ Application application = applicationDAO.find(key);
+ if (application == null) {
+ LOG.error("Could not find application '" + key + "'");
+
+ throw new NotFoundException(key);
+ }
+
+ return binder.getApplicationTO(application);
+ }
+
+ @PreAuthorize("hasRole('" + StandardEntitlement.APPLICATION_READ + "')")
+ @Transactional(readOnly = true)
+ public PrivilegeTO readPrivilege(final String key) {
+ Privilege privilege = applicationDAO.findPrivilege(key);
+ if (privilege == null) {
+ LOG.error("Could not find privilege '" + key + "'");
+
+ throw new NotFoundException(key);
+ }
+
+ return binder.getPrivilegeTO(privilege);
+ }
+
+ @PreAuthorize("hasRole('" + StandardEntitlement.APPLICATION_LIST + "')")
+ @Transactional(readOnly = true)
+ public List<ApplicationTO> list() {
+ return applicationDAO.findAll().stream().
+ map(application -> binder.getApplicationTO(application)).collect(Collectors.toList());
+ }
+
+ @PreAuthorize("hasRole('" + StandardEntitlement.APPLICATION_CREATE + "')")
+ public ApplicationTO create(final ApplicationTO applicationTO) {
+ return binder.getApplicationTO(applicationDAO.save(binder.create(applicationTO)));
+ }
+
+ @PreAuthorize("hasRole('" + StandardEntitlement.APPLICATION_UPDATE + "')")
+ public ApplicationTO update(final ApplicationTO applicationTO) {
+ Application application = applicationDAO.find(applicationTO.getKey());
+ if (application == null) {
+ LOG.error("Could not find application '" + applicationTO.getKey() + "'");
+ throw new NotFoundException(applicationTO.getKey());
+ }
+
+ return binder.getApplicationTO(applicationDAO.save(binder.update(application, applicationTO)));
+ }
+
+ @PreAuthorize("hasRole('" + StandardEntitlement.APPLICATION_DELETE + "')")
+ public ApplicationTO delete(final String key) {
+ Application application = applicationDAO.find(key);
+ if (application == null) {
+ LOG.error("Could not find application '" + key + "'");
+
+ throw new NotFoundException(key);
+ }
+
+ ApplicationTO deleted = binder.getApplicationTO(application);
+ applicationDAO.delete(key);
+ return deleted;
+ }
+
+ @Override
+ protected ApplicationTO resolveReference(final Method method, final Object... args)
+ throws UnresolvedReferenceException {
+
+ String key = null;
+
+ if (ArrayUtils.isNotEmpty(args)) {
+ for (int i = 0; key == null && i < args.length; i++) {
+ if (args[i] instanceof String) {
+ key = (String) args[i];
+ } else if (args[i] instanceof ApplicationTO) {
+ key = ((ApplicationTO) args[i]).getKey();
+ }
+ }
+ }
+
+ if (key != null) {
+ try {
+ return binder.getApplicationTO(applicationDAO.find(key));
+ } catch (Throwable ignore) {
+ LOG.debug("Unresolved reference", ignore);
+ throw new UnresolvedReferenceException(ignore);
+ }
+ }
+
+ throw new UnresolvedReferenceException();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
index cfc92f9..d094652 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
@@ -27,7 +27,6 @@ import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.patch.BooleanReplacePatchItem;
@@ -81,7 +80,7 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserPatch> {
@PreAuthorize("isAuthenticated()")
@Transactional(readOnly = true)
public Pair<String, UserTO> selfRead() {
- return ImmutablePair.of(
+ return Pair.of(
POJOHelper.serialize(AuthContextUtils.getAuthorizations()),
binder.returnUserTO(binder.getAuthenticatedUserTO()));
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ApplicationDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ApplicationDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ApplicationDAO.java
new file mode 100644
index 0000000..e46667c
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ApplicationDAO.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.dao;
+
+import java.util.List;
+import org.apache.syncope.core.persistence.api.entity.Application;
+import org.apache.syncope.core.persistence.api.entity.Privilege;
+
+public interface ApplicationDAO extends DAO<Application> {
+
+ Application find(String key);
+
+ Privilege findPrivilege(String key);
+
+ List<Application> findAll();
+
+ Application save(Application application);
+
+ void delete(Application application);
+
+ void delete(String key);
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RoleDAO.java
----------------------------------------------------------------------
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 cc29852..0d6aea1 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
@@ -19,6 +19,7 @@
package org.apache.syncope.core.persistence.api.dao;
import java.util.List;
+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;
@@ -31,6 +32,8 @@ public interface RoleDAO extends DAO<Role> {
List<Role> findByRealm(Realm realm);
+ List<Role> findByPrivilege(Privilege privilege);
+
List<Role> findAll();
Role save(Role role);
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/PrivilegeCond.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/PrivilegeCond.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/PrivilegeCond.java
new file mode 100644
index 0000000..1647cdb
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/PrivilegeCond.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.dao.search;
+
+public class PrivilegeCond extends AbstractSearchCond {
+
+ private static final long serialVersionUID = -8095105031495519762L;
+
+ private String privilege;
+
+ public String getPrivilege() {
+ return privilege;
+ }
+
+ public void setPrivilege(final String privilege) {
+ this.privilege = privilege;
+ }
+
+ @Override
+ public final boolean isValid() {
+ return privilege != null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java
index 520fc58..bb1dfa2 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java
@@ -49,6 +49,8 @@ public class SearchCond extends AbstractSearchCond {
private RoleCond roleCond;
+ private PrivilegeCond privilegeCond;
+
private DynRealmCond dynRealmCond;
private ResourceCond resourceCond;
@@ -119,6 +121,15 @@ public class SearchCond extends AbstractSearchCond {
return nodeCond;
}
+ public static SearchCond getLeafCond(final PrivilegeCond privilegeCond) {
+ SearchCond nodeCond = new SearchCond();
+
+ nodeCond.type = Type.LEAF;
+ nodeCond.privilegeCond = privilegeCond;
+
+ return nodeCond;
+ }
+
public static SearchCond getLeafCond(final DynRealmCond dynRealmCond) {
SearchCond nodeCond = new SearchCond();
@@ -179,6 +190,12 @@ public class SearchCond extends AbstractSearchCond {
return nodeCond;
}
+ public static SearchCond getNotLeafCond(final PrivilegeCond privilegeCond) {
+ SearchCond nodeCond = getLeafCond(privilegeCond);
+ nodeCond.type = Type.NOT_LEAF;
+ return nodeCond;
+ }
+
public static SearchCond getNotLeafCond(final ResourceCond resourceCond) {
SearchCond nodeCond = getLeafCond(resourceCond);
nodeCond.type = Type.NOT_LEAF;
@@ -306,6 +323,10 @@ public class SearchCond extends AbstractSearchCond {
return roleCond;
}
+ public PrivilegeCond getPrivilegeCond() {
+ return privilegeCond;
+ }
+
public DynRealmCond getDynRealmCond() {
return dynRealmCond;
}
@@ -347,12 +368,14 @@ public class SearchCond extends AbstractSearchCond {
case NOT_LEAF:
isValid = (anyTypeCond != null || anyCond != null || attributeCond != null || dynRealmCond != null
|| relationshipCond != null || relationshipTypeCond != null || membershipCond != null
- || roleCond != null || resourceCond != null || assignableCond != null || memberCond != null)
+ || roleCond != null || privilegeCond != null || resourceCond != null
+ || assignableCond != null || memberCond != null)
&& (anyTypeCond == null || anyTypeCond.isValid())
&& (anyCond == null || anyCond.isValid())
&& (attributeCond == null || attributeCond.isValid())
&& (membershipCond == null || membershipCond.isValid())
&& (roleCond == null || roleCond.isValid())
+ && (privilegeCond == null || privilegeCond.isValid())
&& (resourceCond == null || resourceCond.isValid())
&& (memberCond == null || memberCond.isValid());
break;
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Application.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Application.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Application.java
new file mode 100644
index 0000000..525d02a
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Application.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.entity;
+
+import java.util.List;
+import java.util.Optional;
+
+public interface Application extends ProvidedKeyEntity {
+
+ String getDescription();
+
+ void setDescription(String description);
+
+ boolean add(Privilege privilege);
+
+ Optional<? extends Privilege> getPrivilege(String key);
+
+ List<? extends Privilege> getPrivileges();
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Privilege.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Privilege.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Privilege.java
new file mode 100644
index 0000000..44744e6
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Privilege.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.api.entity;
+
+public interface Privilege extends ProvidedKeyEntity {
+
+ Application getApplication();
+
+ void setApplication(Application application);
+
+ String getDescription();
+
+ void setDescription(String description);
+
+ String getSpecMimeType();
+
+ void setSpecMimeType(String specMimeType);
+
+ byte[] getSpec();
+
+ void setSpec(byte[] spec);
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java
----------------------------------------------------------------------
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 68a1e9c..619c228 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
@@ -41,4 +41,8 @@ public interface Role extends ProvidedKeyEntity {
String getConsoleLayoutInfo();
void setConsoleLayoutInfo(String consoleLayoutInfo);
+
+ boolean add(Privilege privilege);
+
+ Set<? extends Privilege> getPrivileges();
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java
index 4c8cd01..103d3d6 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java
@@ -43,6 +43,7 @@ import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
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.PrivilegeCond;
import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
@@ -159,6 +160,12 @@ public class SearchCondVisitor extends AbstractSearchConditionVisitor<SearchBean
leaf = SearchCond.getLeafCond(roleCond);
break;
+ case PRIVILEGES:
+ PrivilegeCond privilegeCond = new PrivilegeCond();
+ privilegeCond.setPrivilege(value);
+ leaf = SearchCond.getLeafCond(privilegeCond);
+ break;
+
case DYNREALMS:
DynRealmCond dynRealmCond = new DynRealmCond();
dynRealmCond.setDynRealm(value);
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java
index 10e229f..8e4946c 100644
--- a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java
+++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java
@@ -35,6 +35,7 @@ import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
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.PrivilegeCond;
import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
import org.junit.jupiter.api.Test;
@@ -199,6 +200,18 @@ public class SearchCondConverterTest {
}
@Test
+ public void privileges() {
+ String fiql = new UserFiqlSearchConditionBuilder().withPrivileges("postMighty").query();
+ assertEquals(SpecialAttr.PRIVILEGES + "==postMighty", fiql);
+
+ PrivilegeCond privilegeCond = new PrivilegeCond();
+ privilegeCond.setPrivilege("postMighty");
+ SearchCond simpleCond = SearchCond.getLeafCond(privilegeCond);
+
+ assertEquals(simpleCond, SearchCondConverter.convert(fiql));
+ }
+
+ @Test
public void dynRealms() {
String dynRealm = UUID.randomUUID().toString();
String fiql = new UserFiqlSearchConditionBuilder().inDynRealms(dynRealm).query();
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentLoader.java
----------------------------------------------------------------------
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 3dc5a05..0211404 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
@@ -108,7 +108,7 @@ public class XMLContentLoader extends AbstractContentDealer implements ContentLo
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
Properties views = PropertiesLoaderUtils.loadProperties(viewsXML.getResource());
- views.stringPropertyNames().stream().forEach(idx -> {
+ views.stringPropertyNames().stream().sorted().forEachOrdered(idx -> {
LOG.debug("[{}] Creating view {}", domain, views.get(idx).toString());
try {
jdbcTemplate.execute(views.get(idx).toString().replaceAll("\\n", " "));
@@ -126,7 +126,7 @@ public class XMLContentLoader extends AbstractContentDealer implements ContentLo
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
Properties indexes = PropertiesLoaderUtils.loadProperties(indexesXML.getResource());
- indexes.stringPropertyNames().stream().forEach(idx -> {
+ indexes.stringPropertyNames().stream().sorted().forEachOrdered(idx -> {
LOG.debug("[{}] Creating index {}", domain, indexes.get(idx).toString());
try {
jdbcTemplate.execute(indexes.get(idx).toString());
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
----------------------------------------------------------------------
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 7ffd176..7d0ba9f 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
@@ -46,6 +46,7 @@ import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
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.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.entity.Any;
@@ -70,7 +71,7 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
Set<String> realmKeys = new HashSet<>();
Set<String> dynRealmKeys = new HashSet<>();
- for (String realmPath : RealmUtils.normalize(adminRealms)) {
+ RealmUtils.normalize(adminRealms).forEach(realmPath -> {
if (realmPath.startsWith("/")) {
Realm realm = realmDAO.findByFullPath(realmPath);
if (realm == null) {
@@ -89,7 +90,7 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
dynRealmKeys.add(dynRealm.getKey());
}
}
- }
+ });
if (!dynRealmKeys.isEmpty()) {
realmKeys.addAll(realmDAO.findAll().stream().
map(r -> r.getKey()).collect(Collectors.toSet()));
@@ -366,6 +367,9 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
} else if (cond.getRoleCond() != null && AnyTypeKind.USER == svs.anyTypeKind) {
query.append(getQuery(cond.getRoleCond(),
cond.getType() == SearchCond.Type.NOT_LEAF, parameters, svs));
+ } else if (cond.getPrivilegeCond() != null && AnyTypeKind.USER == svs.anyTypeKind) {
+ query.append(getQuery(cond.getPrivilegeCond(),
+ cond.getType() == SearchCond.Type.NOT_LEAF, parameters, svs));
} else if (cond.getDynRealmCond() != null) {
query.append(getQuery(cond.getDynRealmCond(),
cond.getType() == SearchCond.Type.NOT_LEAF, parameters, svs));
@@ -550,6 +554,37 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
}
private String getQuery(
+ final PrivilegeCond cond, final boolean not, final List<Object> parameters, final SearchSupport svs) {
+
+ StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ").
+ append(svs.field().name).append(" WHERE (");
+
+ if (not) {
+ query.append("any_id NOT IN (");
+ } else {
+ query.append("any_id IN (");
+ }
+
+ query.append("SELECT DISTINCT any_id FROM ").
+ append(svs.priv().name).append(" WHERE ").
+ append("privilege_id=?").append(setParameter(parameters, cond.getPrivilege())).
+ append(") ");
+
+ if (not) {
+ query.append("AND any_id NOT IN (");
+ } else {
+ query.append("OR any_id IN (");
+ }
+
+ query.append("SELECT DISTINCT any_id FROM ").
+ append(svs.dynpriv().name).append(" WHERE ").
+ append("privilege_id=?").append(setParameter(parameters, cond.getPrivilege())).
+ append("))");
+
+ return query.toString();
+ }
+
+ private String getQuery(
final DynRealmCond cond, final boolean not, final List<Object> parameters, final SearchSupport svs) {
StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ").
@@ -609,9 +644,9 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ").
append(svs.field().name).append(" WHERE (");
if (cond.isFromGroup()) {
- for (Realm current : realmDAO.findDescendants(realm)) {
+ realmDAO.findDescendants(realm).forEach(current -> {
query.append("realm_id=?").append(setParameter(parameters, current.getKey())).append(" OR ");
- }
+ });
query.setLength(query.length() - 4);
} else {
for (Realm current = realm; current.getParent() != null; current = current.getParent()) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAApplicationDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAApplicationDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAApplicationDAO.java
new file mode 100644
index 0000000..dd6958d
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAApplicationDAO.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.jpa.dao;
+
+import java.util.List;
+import javax.persistence.TypedQuery;
+import org.apache.syncope.core.persistence.api.dao.ApplicationDAO;
+import org.apache.syncope.core.persistence.api.dao.RoleDAO;
+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.entity.JPAApplication;
+import org.apache.syncope.core.persistence.jpa.entity.JPAPrivilege;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class JPAApplicationDAO extends AbstractDAO<Application> implements ApplicationDAO {
+
+ @Autowired
+ private RoleDAO roleDAO;
+
+ @Override
+ public Application find(final String key) {
+ return entityManager().find(JPAApplication.class, key);
+ }
+
+ @Override
+ public Privilege findPrivilege(final String key) {
+ return entityManager().find(JPAPrivilege.class, key);
+ }
+
+ @Override
+ public List<Application> findAll() {
+ TypedQuery<Application> query = entityManager().createQuery(
+ "SELECT e FROM " + JPAApplication.class.getSimpleName() + " e ", Application.class);
+ return query.getResultList();
+ }
+
+ @Override
+ public Application save(final Application application) {
+ return entityManager().merge(application);
+ }
+
+ @Override
+ public void delete(final Application application) {
+ application.getPrivileges().forEach(privilege -> {
+ roleDAO.findByPrivilege(privilege).forEach(role -> {
+ role.getPrivileges().remove(privilege);
+ });
+
+ privilege.setApplication(null);
+ });
+ application.getPrivileges().clear();
+
+ entityManager().remove(application);
+ }
+
+ @Override
+ public void delete(final String key) {
+ Application application = find(key);
+ if (application == null) {
+ return;
+ }
+
+ delete(application);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
index 367ee8d..dda23b2 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
@@ -27,6 +27,7 @@ import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
import org.apache.syncope.core.persistence.api.dao.RoleDAO;
import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+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;
@@ -80,6 +81,15 @@ public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO {
}
@Override
+ public List<Role> findByPrivilege(final Privilege privilege) {
+ TypedQuery<Role> query = entityManager().createQuery(
+ "SELECT e FROM " + JPARole.class.getSimpleName() + " e WHERE :privilege MEMBER OF e.privileges",
+ Role.class);
+ query.setParameter("privilege", privilege);
+ return query.getResultList();
+ }
+
+ @Override
public List<Role> findAll() {
TypedQuery<Role> query = entityManager().createQuery(
"SELECT e FROM " + JPARole.class.getSimpleName() + " e ", Role.class);
@@ -97,14 +107,14 @@ public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO {
clearDynMembers(merged);
- for (User user : matching) {
+ matching.forEach((user) -> {
Query insert = entityManager().createNativeQuery("INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?)");
insert.setParameter(1, user.getKey());
insert.setParameter(2, merged.getKey());
insert.executeUpdate();
publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, user, AuthContextUtils.getDomain()));
- }
+ });
}
return merged;
@@ -116,10 +126,10 @@ public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO {
"SELECT e FROM " + JPAUser.class.getSimpleName() + " e WHERE :role MEMBER OF e.roles", User.class);
query.setParameter("role", role);
- for (User user : query.getResultList()) {
+ query.getResultList().forEach(user -> {
user.getRoles().remove(role);
publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, user, AuthContextUtils.getDomain()));
- }
+ });
clearDynMembers(role);
@@ -166,22 +176,20 @@ public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO {
@Transactional
@Override
public void refreshDynMemberships(final User user) {
- for (Role role : findAll()) {
- if (role.getDynMembership() != null) {
- Query delete = entityManager().createNativeQuery(
- "DELETE FROM " + DYNMEMB_TABLE + " WHERE role_id=? AND any_id=?");
- delete.setParameter(1, role.getKey());
- delete.setParameter(2, user.getKey());
- delete.executeUpdate();
-
- if (searchDAO().matches(user, SearchCondConverter.convert(role.getDynMembership().getFIQLCond()))) {
- Query insert = entityManager().createNativeQuery("INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?)");
- insert.setParameter(1, user.getKey());
- insert.setParameter(2, role.getKey());
- insert.executeUpdate();
- }
+ findAll().stream().filter(role -> role.getDynMembership() != null).forEach(role -> {
+ Query delete = entityManager().createNativeQuery(
+ "DELETE FROM " + DYNMEMB_TABLE + " WHERE role_id=? AND any_id=?");
+ delete.setParameter(1, role.getKey());
+ delete.setParameter(2, user.getKey());
+ delete.executeUpdate();
+
+ if (searchDAO().matches(user, SearchCondConverter.convert(role.getDynMembership().getFIQLCond()))) {
+ Query insert = entityManager().createNativeQuery("INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?)");
+ insert.setParameter(1, user.getKey());
+ insert.setParameter(2, role.getKey());
+ insert.executeUpdate();
}
- }
+ });
}
@Override
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java
----------------------------------------------------------------------
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 5cac5bb..aa65aea 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
@@ -131,6 +131,14 @@ class SearchSupport {
return new SearchView("svr", field().name + "_role");
}
+ public SearchView priv() {
+ return new SearchView("svp", field().name + "_priv");
+ }
+
+ public SearchView dynpriv() {
+ return new SearchView("svdp", field().name + "_dynpriv");
+ }
+
public SearchView dynrolemembership() {
return new SearchView("svdr", JPARoleDAO.DYNMEMB_TABLE);
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAApplication.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..0b5f093
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAApplication.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.jpa.entity;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import org.apache.syncope.core.persistence.api.entity.Application;
+import org.apache.syncope.core.persistence.api.entity.Privilege;
+
+@Entity
+@Table(name = JPAApplication.TABLE)
+public class JPAApplication extends AbstractProvidedKeyEntity implements Application {
+
+ private static final long serialVersionUID = -5951400197744722305L;
+
+ public static final String TABLE = "Application";
+
+ private String description;
+
+ @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "application")
+ private List<JPAPrivilege> 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, JPAPrivilege.class);
+ return privileges.contains((JPAPrivilege) privilege) || privileges.add((JPAPrivilege) privilege);
+ }
+
+ @Override
+ public Optional<? extends Privilege> getPrivilege(final String key) {
+ return privileges.stream().filter(privilege -> privilege.getKey().equals(key)).findFirst();
+ }
+
+ @Override
+ public List<? extends Privilege> getPrivileges() {
+ return privileges;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
----------------------------------------------------------------------
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 fe3a037..aa2809a 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
@@ -30,6 +30,7 @@ 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.ConnInstance;
import org.apache.syncope.core.persistence.api.entity.ConnInstanceHistoryConf;
import org.apache.syncope.core.persistence.api.entity.ConnPoolConf;
@@ -128,6 +129,7 @@ import org.apache.syncope.core.persistence.jpa.entity.resource.JPAOrgUnit;
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.Implementation;
+import org.apache.syncope.core.persistence.api.entity.Privilege;
import org.apache.syncope.core.persistence.api.entity.policy.CorrelationRule;
import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
import org.apache.syncope.core.persistence.api.entity.resource.OrgUnitItem;
@@ -171,6 +173,10 @@ public class JPAEntityFactory implements EntityFactory {
result = (E) new JPAAnyObject();
} else if (reference.equals(Role.class)) {
result = (E) new JPARole();
+ } else if (reference.equals(Application.class)) {
+ result = (E) new JPAApplication();
+ } else if (reference.equals(Privilege.class)) {
+ result = (E) new JPAPrivilege();
} else if (reference.equals(User.class)) {
result = (E) new JPAUser();
} else if (reference.equals(Group.class)) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/425f9b9e/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPrivilege.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..a01bb5f
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPrivilege.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.jpa.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.Lob;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.core.persistence.api.entity.Application;
+import org.apache.syncope.core.persistence.api.entity.Privilege;
+
+@Entity
+@Table(name = JPAPrivilege.TABLE)
+public class JPAPrivilege extends AbstractProvidedKeyEntity implements Privilege {
+
+ private static final long serialVersionUID = -6479069294944858456L;
+
+ public static final String TABLE = "Privilege";
+
+ @ManyToOne
+ private JPAApplication application;
+
+ private String description;
+
+ @NotNull
+ private String specMimeType;
+
+ @Lob
+ private byte[] spec;
+
+ @Override
+ public Application getApplication() {
+ return application;
+ }
+
+ @Override
+ public void setApplication(final Application application) {
+ checkType(application, JPAApplication.class);
+ this.application = (JPAApplication) application;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public void setDescription(final String description) {
+ this.description = description;
+ }
+
+ @Override
+ public String getSpecMimeType() {
+ return specMimeType;
+ }
+
+ @Override
+ public void setSpecMimeType(final String specMimeType) {
+ this.specMimeType = specMimeType;
+ }
+
+ @Override
+ public byte[] getSpec() {
+ return spec;
+ }
+
+ @Override
+ public void setSpec(final byte[] spec) {
+ this.spec = ArrayUtils.clone(spec);
+ }
+
+}