http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/DynRoleMembership.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/DynRoleMembership.java
 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/DynRoleMembership.java
new file mode 100644
index 0000000..5b253d8
--- /dev/null
+++ 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/DynRoleMembership.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.entity.user;
+
+import org.apache.syncope.core.persistence.api.entity.DynMembership;
+import org.apache.syncope.core.persistence.api.entity.Role;
+
+public interface DynRoleMembership extends DynMembership<User> {
+
+    Role getRole();
+
+    void setRole(Role role);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDerAttr.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDerAttr.java
 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDerAttr.java
index 9c88907..b3ebdec 100644
--- 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDerAttr.java
+++ 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDerAttr.java
@@ -20,12 +20,6 @@ package org.apache.syncope.core.persistence.api.entity.user;
 
 import org.apache.syncope.core.persistence.api.entity.DerAttr;
 
-public interface UDerAttr extends DerAttr {
-
-    @Override
-    User getOwner();
-
-    @Override
-    UDerSchema getSchema();
+public interface UDerAttr extends DerAttr<User> {
 
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDerSchema.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDerSchema.java
 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDerSchema.java
deleted file mode 100644
index f9c75f0..0000000
--- 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDerSchema.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.api.entity.user;
-
-import org.apache.syncope.core.persistence.api.entity.DerSchema;
-
-public interface UDerSchema extends DerSchema {
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDynGroupMembership.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDynGroupMembership.java
 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDynGroupMembership.java
new file mode 100644
index 0000000..efec3f1
--- /dev/null
+++ 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDynGroupMembership.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, 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.user;
+
+import org.apache.syncope.core.persistence.api.entity.DynGroupMembership;
+
+public interface UDynGroupMembership extends DynGroupMembership<User> {
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDynMembership.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDynMembership.java
 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDynMembership.java
new file mode 100644
index 0000000..7901a6e
--- /dev/null
+++ 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UDynMembership.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, 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.user;
+
+import org.apache.syncope.core.persistence.api.entity.DynMembership;
+
+public interface UDynMembership extends DynMembership<User> {
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UMapping.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UMapping.java
 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UMapping.java
deleted file mode 100644
index f99b4bc..0000000
--- 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UMapping.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.api.entity.user;
-
-import org.apache.syncope.core.persistence.api.entity.Mapping;
-
-public interface UMapping extends Mapping<UMappingItem> {
-
-    UMappingItem getPasswordItem();
-
-    boolean setPasswordItem(UMappingItem item);
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UMappingItem.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UMappingItem.java
 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UMappingItem.java
deleted file mode 100644
index a21aa0a..0000000
--- 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UMappingItem.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.api.entity.user;
-
-import org.apache.syncope.core.persistence.api.entity.Mapping;
-import org.apache.syncope.core.persistence.api.entity.MappingItem;
-
-public interface UMappingItem extends MappingItem {
-
-    @Override
-    Mapping<UMappingItem> getMapping();
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UMembership.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UMembership.java
 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UMembership.java
new file mode 100644
index 0000000..76f9ef6
--- /dev/null
+++ 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UMembership.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, 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.user;
+
+import org.apache.syncope.core.persistence.api.entity.Membership;
+
+public interface UMembership extends Membership<User> {
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UPlainAttr.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UPlainAttr.java
 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UPlainAttr.java
index fa861d9..5c91acf 100644
--- 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UPlainAttr.java
+++ 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UPlainAttr.java
@@ -21,13 +21,7 @@ package org.apache.syncope.core.persistence.api.entity.user;
 import java.util.List;
 import org.apache.syncope.core.persistence.api.entity.PlainAttr;
 
-public interface UPlainAttr extends PlainAttr {
-
-    @Override
-    User getOwner();
-
-    @Override
-    UPlainSchema getSchema();
+public interface UPlainAttr extends PlainAttr<User> {
 
     @Override
     List<? extends UPlainAttrValue> getValues();

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UPlainAttrUniqueValue.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UPlainAttrUniqueValue.java
 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UPlainAttrUniqueValue.java
index db3ff47..71eb24e 100644
--- 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UPlainAttrUniqueValue.java
+++ 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UPlainAttrUniqueValue.java
@@ -25,7 +25,4 @@ public interface UPlainAttrUniqueValue extends 
PlainAttrUniqueValue {
     @Override
     UPlainAttr getAttr();
 
-    @Override
-    UPlainSchema getSchema();
-
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UPlainSchema.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UPlainSchema.java
 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UPlainSchema.java
deleted file mode 100644
index b9126af..0000000
--- 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UPlainSchema.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.api.entity.user;
-
-import org.apache.syncope.core.persistence.api.entity.PlainSchema;
-
-public interface UPlainSchema extends PlainSchema {
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/URelationship.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/URelationship.java
 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/URelationship.java
new file mode 100644
index 0000000..2a4c97b
--- /dev/null
+++ 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/URelationship.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.entity.user;
+
+import org.apache.syncope.core.persistence.api.entity.Relationship;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
+
+public interface URelationship extends Relationship<User, AnyObject> {
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UVirAttr.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UVirAttr.java
 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UVirAttr.java
index 12f1b5e..bb3ab70 100644
--- 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UVirAttr.java
+++ 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UVirAttr.java
@@ -20,12 +20,6 @@ package org.apache.syncope.core.persistence.api.entity.user;
 
 import org.apache.syncope.core.persistence.api.entity.VirAttr;
 
-public interface UVirAttr extends VirAttr {
-
-    @Override
-    User getOwner();
-
-    @Override
-    UVirSchema getSchema();
+public interface UVirAttr extends VirAttr<User> {
 
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UVirSchema.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UVirSchema.java
 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UVirSchema.java
deleted file mode 100644
index 3768d6f..0000000
--- 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/UVirSchema.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.api.entity.user;
-
-import org.apache.syncope.core.persistence.api.entity.VirSchema;
-
-public interface UVirSchema extends VirSchema {
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/User.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/User.java
 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/User.java
index 7349ecb..d896984 100644
--- 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/User.java
+++ 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/user/User.java
@@ -18,129 +18,104 @@
  */
 package org.apache.syncope.core.persistence.api.entity.user;
 
-import java.util.Collection;
 import java.util.Date;
 import java.util.List;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
+import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.Role;
-import org.apache.syncope.core.persistence.api.entity.Subject;
-import org.apache.syncope.core.persistence.api.entity.membership.Membership;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 
-public interface User extends Subject<UPlainAttr, UDerAttr, UVirAttr> {
+public interface User extends Any<UPlainAttr, UDerAttr, UVirAttr> {
 
-    boolean addRole(Role role);
+    String getUsername();
 
-    boolean addMembership(Membership membership);
+    void setUsername(String username);
 
-    boolean canDecodePassword();
+    String getToken();
 
-    boolean checkToken(String token);
+    Date getTokenExpireTime();
 
     void generateToken(int tokenLength, int tokenExpireTime);
 
-    Date getChangePwdDate();
-
-    CipherAlgorithm getCipherAlgorithm();
-
-    String getClearPassword();
-
-    Integer getFailedLogins();
-
-    Date getLastLoginDate();
-
-    List<? extends Role> getRoles();
-
-    Membership getMembership(Long groupKey);
-
-    List<? extends Membership> getMemberships();
-
-    Collection<Long> getStaticGroupKeys();
-
-    String getPassword();
-
-    List<String> getPasswordHistory();
-
-    String getSecurityAnswer();
+    void removeToken();
 
-    SecurityQuestion getSecurityQuestion();
+    boolean checkToken(String token);
 
-    String getStatus();
+    boolean hasTokenExpired();
 
-    String getToken();
+    Date getChangePwdDate();
 
-    Date getTokenExpireTime();
+    void setChangePwdDate(Date changePwdDate);
 
-    String getUsername();
+    CipherAlgorithm getCipherAlgorithm();
 
-    String getWorkflowId();
+    void setCipherAlgorithm(CipherAlgorithm cipherAlgorithm);
 
-    boolean hasTokenExpired();
+    boolean canDecodePassword();
 
-    Boolean isSuspended();
+    String getClearPassword();
 
     void removeClearPassword();
 
-    boolean removeRole(Role role);
-
-    boolean removeMembership(Membership membership);
+    String getPassword();
 
-    void removeToken();
+    void setEncodedPassword(String password, CipherAlgorithm cipherAlgoritm);
 
-    void setChangePwdDate(Date changePwdDate);
+    void setPassword(String password, CipherAlgorithm cipherAlgoritm);
 
-    void setCipherAlgorithm(CipherAlgorithm cipherAlgorithm);
+    List<String> getPasswordHistory();
 
-    void setEncodedPassword(String password, CipherAlgorithm cipherAlgoritm);
+    boolean verifyPasswordHistory(String password, int size);
 
-    void setFailedLogins(Integer failedLogins);
+    SecurityQuestion getSecurityQuestion();
 
-    void setLastLoginDate(Date lastLoginDate);
+    void setSecurityQuestion(SecurityQuestion securityQuestion);
 
-    void setPassword(String password, CipherAlgorithm cipherAlgoritm);
+    String getSecurityAnswer();
 
     void setSecurityAnswer(String securityAnswer);
 
-    void setSecurityQuestion(SecurityQuestion securityQuestion);
+    Integer getFailedLogins();
 
-    void setStatus(String status);
+    void setFailedLogins(Integer failedLogins);
 
-    void setSuspended(Boolean suspended);
+    Date getLastLoginDate();
 
-    void setUsername(String username);
+    void setLastLoginDate(Date lastLoginDate);
 
-    void setWorkflowId(String workflowId);
+    Boolean isSuspended();
 
-    boolean verifyPasswordHistory(String password, int size);
+    void setSuspended(Boolean suspended);
 
     @Override
-    boolean addPlainAttr(UPlainAttr attr);
+    boolean add(UPlainAttr attr);
 
     @Override
-    boolean removePlainAttr(UPlainAttr attr);
+    boolean remove(UPlainAttr attr);
 
     @Override
-    boolean addDerAttr(UDerAttr attr);
+    UPlainAttr getPlainAttr(String plainSchemaName);
 
     @Override
-    boolean removeDerAttr(UDerAttr derAttr);
+    List<? extends UPlainAttr> getPlainAttrs();
 
     @Override
-    boolean addVirAttr(UVirAttr attr);
+    boolean add(UDerAttr attr);
 
     @Override
-    boolean removeVirAttr(UVirAttr virAttr);
+    boolean remove(UDerAttr derAttr);
 
     @Override
-    UPlainAttr getPlainAttr(String plainSchemaName);
+    UDerAttr getDerAttr(String derSchemaName);
 
     @Override
-    List<? extends UPlainAttr> getPlainAttrs();
+    List<? extends UDerAttr> getDerAttrs();
 
     @Override
-    UDerAttr getDerAttr(String derSchemaName);
+    boolean add(UVirAttr attr);
 
     @Override
-    List<? extends UDerAttr> getDerAttrs();
+    boolean remove(UVirAttr virAttr);
 
     @Override
     UVirAttr getVirAttr(String virSchemaName);
@@ -148,4 +123,25 @@ public interface User extends Subject<UPlainAttr, 
UDerAttr, UVirAttr> {
     @Override
     List<? extends UVirAttr> getVirAttrs();
 
+    boolean add(Role role);
+
+    boolean remove(Role role);
+
+    List<? extends Role> getRoles();
+
+    boolean add(URelationship relationship);
+
+    boolean remove(URelationship relationship);
+
+    URelationship getRelationship(AnyObject rightEnd);
+
+    List<? extends URelationship> getRelationships();
+
+    boolean add(UMembership membership);
+
+    boolean remove(UMembership membership);
+
+    UMembership getMembership(Long groupKey);
+
+    List<? extends UMembership> getMemberships();
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java
index bc5f6e3..ea13072 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java
@@ -52,17 +52,21 @@ import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.core.misc.DataFormat;
 import org.apache.syncope.core.persistence.api.content.ContentExporter;
 import org.apache.syncope.core.persistence.jpa.entity.JPAReportExec;
-import org.apache.syncope.core.persistence.jpa.entity.membership.JPAMDerAttr;
-import org.apache.syncope.core.persistence.jpa.entity.membership.JPAMPlainAttr;
-import 
org.apache.syncope.core.persistence.jpa.entity.membership.JPAMPlainAttrUniqueValue;
-import 
org.apache.syncope.core.persistence.jpa.entity.membership.JPAMPlainAttrValue;
-import org.apache.syncope.core.persistence.jpa.entity.membership.JPAMVirAttr;
-import org.apache.syncope.core.persistence.jpa.entity.membership.JPAMembership;
+import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADerAttr;
+import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership;
+import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAPlainAttr;
+import 
org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAPlainAttrUniqueValue;
+import 
org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAPlainAttrValue;
+import 
org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAARelationship;
+import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAVirAttr;
+import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPATaskExec;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUDerAttr;
+import org.apache.syncope.core.persistence.jpa.entity.user.JPAUMembership;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttr;
 import 
org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrUniqueValue;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUPlainAttrValue;
+import org.apache.syncope.core.persistence.jpa.entity.user.JPAURelationship;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUVirAttr;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
 import org.springframework.jdbc.datasource.DataSourceUtils;
@@ -82,8 +86,9 @@ public class XMLContentExporter extends AbstractContentDealer 
implements Content
                 "QRTZ_", "LOGGING", JPAReportExec.TABLE, JPATaskExec.TABLE,
                 JPAUser.TABLE, JPAUPlainAttr.TABLE, JPAUPlainAttrValue.TABLE, 
JPAUPlainAttrUniqueValue.TABLE,
                 JPAUDerAttr.TABLE, JPAUVirAttr.TABLE,
-                JPAMembership.TABLE, JPAMPlainAttr.TABLE, 
JPAMPlainAttrValue.TABLE, JPAMPlainAttrUniqueValue.TABLE,
-                JPAMDerAttr.TABLE, JPAMVirAttr.TABLE
+                JPAAnyObject.TABLE, JPAAPlainAttr.TABLE, 
JPAAPlainAttrValue.TABLE, JPAAPlainAttrUniqueValue.TABLE,
+                JPAADerAttr.TABLE, JPAAVirAttr.TABLE,
+                JPAARelationship.TABLE, JPAAMembership.TABLE, 
JPAURelationship.TABLE, JPAUMembership.TABLE
             }));
 
     protected static final Set<String> TABLE_SUFFIXES_TO_BE_INCLUDED =

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
new file mode 100644
index 0000000..3448737
--- /dev/null
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
@@ -0,0 +1,434 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, 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 static org.apache.syncope.core.persistence.jpa.dao.AbstractDAO.LOG;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+import javax.persistence.NoResultException;
+import javax.persistence.Query;
+import javax.persistence.TemporalType;
+import org.apache.commons.jexl2.parser.Parser;
+import org.apache.commons.jexl2.parser.ParserConstants;
+import org.apache.commons.jexl2.parser.Token;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
+import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.DerSchema;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.api.entity.VirAttr;
+import 
org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.persistence.jpa.entity.AbstractPlainAttrValue;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public abstract class AbstractAnyDAO<A extends Any<?, ?, ?>> extends 
AbstractDAO<A, Long> implements AnyDAO<A> {
+
+    @Autowired
+    protected PlainSchemaDAO plainSchemaDAO;
+
+    @Autowired
+    protected DerSchemaDAO derSchemaDAO;
+
+    @Autowired
+    protected AnySearchDAO searchDAO;
+
+    protected AnyUtils anyUtils;
+
+    protected abstract AnyUtils init();
+
+    protected AnyUtils getAnyUtils() {
+        synchronized (this) {
+            if (anyUtils == null) {
+                anyUtils = init();
+            }
+        }
+        return anyUtils;
+    }
+
+    protected abstract void securityChecks(A any);
+
+    @Override
+    public A authFind(final Long key) {
+        if (key == null) {
+            throw new NotFoundException("Null key");
+        }
+
+        A any = find(key);
+        if (any == null) {
+            throw new NotFoundException("Any " + key);
+        }
+
+        securityChecks(any);
+
+        return any;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public A find(final Long key) {
+        return (A) entityManager.find(getAnyUtils().anyClass(), key);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public A findByWorkflowId(final String workflowId) {
+        Query query = entityManager.createQuery("SELECT e FROM " + 
getAnyUtils().anyClass().getSimpleName()
+                + " e WHERE e.workflowId = :workflowId", User.class);
+        query.setParameter("workflowId", workflowId);
+
+        A result = null;
+        try {
+            result = (A) query.getSingleResult();
+        } catch (NoResultException e) {
+            LOG.debug("No user found with workflow id {}", workflowId, e);
+        }
+
+        return result;
+    }
+
+    private Query findByAttrValueQuery(final String entityName) {
+        return entityManager.createQuery("SELECT e FROM " + entityName + " e"
+                + " WHERE e.attribute.schema.name = :schemaName AND 
(e.stringValue IS NOT NULL"
+                + " AND e.stringValue = :stringValue)"
+                + " OR (e.booleanValue IS NOT NULL AND e.booleanValue = 
:booleanValue)"
+                + " OR (e.dateValue IS NOT NULL AND e.dateValue = :dateValue)"
+                + " OR (e.longValue IS NOT NULL AND e.longValue = :longValue)"
+                + " OR (e.doubleValue IS NOT NULL AND e.doubleValue = 
:doubleValue)");
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public List<A> findByAttrValue(final String schemaName, final 
PlainAttrValue attrValue) {
+        PlainSchema schema = plainSchemaDAO.find(schemaName);
+        if (schema == null) {
+            LOG.error("Invalid schema name '{}'", schemaName);
+            return Collections.<A>emptyList();
+        }
+
+        String entityName = schema.isUniqueConstraint()
+                ? getAnyUtils().plainAttrUniqueValueClass().getName()
+                : getAnyUtils().plainAttrValueClass().getName();
+        Query query = findByAttrValueQuery(entityName);
+        query.setParameter("schemaName", schemaName);
+        query.setParameter("stringValue", attrValue.getStringValue());
+        query.setParameter("booleanValue", attrValue.getBooleanValue() == null
+                ? null
+                : ((AbstractPlainAttrValue) 
attrValue).getBooleanAsInteger(attrValue.getBooleanValue()));
+        if (attrValue.getDateValue() == null) {
+            query.setParameter("dateValue", null);
+        } else {
+            query.setParameter("dateValue", attrValue.getDateValue(), 
TemporalType.TIMESTAMP);
+        }
+        query.setParameter("longValue", attrValue.getLongValue());
+        query.setParameter("doubleValue", attrValue.getDoubleValue());
+
+        List<A> result = new ArrayList<>();
+        for (PlainAttrValue value : (List<PlainAttrValue>) 
query.getResultList()) {
+            A any = (A) value.getAttr().getOwner();
+            if (!result.contains(any)) {
+                result.add(any);
+            }
+        }
+
+        return result;
+    }
+
+    @Override
+    public A findByAttrUniqueValue(final String schemaName, final 
PlainAttrValue attrUniqueValue) {
+        PlainSchema schema = plainSchemaDAO.find(schemaName);
+        if (schema == null) {
+            LOG.error("Invalid schema name '{}'", schemaName);
+            return null;
+        }
+        if (!schema.isUniqueConstraint()) {
+            LOG.error("This schema has not unique constraint: '{}'", 
schemaName);
+            return null;
+        }
+
+        List<A> result = findByAttrValue(schemaName, attrUniqueValue);
+        return result.isEmpty()
+                ? null
+                : result.iterator().next();
+    }
+
+    /**
+     * Split an attribute value recurring on provided literals/tokens.
+     *
+     * @param attrValue value to be split
+     * @param literals literals/tokens
+     * @return split value
+     */
+    private List<String> split(final String attrValue, final List<String> 
literals) {
+        final List<String> attrValues = new ArrayList<>();
+
+        if (literals.isEmpty()) {
+            attrValues.add(attrValue);
+        } else {
+            for (String token : 
attrValue.split(Pattern.quote(literals.get(0)))) {
+                attrValues.addAll(split(token, literals.subList(1, 
literals.size())));
+            }
+        }
+
+        return attrValues;
+    }
+
+    /**
+     * Generate one where clause for each different attribute schema into the 
derived schema expression provided.
+     *
+     * @param expression derived schema expression
+     * @param value derived attribute value
+     * @param attrUtils USER / GROUP
+     * @return where clauses to use to build the query
+     */
+    private Set<String> getWhereClause(final String expression, final String 
value) {
+        final Parser parser = new Parser(new StringReader(expression));
+
+        // Schema names
+        final List<String> identifiers = new ArrayList<>();
+
+        // Literals
+        final List<String> literals = new ArrayList<>();
+
+        // Get schema names and literals
+        for (Token token = parser.getNextToken(); token != null && 
StringUtils.isNotBlank(token.toString());
+                token = parser.getNextToken()) {
+
+            if (token.kind == ParserConstants.STRING_LITERAL) {
+                literals.add(token.toString().substring(1, 
token.toString().length() - 1));
+            }
+
+            if (token.kind == ParserConstants.IDENTIFIER) {
+                identifiers.add(token.toString());
+            }
+        }
+
+        // Sort literals in order to process later literals included into 
others
+        Collections.sort(literals, new Comparator<String>() {
+
+            @Override
+            public int compare(final String t, final String t1) {
+                if (t == null && t1 == null) {
+                    return 0;
+                } else if (t != null && t1 == null) {
+                    return -1;
+                } else if (t == null && t1 != null) {
+                    return 1;
+                } else if (t.length() == t1.length()) {
+                    return 0;
+                } else if (t.length() > t1.length()) {
+                    return -1;
+                } else {
+                    return 1;
+                }
+            }
+        });
+
+        // Split value on provided literals
+        final List<String> attrValues = split(value, literals);
+
+        if (attrValues.size() != identifiers.size()) {
+            LOG.error("Ambiguous JEXL expression resolution.");
+            throw new IllegalArgumentException("literals and values have 
different size");
+        }
+
+        // clauses to be used with INTERSECTed queries
+        final Set<String> clauses = new HashSet<>();
+
+        // builder to build the clauses
+        final StringBuilder bld = new StringBuilder();
+
+        // Contains used identifiers in order to avoid replications
+        final Set<String> used = new HashSet<>();
+
+        // Create several clauses: one for eanch identifiers
+        for (int i = 0; i < identifiers.size(); i++) {
+            if (!used.contains(identifiers.get(i))) {
+
+                // verify schema existence and get schema type
+                PlainSchema schema = plainSchemaDAO.find(identifiers.get(i));
+                if (schema == null) {
+                    LOG.error("Invalid schema name '{}'", identifiers.get(i));
+                    throw new IllegalArgumentException("Invalid schema name " 
+ identifiers.get(i));
+                }
+
+                // clear builder
+                bld.delete(0, bld.length());
+
+                bld.append("(");
+
+                // set schema name
+                bld.append("s.name = 
'").append(identifiers.get(i)).append("'");
+
+                bld.append(" AND ");
+
+                bld.append("s.name = a.schema_name").append(" AND ");
+
+                bld.append("a.id = v.attribute_id");
+
+                bld.append(" AND ");
+
+                // use a value clause different for eanch different schema type
+                switch (schema.getType()) {
+                    case Boolean:
+                        bld.append("v.booleanValue = 
'").append(attrValues.get(i)).append("'");
+                        break;
+                    case Long:
+                        bld.append("v.longValue = ").append(attrValues.get(i));
+                        break;
+                    case Double:
+                        bld.append("v.doubleValue = 
").append(attrValues.get(i));
+                        break;
+                    case Date:
+                        bld.append("v.dateValue = 
'").append(attrValues.get(i)).append("'");
+                        break;
+                    default:
+                        bld.append("v.stringValue = 
'").append(attrValues.get(i)).append("'");
+                }
+
+                bld.append(")");
+
+                used.add(identifiers.get(i));
+
+                clauses.add(bld.toString());
+            }
+        }
+
+        LOG.debug("Generated where clauses {}", clauses);
+
+        return clauses;
+    }
+
+    @Override
+    public List<A> findByDerAttrValue(final String schemaName, final String 
value) {
+        DerSchema schema = derSchemaDAO.find(schemaName);
+        if (schema == null) {
+            LOG.error("Invalid schema name '{}'", schemaName);
+            return Collections.<A>emptyList();
+        }
+
+        // query string
+        StringBuilder querystring = new StringBuilder();
+
+        boolean subquery = false;
+        for (String clause : getWhereClause(schema.getExpression(), value)) {
+            if (querystring.length() > 0) {
+                subquery = true;
+                querystring.append(" AND a.owner_id IN ( ");
+            }
+
+            querystring.append("SELECT a.owner_id ").
+                    append("FROM 
").append(getAnyUtils().plainAttrClass().getSimpleName().substring(3)).append(" 
a, ").
+                    
append(getAnyUtils().plainAttrValueClass().getSimpleName().substring(3)).append("
 v, ").
+                    append(PlainSchema.class.getSimpleName()).append(" s ").
+                    append("WHERE ").append(clause);
+
+            if (subquery) {
+                querystring.append(')');
+            }
+        }
+
+        Query query = entityManager.createNativeQuery(querystring.toString());
+
+        List<A> result = new ArrayList<>();
+        for (Object anyKey : query.getResultList()) {
+            A any = find(Long.parseLong(anyKey.toString()));
+            if (!result.contains(any)) {
+                result.add(any);
+            }
+        }
+
+        return result;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public List<A> findByResource(final ExternalResource resource) {
+        Query query = entityManager.createQuery(
+                "SELECT e FROM " + getAnyUtils().anyClass().getSimpleName() + 
" e "
+                + "WHERE :resource MEMBER OF e.resources");
+        query.setParameter("resource", resource);
+
+        return query.getResultList();
+    }
+
+    @Override
+    public final List<A> findAll(final Set<String> adminRealms,
+            final int page, final int itemsPerPage) {
+
+        return findAll(adminRealms, page, itemsPerPage, 
Collections.<OrderByClause>emptyList());
+    }
+
+    private SearchCond getAllMatchingCond() {
+        AnyCond idCond = new AnyCond(AttributeCond.Type.ISNOTNULL);
+        idCond.setSchema("id");
+        return SearchCond.getLeafCond(idCond);
+    }
+
+    @Override
+    public List<A> findAll(final Set<String> adminRealms,
+            final int page, final int itemsPerPage, final List<OrderByClause> 
orderBy) {
+
+        return searchDAO.search(adminRealms, getAllMatchingCond(), page, 
itemsPerPage, orderBy,
+                getAnyUtils().getAnyTypeKind());
+    }
+
+    @Override
+    public final int count(final Set<String> adminRealms) {
+        return searchDAO.count(adminRealms, getAllMatchingCond(), 
getAnyUtils().getAnyTypeKind());
+    }
+
+    @Override
+    public A save(final A any) {
+        A merged = entityManager.merge(any);
+        for (VirAttr<?> virAttr : merged.getVirAttrs()) {
+            virAttr.getValues().clear();
+            
virAttr.getValues().addAll(any.getVirAttr(virAttr.getSchema().getKey()).getValues());
+        }
+
+        return merged;
+    }
+
+    @Override
+    public void delete(final Long key) {
+        A any = find(key);
+        if (any == null) {
+            return;
+        }
+
+        delete(any);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractDAO.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractDAO.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractDAO.java
index 2bf2b80..a4ff529 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractDAO.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractDAO.java
@@ -37,9 +37,6 @@ import org.springframework.util.ReflectionUtils;
 @Configurable
 public abstract class AbstractDAO<E extends Entity<KEY>, KEY> implements 
DAO<E, KEY> {
 
-    /**
-     * Logger.
-     */
     protected static final Logger LOG = LoggerFactory.getLogger(DAO.class);
 
     private static final String CACHE_STORE_MODE = 
"javax.persistence.cache.storeMode";

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractSubjectDAO.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractSubjectDAO.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractSubjectDAO.java
deleted file mode 100644
index 954651a..0000000
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractSubjectDAO.java
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.jpa.dao;
-
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.regex.Pattern;
-import javax.persistence.Query;
-import javax.persistence.TemporalType;
-import org.apache.commons.jexl2.parser.Parser;
-import org.apache.commons.jexl2.parser.ParserConstants;
-import org.apache.commons.jexl2.parser.Token;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
-import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
-import org.apache.syncope.core.persistence.api.dao.SubjectDAO;
-import org.apache.syncope.core.persistence.api.dao.SubjectSearchDAO;
-import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
-import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
-import org.apache.syncope.core.persistence.api.dao.search.SubjectCond;
-import org.apache.syncope.core.persistence.api.entity.AttributableUtils;
-import org.apache.syncope.core.persistence.api.entity.DerAttr;
-import org.apache.syncope.core.persistence.api.entity.DerSchema;
-import org.apache.syncope.core.persistence.api.entity.ExternalResource;
-import org.apache.syncope.core.persistence.api.entity.PlainAttr;
-import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
-import org.apache.syncope.core.persistence.api.entity.PlainSchema;
-import org.apache.syncope.core.persistence.api.entity.Subject;
-import org.apache.syncope.core.persistence.api.entity.VirAttr;
-import org.apache.syncope.core.persistence.jpa.entity.AbstractPlainAttrValue;
-import org.springframework.beans.factory.annotation.Autowired;
-
-abstract class AbstractSubjectDAO<P extends PlainAttr, D extends DerAttr, V 
extends VirAttr>
-        extends AbstractDAO<Subject<P, D, V>, Long> implements SubjectDAO<P, 
D, V> {
-
-    @Autowired
-    protected PlainSchemaDAO plainSchemaDAO;
-
-    @Autowired
-    protected DerSchemaDAO derSchemaDAO;
-
-    @Autowired
-    protected SubjectSearchDAO searchDAO;
-
-    protected SearchCond getAllMatchingCond() {
-        SubjectCond idCond = new SubjectCond(AttributeCond.Type.ISNOTNULL);
-        idCond.setSchema("id");
-        return SearchCond.getLeafCond(idCond);
-    }
-
-    /**
-     * Split an attribute value recurring on provided literals/tokens.
-     *
-     * @param attrValue value to be split
-     * @param literals literals/tokens
-     * @return split value
-     */
-    private List<String> split(final String attrValue, final List<String> 
literals) {
-        final List<String> attrValues = new ArrayList<>();
-
-        if (literals.isEmpty()) {
-            attrValues.add(attrValue);
-        } else {
-            for (String token : 
attrValue.split(Pattern.quote(literals.get(0)))) {
-                attrValues.addAll(split(token, literals.subList(1, 
literals.size())));
-            }
-        }
-
-        return attrValues;
-    }
-
-    /**
-     * Generate one where clause for each different attribute schema into the 
derived schema expression provided.
-     *
-     * @param expression derived schema expression
-     * @param value derived attribute value
-     * @param attrUtils USER / GROUP
-     * @return where clauses to use to build the query
-     */
-    private Set<String> getWhereClause(final String expression, final String 
value, final AttributableUtils attrUtils) {
-        final Parser parser = new Parser(new StringReader(expression));
-
-        // Schema names
-        final List<String> identifiers = new ArrayList<>();
-
-        // Literals
-        final List<String> literals = new ArrayList<>();
-
-        // Get schema names and literals
-        for (Token token = parser.getNextToken(); token != null && 
StringUtils.isNotBlank(token.toString());
-                token = parser.getNextToken()) {
-
-            if (token.kind == ParserConstants.STRING_LITERAL) {
-                literals.add(token.toString().substring(1, 
token.toString().length() - 1));
-            }
-
-            if (token.kind == ParserConstants.IDENTIFIER) {
-                identifiers.add(token.toString());
-            }
-        }
-
-        // Sort literals in order to process later literals included into 
others
-        Collections.sort(literals, new Comparator<String>() {
-
-            @Override
-            public int compare(final String t, final String t1) {
-                if (t == null && t1 == null) {
-                    return 0;
-                } else if (t != null && t1 == null) {
-                    return -1;
-                } else if (t == null && t1 != null) {
-                    return 1;
-                } else if (t.length() == t1.length()) {
-                    return 0;
-                } else if (t.length() > t1.length()) {
-                    return -1;
-                } else {
-                    return 1;
-                }
-            }
-        });
-
-        // Split value on provided literals
-        final List<String> attrValues = split(value, literals);
-
-        if (attrValues.size() != identifiers.size()) {
-            LOG.error("Ambiguous JEXL expression resolution.");
-            throw new IllegalArgumentException("literals and values have 
different size");
-        }
-
-        // clauses to be used with INTERSECTed queries
-        final Set<String> clauses = new HashSet<>();
-
-        // builder to build the clauses
-        final StringBuilder bld = new StringBuilder();
-
-        // Contains used identifiers in order to avoid replications
-        final Set<String> used = new HashSet<>();
-
-        // Create several clauses: one for eanch identifiers
-        for (int i = 0; i < identifiers.size(); i++) {
-            if (!used.contains(identifiers.get(i))) {
-
-                // verify schema existence and get schema type
-                PlainSchema schema = plainSchemaDAO.find(identifiers.get(i), 
attrUtils.plainSchemaClass());
-                if (schema == null) {
-                    LOG.error("Invalid schema name '{}'", identifiers.get(i));
-                    throw new IllegalArgumentException("Invalid schema name " 
+ identifiers.get(i));
-                }
-
-                // clear builder
-                bld.delete(0, bld.length());
-
-                bld.append("(");
-
-                // set schema name
-                bld.append("s.name = 
'").append(identifiers.get(i)).append("'");
-
-                bld.append(" AND ");
-
-                bld.append("s.name = a.schema_name").append(" AND ");
-
-                bld.append("a.id = v.attribute_id");
-
-                bld.append(" AND ");
-
-                // use a value clause different for eanch different schema type
-                switch (schema.getType()) {
-                    case Boolean:
-                        bld.append("v.booleanValue = 
'").append(attrValues.get(i)).append("'");
-                        break;
-                    case Long:
-                        bld.append("v.longValue = ").append(attrValues.get(i));
-                        break;
-                    case Double:
-                        bld.append("v.doubleValue = 
").append(attrValues.get(i));
-                        break;
-                    case Date:
-                        bld.append("v.dateValue = 
'").append(attrValues.get(i)).append("'");
-                        break;
-                    default:
-                        bld.append("v.stringValue = 
'").append(attrValues.get(i)).append("'");
-                }
-
-                bld.append(")");
-
-                used.add(identifiers.get(i));
-
-                clauses.add(bld.toString());
-            }
-        }
-
-        LOG.debug("Generated where clauses {}", clauses);
-
-        return clauses;
-    }
-
-    protected abstract Subject<P, D, V> findInternal(Long key);
-
-    private Query findByAttrValueQuery(final String entityName) {
-        return entityManager.createQuery("SELECT e FROM " + entityName + " e"
-                + " WHERE e.attribute.schema.name = :schemaName AND 
(e.stringValue IS NOT NULL"
-                + " AND e.stringValue = :stringValue)"
-                + " OR (e.booleanValue IS NOT NULL AND e.booleanValue = 
:booleanValue)"
-                + " OR (e.dateValue IS NOT NULL AND e.dateValue = :dateValue)"
-                + " OR (e.longValue IS NOT NULL AND e.longValue = :longValue)"
-                + " OR (e.doubleValue IS NOT NULL AND e.doubleValue = 
:doubleValue)");
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public List<? extends Subject<P, D, V>> findByAttrValue(
-            final String schemaName, final PlainAttrValue attrValue, final 
AttributableUtils attrUtils) {
-
-        PlainSchema schema = plainSchemaDAO.find(schemaName, 
attrUtils.plainSchemaClass());
-        if (schema == null) {
-            LOG.error("Invalid schema name '{}'", schemaName);
-            return Collections.<Subject<P, D, V>>emptyList();
-        }
-
-        final String entityName = schema.isUniqueConstraint()
-                ? attrUtils.plainAttrUniqueValueClass().getName()
-                : attrUtils.plainAttrValueClass().getName();
-
-        Query query = findByAttrValueQuery(entityName);
-
-        query.setParameter("schemaName", schemaName);
-        query.setParameter("stringValue", attrValue.getStringValue());
-        query.setParameter("booleanValue", attrValue.getBooleanValue() == null
-                ? null
-                : ((AbstractPlainAttrValue) 
attrValue).getBooleanAsInteger(attrValue.getBooleanValue()));
-        if (attrValue.getDateValue() == null) {
-            query.setParameter("dateValue", null);
-        } else {
-            query.setParameter("dateValue", attrValue.getDateValue(), 
TemporalType.TIMESTAMP);
-        }
-        query.setParameter("longValue", attrValue.getLongValue());
-        query.setParameter("doubleValue", attrValue.getDoubleValue());
-
-        List<Subject<P, D, V>> result = new ArrayList<>();
-        for (PlainAttrValue value : (List<PlainAttrValue>) 
query.getResultList()) {
-            Subject<P, D, V> subject = (Subject<P, D, V>) 
value.getAttr().getOwner();
-            if (!result.contains(subject)) {
-                result.add(subject);
-            }
-        }
-
-        return result;
-    }
-
-    @Override
-    public Subject<P, D, V> findByAttrUniqueValue(
-            final String schemaName, final PlainAttrValue attrUniqueValue, 
final AttributableUtils attrUtils) {
-
-        PlainSchema schema = plainSchemaDAO.find(schemaName, 
attrUtils.plainSchemaClass());
-        if (schema == null) {
-            LOG.error("Invalid schema name '{}'", schemaName);
-            return null;
-        }
-        if (!schema.isUniqueConstraint()) {
-            LOG.error("This schema has not unique constraint: '{}'", 
schemaName);
-            return null;
-        }
-
-        List<? extends Subject<P, D, V>> result = findByAttrValue(schemaName, 
attrUniqueValue, attrUtils);
-        return result.isEmpty()
-                ? null
-                : result.iterator().next();
-    }
-
-    /**
-     * Find users / groups by derived attribute value. This method could fail 
if one or more string literals contained
-     * into the derived attribute value provided derive from identifier 
(schema name) replacement. When you are going to
-     * specify a derived attribute expression you must be quite sure that 
string literals used to build the expression
-     * cannot be found into the attribute values used to replace attribute 
schema names used as identifiers.
-     *
-     * @param schemaName derived schema name
-     * @param value derived attribute value
-     * @param attrUtils AttributableUtil
-     * @return list of users / groups
-     */
-    @Override
-    public List<? extends Subject<P, D, V>> findByDerAttrValue(
-            final String schemaName, final String value, final 
AttributableUtils attrUtils) {
-
-        DerSchema schema = derSchemaDAO.find(schemaName, 
attrUtils.derSchemaClass());
-        if (schema == null) {
-            LOG.error("Invalid schema name '{}'", schemaName);
-            return Collections.<Subject<P, D, V>>emptyList();
-        }
-
-        // query string
-        final StringBuilder querystring = new StringBuilder();
-
-        boolean subquery = false;
-        for (String clause : getWhereClause(schema.getExpression(), value, 
attrUtils)) {
-            if (querystring.length() > 0) {
-                subquery = true;
-                querystring.append(" AND a.owner_id IN ( ");
-            }
-
-            querystring.append("SELECT a.owner_id ").
-                    append("FROM 
").append(attrUtils.plainAttrClass().getSimpleName().substring(3)).append(" a, 
").
-                    
append(attrUtils.plainAttrValueClass().getSimpleName().substring(3)).append(" 
v, ").
-                    
append(attrUtils.plainSchemaClass().getSimpleName().substring(3)).append(" s ").
-                    append("WHERE ").append(clause);
-
-            if (subquery) {
-                querystring.append(')');
-            }
-        }
-
-        LOG.debug("Execute query {}", querystring);
-
-        final Query query = 
entityManager.createNativeQuery(querystring.toString());
-
-        final List<Subject<P, D, V>> result = new ArrayList<>();
-        for (Object userId : query.getResultList()) {
-            Subject<P, D, V> subject = 
findInternal(Long.parseLong(userId.toString()));
-            if (!result.contains(subject)) {
-                result.add(subject);
-            }
-        }
-
-        return result;
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public List<? extends Subject<P, D, V>> findByResource(
-            final ExternalResource resource, final AttributableUtils 
attrUtils) {
-
-        Query query = entityManager.createQuery(
-                "SELECT e FROM " + 
attrUtils.attributableClass().getSimpleName() + " e "
-                + "WHERE :resource MEMBER OF e.resources");
-        query.setParameter("resource", resource);
-
-        return query.getResultList();
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/081d9a04/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
----------------------------------------------------------------------
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
new file mode 100644
index 0000000..fd99019
--- /dev/null
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.dao;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.persistence.TypedQuery;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.commons.collections4.Transformer;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.Entitlement;
+import org.apache.syncope.core.misc.security.AuthContextUtils;
+import org.apache.syncope.core.misc.security.UnauthorizedException;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+import 
org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtilsFactory;
+import 
org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADynGroupMembership;
+import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+@Repository
+public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements 
AnyObjectDAO {
+
+    @Override
+    protected AnyUtils init() {
+        return new JPAAnyUtilsFactory().getInstance(AnyTypeKind.ANY_OBJECT);
+    }
+
+    @Override
+    protected void securityChecks(final AnyObject anyObject) {
+        Set<String> authRealms = 
AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_READ);
+        boolean authorized = CollectionUtils.exists(authRealms, new 
Predicate<String>() {
+
+            @Override
+            public boolean evaluate(final String realm) {
+                return anyObject.getRealm().getFullPath().startsWith(realm);
+            }
+        });
+        if (authRealms == null || authRealms.isEmpty() || !authorized) {
+            throw new UnauthorizedException(AnyTypeKind.ANY_OBJECT, 
anyObject.getKey());
+        }
+    }
+
+    @Override
+    public List<AnyObject> findByAnyType(final String anyTypeName) {
+        TypedQuery<AnyObject> query = entityManager.createQuery(
+                "SELECT e FROM " + JPAAnyObject.class.getSimpleName() + " e 
WHERE e.type.name=:name", AnyObject.class);
+        query.setParameter("name", anyTypeName);
+
+        return query.getResultList();
+    }
+
+    @Override
+    public void delete(final AnyObject any) {
+        for (Group group : findDynGroupMemberships(any)) {
+            group.getADynMembership().remove(any);
+        }
+
+        entityManager.remove(any);
+    }
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
+    @Override
+    public List<Group> findDynGroupMemberships(final AnyObject anyObject) {
+        TypedQuery<Group> query = entityManager.createQuery(
+                "SELECT e.group FROM " + 
JPAADynGroupMembership.class.getSimpleName()
+                + " e WHERE :anyObject MEMBER OF e.members", Group.class);
+        query.setParameter("anyObject", anyObject);
+
+        return query.getResultList();
+    }
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
+    @Override
+    public Collection<Group> findAllGroups(final AnyObject anyObject) {
+        return CollectionUtils.union(
+                CollectionUtils.collect(anyObject.getMemberships(), new 
Transformer<AMembership, Group>() {
+
+                    @Override
+                    public Group transform(final AMembership input) {
+                        return input.getRightEnd();
+                    }
+                }, new ArrayList<Group>()),
+                findDynGroupMemberships(anyObject));
+    }
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
+    @Override
+    public Collection<Long> findAllGroupKeys(final AnyObject anyObject) {
+        return CollectionUtils.collect(findAllGroups(anyObject), new 
Transformer<Group, Long>() {
+
+            @Override
+            public Long transform(final Group input) {
+                return input.getKey();
+            }
+        });
+    }
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
+    @Override
+    public Collection<ExternalResource> findAllResources(final AnyObject 
anyObject) {
+        Set<ExternalResource> result = new HashSet<>();
+        result.addAll(anyObject.getResources());
+        for (Group group : findAllGroups(anyObject)) {
+            result.addAll(group.getResources());
+        }
+
+        return result;
+    }
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
+    @Override
+    public Collection<String> findAllResourceNames(final AnyObject anyObject) {
+        return CollectionUtils.collect(findAllResources(anyObject), new 
Transformer<ExternalResource, String>() {
+
+            @Override
+            public String transform(final ExternalResource input) {
+                return input.getKey();
+            }
+        });
+    }
+
+}

Reply via email to