syncope-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ilgro...@apache.org
Subject [19/27] syncope git commit: [SYNCOPE-119] Renaming Role to Group
Date Mon, 06 Apr 2015 16:14:15 GMT
http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/lib/src/main/java/org/apache/syncope/common/lib/types/AbstractPolicySpec.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AbstractPolicySpec.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AbstractPolicySpec.java
new file mode 100644
index 0000000..5f2fd1e
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AbstractPolicySpec.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.types;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.lib.annotation.SchemaList;
+
+@XmlType
+public abstract class AbstractPolicySpec implements PolicySpec {
+
+    private static final long serialVersionUID = -6210646284287392063L;
+
+    /**
+     * Substrings not permitted.
+     */
+    private final List<String> wordsNotPermitted = new ArrayList<>();
+
+    /**
+     * User attribute values not permitted.
+     */
+    @SchemaList
+    protected final List<String> schemasNotPermitted = new ArrayList<>();
+
+    /**
+     * Substrings not permitted as prefix.
+     */
+    protected final List<String> prefixesNotPermitted = new ArrayList<>();
+
+    /**
+     * Substrings not permitted as suffix.
+     */
+    protected final List<String> suffixesNotPermitted = new ArrayList<>();
+
+    @XmlElementWrapper(name = "wordsNotPermitted")
+    @XmlElement(name = "word")
+    @JsonProperty("wordsNotPermitted")
+    public List<String> getWordsNotPermitted() {
+        return wordsNotPermitted;
+    }
+
+    @XmlElementWrapper(name = "prefixesNotPermitted")
+    @XmlElement(name = "prefix")
+    @JsonProperty("prefixesNotPermitted")
+    public List<String> getPrefixesNotPermitted() {
+        return prefixesNotPermitted;
+    }
+
+    @XmlElementWrapper(name = "schemasNotPermitted")
+    @XmlElement(name = "schema")
+    @JsonProperty("schemasNotPermitted")
+    public List<String> getSchemasNotPermitted() {
+        return schemasNotPermitted;
+    }
+
+    @XmlElementWrapper(name = "suffixesNotPermitted")
+    @XmlElement(name = "suffix")
+    @JsonProperty("suffixesNotPermitted")
+    public List<String> getSuffixesNotPermitted() {
+        return suffixesNotPermitted;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/lib/src/main/java/org/apache/syncope/common/lib/types/AccountPolicySpec.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AccountPolicySpec.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AccountPolicySpec.java
index 8d5a792..cada048 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AccountPolicySpec.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AccountPolicySpec.java
@@ -18,16 +18,10 @@
  */
 package org.apache.syncope.common.lib.types;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
-import java.util.ArrayList;
-import java.util.List;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlElementWrapper;
 import javax.xml.bind.annotation.XmlType;
-import org.apache.syncope.common.lib.annotation.SchemaList;
 
 @XmlType
-public class AccountPolicySpec implements PolicySpec {
+public class AccountPolicySpec extends AbstractPolicySpec {
 
     private static final long serialVersionUID = 3259256974414758406L;
 
@@ -47,27 +41,6 @@ public class AccountPolicySpec implements PolicySpec {
     private String pattern;
 
     /**
-     * Substrings not permitted.
-     */
-    private List<String> wordsNotPermitted;
-
-    /**
-     * User attribute values not permitted.
-     */
-    @SchemaList
-    private List<String> schemasNotPermitted;
-
-    /**
-     * Substrings not permitted as prefix.
-     */
-    private List<String> prefixesNotPermitted;
-
-    /**
-     * Substrings not permitted as suffix.
-     */
-    private List<String> suffixesNotPermitted;
-
-    /**
      * Specify if one or more lowercase characters are permitted.
      */
     private boolean allUpperCase;
@@ -130,46 +103,6 @@ public class AccountPolicySpec implements PolicySpec {
         this.pattern = pattern;
     }
 
-    @XmlElementWrapper(name = "prefixesNotPermitted")
-    @XmlElement(name = "prefix")
-    @JsonProperty("prefixesNotPermitted")
-    public List<String> getPrefixesNotPermitted() {
-        if (prefixesNotPermitted == null) {
-            prefixesNotPermitted = new ArrayList<String>();
-        }
-        return prefixesNotPermitted;
-    }
-
-    @XmlElementWrapper(name = "schemasNotPermitted")
-    @XmlElement(name = "schema")
-    @JsonProperty("schemasNotPermitted")
-    public List<String> getSchemasNotPermitted() {
-        if (schemasNotPermitted == null) {
-            schemasNotPermitted = new ArrayList<String>();
-        }
-        return schemasNotPermitted;
-    }
-
-    @XmlElementWrapper(name = "suffixesNotPermitted")
-    @XmlElement(name = "suffix")
-    @JsonProperty("suffixesNotPermitted")
-    public List<String> getSuffixesNotPermitted() {
-        if (suffixesNotPermitted == null) {
-            suffixesNotPermitted = new ArrayList<String>();
-        }
-        return suffixesNotPermitted;
-    }
-
-    @XmlElementWrapper(name = "wordsNotPermitted")
-    @XmlElement(name = "word")
-    @JsonProperty("wordsNotPermitted")
-    public List<String> getWordsNotPermitted() {
-        if (wordsNotPermitted == null) {
-            wordsNotPermitted = new ArrayList<String>();
-        }
-        return wordsNotPermitted;
-    }
-
     public boolean isPropagateSuspension() {
         return propagateSuspension;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/lib/src/main/java/org/apache/syncope/common/lib/types/AttrSchemaType.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AttrSchemaType.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AttrSchemaType.java
index 2fc937b..be78979 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AttrSchemaType.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AttrSchemaType.java
@@ -33,7 +33,7 @@ public enum AttrSchemaType {
     Encrypted(byte[].class),
     Binary(byte[].class);
 
-    final private Class<?> type;
+    private final Class<?> type;
 
     AttrSchemaType(final Class<?> type) {
         this.type = type;

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/lib/src/main/java/org/apache/syncope/common/lib/types/AttributableType.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AttributableType.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AttributableType.java
index 6b989df..fab34f0 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AttributableType.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AttributableType.java
@@ -24,7 +24,7 @@ import javax.xml.bind.annotation.XmlEnum;
 public enum AttributableType {
 
     USER,
-    ROLE,
+    GROUP,
     MEMBERSHIP,
     CONFIGURATION;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/lib/src/main/java/org/apache/syncope/common/lib/types/AuditElements.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AuditElements.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AuditElements.java
index 40ff86a..323ea09 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AuditElements.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AuditElements.java
@@ -26,9 +26,6 @@ public final class AuditElements implements Serializable {
 
     private static final long serialVersionUID = -4385059255522273254L;
 
-    private AuditElements() {
-    }
-
     @XmlEnum
     public enum EventCategoryType {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/lib/src/main/java/org/apache/syncope/common/lib/types/AuditLoggerName.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AuditLoggerName.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AuditLoggerName.java
index d491037..de0ec90 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AuditLoggerName.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AuditLoggerName.java
@@ -49,8 +49,9 @@ public class AuditLoggerName extends AbstractBaseBean {
             @JsonProperty("category") final String category,
             @JsonProperty("subcategory") final String subcategory,
             @JsonProperty("event") final String event,
-            @JsonProperty("result") final Result result)
-            throws IllegalArgumentException {
+            @JsonProperty("result") final Result result) {
+
+        super();
 
         this.type = type == null ? AuditElements.EventCategoryType.CUSTOM : type;
         this.category = category;
@@ -86,7 +87,7 @@ public class AuditLoggerName extends AbstractBaseBean {
 
     @SuppressWarnings("unchecked")
     public static AuditLoggerName fromLoggerName(final String loggerName)
-            throws IllegalArgumentException, ParseException {
+            throws ParseException {
 
         if (StringUtils.isBlank(loggerName)) {
             throw new IllegalArgumentException("Null value not permitted");
@@ -153,7 +154,7 @@ public class AuditLoggerName extends AbstractBaseBean {
             }
         }
 
-        return new AbstractMap.SimpleEntry< EventCategoryTO, Result>(eventCategoryTO, condition);
+        return new AbstractMap.SimpleEntry<>(eventCategoryTO, condition);
     }
 
     /**
@@ -183,38 +184,24 @@ public class AuditLoggerName extends AbstractBaseBean {
                 eventBuilder.append(type.name());
             }
         }
-        eventBuilder.append(']');
-
-        eventBuilder.append(":");
-
-        eventBuilder.append('[');
+        eventBuilder.append("]:[");
         if (StringUtils.isNotBlank(category)) {
             eventBuilder.append(category);
         }
-        eventBuilder.append(']');
-
-        eventBuilder.append(":");
-
-        eventBuilder.append('[');
+        eventBuilder.append("]:[");
         if (StringUtils.isNotBlank(subcategory)) {
             eventBuilder.append(subcategory);
         }
-        eventBuilder.append(']');
-
-        eventBuilder.append(":");
-
-        eventBuilder.append('[');
+        eventBuilder.append("]:[");
         if (StringUtils.isNotBlank(event)) {
             eventBuilder.append(event);
         }
         eventBuilder.append(']');
 
         if (resultValueCondition != null) {
-            eventBuilder.append(":");
-
-            eventBuilder.append('[');
-            eventBuilder.append(resultValueCondition);
-            eventBuilder.append(']');
+            eventBuilder.append(":[").
+                    append(resultValueCondition).
+                    append(']');
         }
 
         return eventBuilder.toString();

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/lib/src/main/java/org/apache/syncope/common/lib/types/CipherAlgorithm.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/CipherAlgorithm.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/CipherAlgorithm.java
index af5592e..d7be1bc 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/CipherAlgorithm.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/CipherAlgorithm.java
@@ -35,9 +35,9 @@ public enum CipherAlgorithm {
     SSHA512("S-SHA-512", false),
     BCRYPT("BCRYPT", false);
 
-    final private String algorithm;
+    private final String algorithm;
 
-    final private boolean invertible;
+    private final boolean invertible;
 
     CipherAlgorithm(final String algorithm, final boolean invertible) {
         this.algorithm = algorithm;

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/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 c55cebd..f67c3d4 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
@@ -35,10 +35,10 @@ public enum ClientExceptionType {
     InvalidConnIdConf(Response.Status.BAD_REQUEST),
     InvalidPolicy(Response.Status.BAD_REQUEST),
     InvalidConf(Response.Status.BAD_REQUEST),
-    InvalidRole(Response.Status.BAD_REQUEST),
+    InvalidGroup(Response.Status.BAD_REQUEST),
     InvalidReport(Response.Status.BAD_REQUEST),
     InvalidReportExec(Response.Status.BAD_REQUEST),
-    InvalidRoles(Response.Status.BAD_REQUEST),
+    InvalidGroups(Response.Status.BAD_REQUEST),
     InvalidSchemaDefinition(Response.Status.BAD_REQUEST),
     InvalidSearchExpression(Response.Status.BAD_REQUEST),
     InvalidPageOrSize(Response.Status.BAD_REQUEST),
@@ -59,9 +59,9 @@ public enum ClientExceptionType {
     RejectedUserCreate(Response.Status.BAD_REQUEST),
     RequiredValuesMissing(Response.Status.BAD_REQUEST),
     RESTValidation(Response.Status.BAD_REQUEST),
-    RoleOwnership(Response.Status.BAD_REQUEST),
+    GroupOwnership(Response.Status.BAD_REQUEST),
     Scheduling(Response.Status.BAD_REQUEST),
-    UnauthorizedRole(Response.Status.UNAUTHORIZED),
+    UnauthorizedGroup(Response.Status.UNAUTHORIZED),
     Unauthorized(Response.Status.UNAUTHORIZED),
     Unknown(Response.Status.BAD_REQUEST),
     Workflow(Response.Status.BAD_REQUEST);

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
index 5a92daa..30193f6 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
@@ -38,12 +38,12 @@ public enum EntityViolationType {
     InvalidPasswordPolicy("org.apache.syncope.core.persistence.validation.passwordpolicy"),
     InvalidPolicy("org.apache.syncope.core.persistence.validation.policy"),
     InvalidPropagationTask("org.apache.syncope.core.persistence.validation.propagationtask"),
-    InvalidRPlainSchema("org.apache.syncope.core.persistence.validation.attrvalue.rPlainSchema"),
-    InvalidRDerSchema("org.apache.syncope.core.persistence.validation.attrvalue.rDerSchema"),
-    InvalidRVirSchema("org.apache.syncope.core.persistence.validation.attrvalue.rVirSchema"),
+    InvalidGPlainSchema("org.apache.syncope.core.persistence.validation.attrvalue.gPlainSchema"),
+    InvalidGDerSchema("org.apache.syncope.core.persistence.validation.attrvalue.gDerSchema"),
+    InvalidGVirSchema("org.apache.syncope.core.persistence.validation.attrvalue.gVirSchema"),
     InvalidReport("org.apache.syncope.core.persistence.validation.report"),
     InvalidResource("org.apache.syncope.core.persistence.validation.externalresource"),
-    InvalidRoleOwner("org.apache.syncope.core.persistence.validation.role.owner"),
+    InvalidGroupOwner("org.apache.syncope.core.persistence.validation.group.owner"),
     InvalidSchemaEncrypted("org.apache.syncope.core.persistence.validation.schema.encrypted"),
     InvalidSchemaEnum("org.apache.syncope.core.persistence.validation.schema.enum"),
     InvalidSchemaMultivalueUnique("org.apache.syncope.core.persistence.validation.schema.multivalueUnique"),

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/lib/src/main/java/org/apache/syncope/common/lib/types/IntMappingType.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/IntMappingType.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/IntMappingType.java
index 7dd60d6..c329d72 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/IntMappingType.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/IntMappingType.java
@@ -42,14 +42,14 @@ public enum IntMappingType {
     Username(AttributableType.USER),
     Password(AttributableType.USER),
     // -------------------------
-    // Role attribute types (the same in RoleMappingType)
+    // Group attribute types (the same in GroupMappingType)
     // -------------------------
-    RolePlainSchema(AttributableType.ROLE),
-    RoleDerivedSchema(AttributableType.ROLE),
-    RoleVirtualSchema(AttributableType.ROLE),
-    RoleId(AttributableType.ROLE),
-    RoleName(AttributableType.ROLE),
-    RoleOwnerSchema(AttributableType.ROLE),
+    GroupPlainSchema(AttributableType.GROUP),
+    GroupDerivedSchema(AttributableType.GROUP),
+    GroupVirtualSchema(AttributableType.GROUP),
+    GroupId(AttributableType.GROUP),
+    GroupName(AttributableType.GROUP),
+    GroupOwnerSchema(AttributableType.GROUP),
     // -------------------------
     // Membership attribute types (the same in MembershipMappingType)
     // -------------------------
@@ -58,7 +58,7 @@ public enum IntMappingType {
     MembershipVirtualSchema(AttributableType.MEMBERSHIP),
     MembershipId(AttributableType.MEMBERSHIP);
 
-    private AttributableType attributableType;
+    private final AttributableType attributableType;
 
     private IntMappingType(final AttributableType attributableType) {
         this.attributableType = attributableType;
@@ -91,11 +91,11 @@ public enum IntMappingType {
      * @return set of attribute types.
      */
     public static Set<IntMappingType> getAttributeTypes(final AttributableType attributableType) {
-        final EnumSet<?> enumset;
+        EnumSet<?> enumset;
 
         switch (attributableType) {
-            case ROLE:
-                enumset = EnumSet.allOf(RoleMappingType.class);
+            case GROUP:
+                enumset = EnumSet.allOf(GroupMappingType.class);
                 break;
 
             case MEMBERSHIP:
@@ -105,9 +105,10 @@ public enum IntMappingType {
             case USER:
             default:
                 enumset = EnumSet.allOf(UserMappingType.class);
+                break;
         }
 
-        final Set<IntMappingType> result = new HashSet<IntMappingType>(enumset.size());
+        final Set<IntMappingType> result = new HashSet<>(enumset.size());
         for (Object obj : enumset) {
             result.add(IntMappingType.valueOf(obj.toString()));
         }
@@ -116,9 +117,8 @@ public enum IntMappingType {
     }
 
     public static Set<IntMappingType> getEmbedded() {
-        return EnumSet.of(
-                IntMappingType.UserId, IntMappingType.Username, IntMappingType.Password,
-                IntMappingType.RoleId, IntMappingType.RoleName, IntMappingType.RoleOwnerSchema,
+        return EnumSet.of(IntMappingType.UserId, IntMappingType.Username, IntMappingType.Password,
+                IntMappingType.GroupId, IntMappingType.GroupName, IntMappingType.GroupOwnerSchema,
                 IntMappingType.MembershipId);
     }
 
@@ -131,8 +131,8 @@ public enum IntMappingType {
      */
     public static boolean contains(final AttributableType attributableType, final String type) {
         switch (attributableType) {
-            case ROLE:
-                for (RoleMappingType c : RoleMappingType.values()) {
+            case GROUP:
+                for (GroupMappingType c : GroupMappingType.values()) {
                     if (c.name().equals(type)) {
                         return true;
                     }
@@ -174,16 +174,16 @@ public enum IntMappingType {
     }
 
     /**
-     * Role attribute types.
+     * Group attribute types.
      */
-    private enum RoleMappingType {
-
-        RolePlainSchema,
-        RoleDerivedSchema,
-        RoleVirtualSchema,
-        RoleId,
-        RoleName,
-        RoleOwnerSchema;
+    private enum GroupMappingType {
+
+        GroupPlainSchema,
+        GroupDerivedSchema,
+        GroupVirtualSchema,
+        GroupId,
+        GroupName,
+        GroupOwnerSchema;
 
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/lib/src/main/java/org/apache/syncope/common/lib/types/PasswordPolicySpec.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/PasswordPolicySpec.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/PasswordPolicySpec.java
index 448fceb..175a1ef 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/PasswordPolicySpec.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/PasswordPolicySpec.java
@@ -18,16 +18,10 @@
  */
 package org.apache.syncope.common.lib.types;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
-import java.util.ArrayList;
-import java.util.List;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlElementWrapper;
 import javax.xml.bind.annotation.XmlType;
-import org.apache.syncope.common.lib.annotation.SchemaList;
 
 @XmlType
-public class PasswordPolicySpec implements PolicySpec {
+public class PasswordPolicySpec extends AbstractPolicySpec {
 
     private static final long serialVersionUID = -7988778083915548547L;
 
@@ -47,17 +41,6 @@ public class PasswordPolicySpec implements PolicySpec {
     private int minLength;
 
     /**
-     * Substrings not permitted.
-     */
-    private List<String> wordsNotPermitted;
-
-    /**
-     * User attribute values not permitted.
-     */
-    @SchemaList
-    private List<String> schemasNotPermitted;
-
-    /**
      * Specify if one or more non alphanumeric characters are required.
      */
     private boolean nonAlphanumericRequired;
@@ -147,16 +130,6 @@ public class PasswordPolicySpec implements PolicySpec {
      */
     private boolean allowNullPassword;
 
-    /**
-     * Substrings not permitted as prefix.
-     */
-    private List<String> prefixesNotPermitted;
-
-    /**
-     * Substrings not permitted as suffix.
-     */
-    private List<String> suffixesNotPermitted;
-
     public boolean isDigitRequired() {
         return digitRequired;
     }
@@ -261,36 +234,6 @@ public class PasswordPolicySpec implements PolicySpec {
         this.nonAlphanumericRequired = nonAlphanumericRequired;
     }
 
-    @XmlElementWrapper(name = "prefixesNotPermitted")
-    @XmlElement(name = "prefix")
-    @JsonProperty("prefixesNotPermitted")
-    public List<String> getPrefixesNotPermitted() {
-        if (prefixesNotPermitted == null) {
-            prefixesNotPermitted = new ArrayList<String>();
-        }
-        return prefixesNotPermitted;
-    }
-
-    @XmlElementWrapper(name = "schemasNotPermitted")
-    @XmlElement(name = "schema")
-    @JsonProperty("schemasNotPermitted")
-    public List<String> getSchemasNotPermitted() {
-        if (schemasNotPermitted == null) {
-            schemasNotPermitted = new ArrayList<String>();
-        }
-        return schemasNotPermitted;
-    }
-
-    @XmlElementWrapper(name = "suffixesNotPermitted")
-    @XmlElement(name = "suffix")
-    @JsonProperty("suffixesNotPermitted")
-    public List<String> getSuffixesNotPermitted() {
-        if (suffixesNotPermitted == null) {
-            suffixesNotPermitted = new ArrayList<String>();
-        }
-        return suffixesNotPermitted;
-    }
-
     public boolean isUppercaseRequired() {
         return uppercaseRequired;
     }
@@ -299,16 +242,6 @@ public class PasswordPolicySpec implements PolicySpec {
         this.uppercaseRequired = uppercaseRequired;
     }
 
-    @XmlElementWrapper(name = "wordsNotPermitted")
-    @XmlElement(name = "word")
-    @JsonProperty("wordsNotPermitted")
-    public List<String> getWordsNotPermitted() {
-        if (wordsNotPermitted == null) {
-            wordsNotPermitted = new ArrayList<String>();
-        }
-        return wordsNotPermitted;
-    }
-
     public boolean isAlphanumericRequired() {
         return alphanumericRequired;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/lib/src/main/java/org/apache/syncope/common/lib/types/ResourceAssociationActionType.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/ResourceAssociationActionType.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ResourceAssociationActionType.java
index 7693ca3..3d7955d 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/ResourceAssociationActionType.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ResourceAssociationActionType.java
@@ -24,15 +24,15 @@ import javax.xml.bind.annotation.XmlEnum;
 public enum ResourceAssociationActionType {
 
     /**
-     * Add association between user/role on Syncope and external resource(s) without any propagation.
+     * Add association between user/group on Syncope and external resource(s) without any propagation.
      */
     LINK,
     /**
-     * Add user/role into external resource(s).
+     * Add user/group into external resource(s).
      */
     PROVISION,
     /**
-     * Assign (link + provision) external resource(s) with user/role.
+     * Assign (link + provision) external resource(s) with user/group.
      */
     ASSIGN
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/lib/src/main/java/org/apache/syncope/common/lib/types/ResourceDeassociationActionType.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/ResourceDeassociationActionType.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ResourceDeassociationActionType.java
index c88f453..aea4fa3 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/ResourceDeassociationActionType.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ResourceDeassociationActionType.java
@@ -24,15 +24,15 @@ import javax.xml.bind.annotation.XmlEnum;
 public enum ResourceDeassociationActionType {
 
     /**
-     * Remove association between user/role on Syncope and external resource(s) without any propagation.
+     * Remove association between user/group on Syncope and external resource(s) without any propagation.
      */
     UNLINK,
     /**
-     * Remove user/role from external resource(s).
+     * Remove user/group from external resource(s).
      */
     DEPROVISION,
     /**
-     * Unassign (unlink + de-provision) external resource(s) from user/role.
+     * Unassign (unlink + de-provision) external resource(s) from user/group.
      */
     UNASSIGN
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/lib/src/main/java/org/apache/syncope/common/lib/types/SubjectType.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/SubjectType.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/SubjectType.java
index 0acd314..538f19a 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/SubjectType.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/SubjectType.java
@@ -24,11 +24,11 @@ import javax.xml.bind.annotation.XmlEnum;
 public enum SubjectType {
 
     USER,
-    ROLE;
+    GROUP;
 
     public AttributableType asAttributableType() {
         return this == USER
                 ? AttributableType.USER
-                : AttributableType.ROLE;
+                : AttributableType.GROUP;
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java
index e0040de..a904209 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java
@@ -36,19 +36,19 @@ public class SyncPolicySpec implements PolicySpec {
      * User attributes and fields for matching during synchronization.
      */
     @SchemaList(extended = true)
-    private final List<String> uAltSearchSchemas = new ArrayList<String>();
+    private final List<String> uAltSearchSchemas = new ArrayList<>();
 
     @ClassList
     private String userJavaRule;
 
     /**
-     * Role attributes and fields for matching during synchronization.
+     * Group attributes and fields for matching during synchronization.
      */
     @SchemaList(extended = true)
-    private final List<String> rAltSearchSchemas = new ArrayList<String>();
+    private final List<String> gAltSearchSchemas = new ArrayList<>();
 
     @ClassList
-    private String roleJavaRule;
+    private String groupJavaRule;
 
     /**
      * Conflict resolution action.
@@ -72,19 +72,19 @@ public class SyncPolicySpec implements PolicySpec {
         return uAltSearchSchemas;
     }
 
-    @XmlElementWrapper(name = "roleAltSearchSchemas")
-    @XmlElement(name = "roleAltSearchSchema")
-    @JsonProperty("roleAltSearchSchemas")
+    @XmlElementWrapper(name = "groupAltSearchSchemas")
+    @XmlElement(name = "groupAltSearchSchema")
+    @JsonProperty("groupAltSearchSchemas")
     public List<String> getrAltSearchSchemas() {
-        return rAltSearchSchemas;
+        return gAltSearchSchemas;
     }
 
-    public String getRoleJavaRule() {
-        return roleJavaRule;
+    public String getGroupJavaRule() {
+        return groupJavaRule;
     }
 
-    public void setRoleJavaRule(final String roleJavaRule) {
-        this.roleJavaRule = roleJavaRule;
+    public void setGroupJavaRule(final String groupJavaRule) {
+        this.groupJavaRule = groupJavaRule;
     }
 
     public String getUserJavaRule() {

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/pom.xml
----------------------------------------------------------------------
diff --git a/common/pom.xml b/common/pom.xml
index a137eb5..db8b41c 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -33,6 +33,10 @@ under the License.
   <artifactId>syncope-common</artifactId>
   <packaging>pom</packaging>
 
+  <properties>
+    <rootpom.basedir>${basedir}/..</rootpom.basedir>
+  </properties>
+
   <modules>
     <module>lib</module>
     <module>rest-api</module>

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java
new file mode 100644
index 0000000..2bbe9b7
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java
@@ -0,0 +1,312 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 java.util.List;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.cxf.jaxrs.model.wadl.Description;
+import org.apache.cxf.jaxrs.model.wadl.Descriptions;
+import org.apache.cxf.jaxrs.model.wadl.DocTarget;
+import org.apache.syncope.common.lib.mod.GroupMod;
+import org.apache.syncope.common.lib.to.BulkAction;
+import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.types.ResourceAssociationActionType;
+import org.apache.syncope.common.lib.types.ResourceDeassociationActionType;
+import org.apache.syncope.common.lib.wrap.ResourceName;
+
+/**
+ * REST operations for groups.
+ */
+@Path("groups")
+public interface GroupService extends JAXRSService {
+
+    /**
+     * Returns children groups of given group.
+     *
+     * @param groupKey key of group to get children from
+     * @return children groups of given group
+     */
+    @GET
+    @Path("{groupKey}/children")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    List<GroupTO> children(@NotNull @PathParam("groupKey") Long groupKey);
+
+    /**
+     * Returns parent group of the given group (or null if no parent exists).
+     *
+     * @param groupKey key of group to get parent group from
+     * @return parent group of the given group (or null if no parent exists)
+     */
+    @GET
+    @Path("{groupKey}/parent")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    GroupTO parent(@NotNull @PathParam("groupKey") Long groupKey);
+
+    /**
+     * Reads the group matching the provided groupKey.
+     *
+     * @param groupKey key of group to be read
+     * @return group with matching id
+     */
+    @GET
+    @Path("{groupKey}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    GroupTO read(@NotNull @PathParam("groupKey") Long groupKey);
+
+    /**
+     * This method is similar to {@link #read(Long)}, but uses different authentication handling to ensure that a user
+     * can read his own groups.
+     *
+     * @param groupKey key of group to be read
+     * @return group with matching id
+     */
+    @Descriptions({
+        @Description(target = DocTarget.METHOD,
+                value = "This method is similar to <tt>read()</tt>, but uses different authentication handling to "
+                + "ensure that a user can read his own groups.")
+    })
+    @GET
+    @Path("{groupKey}/own")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    GroupTO readSelf(@NotNull @PathParam("groupKey") Long groupKey);
+
+    /**
+     * Returns a paged list of existing groups.
+     *
+     * @return paged list of all existing groups
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<GroupTO> list();
+
+    /**
+     * Returns a paged list of existing groups.
+     *
+     * @param orderBy list of ordering clauses, separated by comma
+     * @return paged list of all existing groups
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<GroupTO> list(@QueryParam(PARAM_ORDERBY) String orderBy);
+
+    /**
+     * Returns a paged list of existing groups matching page/size conditions.
+     *
+     * @param page result page number
+     * @param size number of entries per page
+     * @return paged list of existing groups matching page/size conditions
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<GroupTO> list(
+            @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
+            @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size);
+
+    /**
+     * Returns a paged list of existing groups matching page/size conditions.
+     *
+     * @param page result page number
+     * @param size number of entries per page
+     * @param orderBy list of ordering clauses, separated by comma
+     * @return paged list of existing groups matching page/size conditions
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<GroupTO> list(
+            @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
+            @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size,
+            @QueryParam(PARAM_ORDERBY) String orderBy);
+
+    /**
+     * Returns a paged list of groups matching the provided FIQL search condition.
+     *
+     * @param fiql FIQL search expression
+     * @return paged list of groups matching the provided FIQL search condition
+     */
+    @GET
+    @Path("search")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<GroupTO> search(@NotNull @QueryParam(PARAM_FIQL) String fiql);
+
+    /**
+     * Returns a paged list of groups matching the provided FIQL search condition.
+     *
+     * @param fiql FIQL search expression
+     * @param orderBy list of ordering clauses, separated by comma
+     * @return paged list of groups matching the provided FIQL search condition
+     */
+    @GET
+    @Path("search")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<GroupTO> search(
+            @NotNull @QueryParam(PARAM_FIQL) String fiql, @QueryParam(PARAM_ORDERBY) String orderBy);
+
+    /**
+     * Returns a paged list of groups matching the provided FIQL search condition.
+     *
+     * @param fiql FIQL search expression
+     * @param page result page number
+     * @param size number of entries per page
+     * @return paged list of groups matching the provided FIQL search condition
+     */
+    @GET
+    @Path("search")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<GroupTO> search(@QueryParam(PARAM_FIQL) String fiql,
+            @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
+            @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size);
+
+    /**
+     * Returns a paged list of groups matching the provided FIQL search condition.
+     *
+     * @param fiql FIQL search expression
+     * @param page result page number
+     * @param size number of entries per page
+     * @param orderBy list of ordering clauses, separated by comma
+     * @return paged list of groups matching the provided FIQL search condition
+     */
+    @GET
+    @Path("search")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    PagedResult<GroupTO> search(@QueryParam(PARAM_FIQL) String fiql,
+            @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
+            @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size,
+            @QueryParam(PARAM_ORDERBY) String orderBy);
+
+    /**
+     * Creates a new group.
+     *
+     * @param groupTO group to be created
+     * @return <tt>Response</tt> object featuring <tt>Location</tt> header of created group as well as the group itself
+     * enriched with propagation status information - {@link GroupTO} as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring <tt>Location</tt> header of created group as well as the "
+                + "group itself enriched with propagation status information - <tt>GroupTO</tt> as <tt>Entity</tt>")
+    })
+    @POST
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response create(@NotNull GroupTO groupTO);
+
+    /**
+     * Updates group matching the provided groupKey.
+     *
+     * @param groupKey key of group to be updated
+     * @param groupMod modification to be applied to group matching the provided groupKey
+     * @return <tt>Response</tt> object featuring the updated group enriched with propagation status information
+     * - {@link GroupTO} as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring the updated group enriched with propagation status information - "
+                + "<tt>GroupTO</tt> as <tt>Entity</tt>")
+    })
+    @POST
+    @Path("{groupKey}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response update(@NotNull @PathParam("groupKey") Long groupKey, @NotNull GroupMod groupMod);
+
+    /**
+     * Deletes group matching provided groupKey.
+     *
+     * @param groupKey key of group to be deleted
+     * @return <tt>Response</tt> object featuring the deleted group enriched with propagation status information
+     * - {@link GroupTO} as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring the deleted group enriched with propagation status information - "
+                + "<tt>GroupTO</tt> as <tt>Entity</tt>")
+    })
+    @DELETE
+    @Path("{groupKey}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response delete(@NotNull @PathParam("groupKey") Long groupKey);
+
+    /**
+     * Executes resource-related operations on given group.
+     *
+     * @param groupKey group id.
+     * @param type resource association action type
+     * @param resourceNames external resources to be used for propagation-related operations
+     * @return <tt>Response</tt> object featuring
+     * {@link BulkActionResult} as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring <tt>BulkActionResult</tt> as <tt>Entity</tt>")
+    })
+    @POST
+    @Path("{groupKey}/deassociate/{type}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response bulkDeassociation(@NotNull @PathParam("groupKey") Long groupKey,
+            @NotNull @PathParam("type") ResourceDeassociationActionType type,
+            @NotNull List<ResourceName> resourceNames);
+
+    /**
+     * Executes resource-related operations on given group.
+     *
+     * @param groupKey group id.
+     * @param type resource association action type
+     * @param resourceNames external resources to be used for propagation-related operations
+     * @return <tt>Response</tt> object featuring {@link BulkActionResult} as <tt>Entity</tt>
+     */
+    @Descriptions({
+        @Description(target = DocTarget.RESPONSE,
+                value = "Featuring <tt>BulkActionResult</tt> as <tt>Entity</tt>")
+    })
+    @POST
+    @Path("{groupKey}/associate/{type}")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response bulkAssociation(@NotNull @PathParam("groupKey") Long groupKey,
+            @NotNull @PathParam("type") ResourceAssociationActionType type,
+            @NotNull List<ResourceName> resourceNames);
+
+    /**
+     * Executes the provided bulk action.
+     *
+     * @param bulkAction list of group ids against which the bulk action will be performed.
+     * @return Bulk action result
+     */
+    @POST
+    @Path("bulk")
+    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    BulkActionResult bulk(@NotNull BulkAction bulkAction);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/JAXRSService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/JAXRSService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/JAXRSService.java
index 7108b49..cd5c473 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/JAXRSService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/JAXRSService.java
@@ -20,20 +20,20 @@ package org.apache.syncope.common.rest.api.service;
 
 public interface JAXRSService {
 
-    final String PARAM_FIQL = "fiql";
+    String PARAM_FIQL = "fiql";
 
-    final String PARAM_PAGE = "page";
+    String PARAM_PAGE = "page";
 
-    final String DEFAULT_PARAM_PAGE = "1";
+    String DEFAULT_PARAM_PAGE = "1";
 
-    final int DEFAULT_PARAM_PAGE_VALUE = Integer.valueOf(DEFAULT_PARAM_PAGE);
+    int DEFAULT_PARAM_PAGE_VALUE = Integer.valueOf(DEFAULT_PARAM_PAGE);
 
-    final String PARAM_SIZE = "size";
+    String PARAM_SIZE = "size";
 
-    final String DEFAULT_PARAM_SIZE = "25";
+    String DEFAULT_PARAM_SIZE = "25";
 
-    final int DEFAULT_PARAM_SIZE_VALUE = Integer.valueOf(DEFAULT_PARAM_SIZE);
+    int DEFAULT_PARAM_SIZE_VALUE = Integer.valueOf(DEFAULT_PARAM_SIZE);
 
-    final String PARAM_ORDERBY = "orderby";
+    String PARAM_ORDERBY = "orderby";
 
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java
index 9199d37..06b6b32 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java
@@ -51,8 +51,8 @@ public interface ResourceService extends JAXRSService {
      * Returns connector object from the external resource, for the given type and key.
      *
      * @param resourceKey Name of resource to read connector object from
-     * @param type user / role
-     * @param key user key / role key
+     * @param type user /group
+     * @param key user key / group key
      * @return connector object from the external resource, for the given type and key
      */
     @GET
@@ -128,12 +128,12 @@ public interface ResourceService extends JAXRSService {
     boolean check(@NotNull ResourceTO resourceTO);
 
     /**
-     * De-associate users or roles (depending on the provided subject type) from the given resource.
+     * De-associate users or groups (depending on the provided subject type) from the given resource.
      *
      * @param resourceKey name of resource
-     * @param subjectType subject type (user or role)
+     * @param subjectType subject type (user or group)
      * @param type resource de-association action type
-     * @param subjectKeys users or roles against which the bulk action will be performed
+     * @param subjectKeys users or groups against which the bulk action will be performed
      * @return <tt>Response</tt> object featuring {@link BulkActionResult} as <tt>Entity</tt>
      */
     @Descriptions({

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RoleService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RoleService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RoleService.java
deleted file mode 100644
index 5d1b737..0000000
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RoleService.java
+++ /dev/null
@@ -1,312 +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.common.rest.api.service;
-
-import java.util.List;
-import javax.validation.constraints.Min;
-import javax.validation.constraints.NotNull;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.DefaultValue;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import org.apache.cxf.jaxrs.model.wadl.Description;
-import org.apache.cxf.jaxrs.model.wadl.Descriptions;
-import org.apache.cxf.jaxrs.model.wadl.DocTarget;
-import org.apache.syncope.common.lib.mod.RoleMod;
-import org.apache.syncope.common.lib.to.BulkAction;
-import org.apache.syncope.common.lib.to.BulkActionResult;
-import org.apache.syncope.common.lib.to.PagedResult;
-import org.apache.syncope.common.lib.to.RoleTO;
-import org.apache.syncope.common.lib.types.ResourceAssociationActionType;
-import org.apache.syncope.common.lib.types.ResourceDeassociationActionType;
-import org.apache.syncope.common.lib.wrap.ResourceName;
-
-/**
- * REST operations for roles.
- */
-@Path("roles")
-public interface RoleService extends JAXRSService {
-
-    /**
-     * Returns children roles of given role.
-     *
-     * @param roleKey key of role to get children from
-     * @return children roles of given role
-     */
-    @GET
-    @Path("{roleKey}/children")
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    List<RoleTO> children(@NotNull @PathParam("roleKey") Long roleKey);
-
-    /**
-     * Returns parent role of the given role (or null if no parent exists).
-     *
-     * @param roleKey key of role to get parent role from
-     * @return parent role of the given role (or null if no parent exists)
-     */
-    @GET
-    @Path("{roleKey}/parent")
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    RoleTO parent(@NotNull @PathParam("roleKey") Long roleKey);
-
-    /**
-     * Reads the role matching the provided roleKey.
-     *
-     * @param roleKey key of role to be read
-     * @return role with matching id
-     */
-    @GET
-    @Path("{roleKey}")
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    RoleTO read(@NotNull @PathParam("roleKey") Long roleKey);
-
-    /**
-     * This method is similar to {@link #read(Long)}, but uses different authentication handling to ensure that a user
-     * can read his own roles.
-     *
-     * @param roleKey key of role to be read
-     * @return role with matching id
-     */
-    @Descriptions({
-        @Description(target = DocTarget.METHOD,
-                value = "This method is similar to <tt>read()</tt>, but uses different authentication handling to "
-                + "ensure that a user can read his own roles.")
-    })
-    @GET
-    @Path("{roleKey}/own")
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    RoleTO readSelf(@NotNull @PathParam("roleKey") Long roleKey);
-
-    /**
-     * Returns a paged list of existing roles.
-     *
-     * @return paged list of all existing roles
-     */
-    @GET
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    PagedResult<RoleTO> list();
-
-    /**
-     * Returns a paged list of existing roles.
-     *
-     * @param orderBy list of ordering clauses, separated by comma
-     * @return paged list of all existing roles
-     */
-    @GET
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    PagedResult<RoleTO> list(@QueryParam(PARAM_ORDERBY) String orderBy);
-
-    /**
-     * Returns a paged list of existing roles matching page/size conditions.
-     *
-     * @param page result page number
-     * @param size number of entries per page
-     * @return paged list of existing roles matching page/size conditions
-     */
-    @GET
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    PagedResult<RoleTO> list(
-            @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
-            @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size);
-
-    /**
-     * Returns a paged list of existing roles matching page/size conditions.
-     *
-     * @param page result page number
-     * @param size number of entries per page
-     * @param orderBy list of ordering clauses, separated by comma
-     * @return paged list of existing roles matching page/size conditions
-     */
-    @GET
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    PagedResult<RoleTO> list(
-            @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
-            @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size,
-            @QueryParam(PARAM_ORDERBY) String orderBy);
-
-    /**
-     * Returns a paged list of roles matching the provided FIQL search condition.
-     *
-     * @param fiql FIQL search expression
-     * @return paged list of roles matching the provided FIQL search condition
-     */
-    @GET
-    @Path("search")
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    PagedResult<RoleTO> search(@NotNull @QueryParam(PARAM_FIQL) String fiql);
-
-    /**
-     * Returns a paged list of roles matching the provided FIQL search condition.
-     *
-     * @param fiql FIQL search expression
-     * @param orderBy list of ordering clauses, separated by comma
-     * @return paged list of roles matching the provided FIQL search condition
-     */
-    @GET
-    @Path("search")
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    PagedResult<RoleTO> search(
-            @NotNull @QueryParam(PARAM_FIQL) String fiql, @QueryParam(PARAM_ORDERBY) String orderBy);
-
-    /**
-     * Returns a paged list of roles matching the provided FIQL search condition.
-     *
-     * @param fiql FIQL search expression
-     * @param page result page number
-     * @param size number of entries per page
-     * @return paged list of roles matching the provided FIQL search condition
-     */
-    @GET
-    @Path("search")
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    PagedResult<RoleTO> search(@QueryParam(PARAM_FIQL) String fiql,
-            @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
-            @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size);
-
-    /**
-     * Returns a paged list of roles matching the provided FIQL search condition.
-     *
-     * @param fiql FIQL search expression
-     * @param page result page number
-     * @param size number of entries per page
-     * @param orderBy list of ordering clauses, separated by comma
-     * @return paged list of roles matching the provided FIQL search condition
-     */
-    @GET
-    @Path("search")
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    PagedResult<RoleTO> search(@QueryParam(PARAM_FIQL) String fiql,
-            @NotNull @Min(1) @QueryParam(PARAM_PAGE) @DefaultValue(DEFAULT_PARAM_PAGE) Integer page,
-            @NotNull @Min(1) @QueryParam(PARAM_SIZE) @DefaultValue(DEFAULT_PARAM_SIZE) Integer size,
-            @QueryParam(PARAM_ORDERBY) String orderBy);
-
-    /**
-     * Creates a new role.
-     *
-     * @param roleTO role to be created
-     * @return <tt>Response</tt> object featuring <tt>Location</tt> header of created role as well as the role itself
-     * enriched with propagation status information - {@link RoleTO} as <tt>Entity</tt>
-     */
-    @Descriptions({
-        @Description(target = DocTarget.RESPONSE,
-                value = "Featuring <tt>Location</tt> header of created role as well as the "
-                + "role itself enriched with propagation status information - <tt>RoleTO</tt> as <tt>Entity</tt>")
-    })
-    @POST
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    Response create(@NotNull RoleTO roleTO);
-
-    /**
-     * Updates role matching the provided roleKey.
-     *
-     * @param roleKey key of role to be updated
-     * @param roleMod modification to be applied to role matching the provided roleKey
-     * @return <tt>Response</tt> object featuring the updated role enriched with propagation status information
-     * - {@link RoleTO} as <tt>Entity</tt>
-     */
-    @Descriptions({
-        @Description(target = DocTarget.RESPONSE,
-                value = "Featuring the updated role enriched with propagation status information - "
-                + "<tt>RoleTO</tt> as <tt>Entity</tt>")
-    })
-    @POST
-    @Path("{roleKey}")
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    Response update(@NotNull @PathParam("roleKey") Long roleKey, @NotNull RoleMod roleMod);
-
-    /**
-     * Deletes role matching provided roleKey.
-     *
-     * @param roleKey key of role to be deleted
-     * @return <tt>Response</tt> object featuring the deleted role enriched with propagation status information
-     * - {@link RoleTO} as <tt>Entity</tt>
-     */
-    @Descriptions({
-        @Description(target = DocTarget.RESPONSE,
-                value = "Featuring the deleted role enriched with propagation status information - "
-                + "<tt>RoleTO</tt> as <tt>Entity</tt>")
-    })
-    @DELETE
-    @Path("{roleKey}")
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    Response delete(@NotNull @PathParam("roleKey") Long roleKey);
-
-    /**
-     * Executes resource-related operations on given role.
-     *
-     * @param roleKey role id.
-     * @param type resource association action type
-     * @param resourceNames external resources to be used for propagation-related operations
-     * @return <tt>Response</tt> object featuring
-     * {@link BulkActionResult} as <tt>Entity</tt>
-     */
-    @Descriptions({
-        @Description(target = DocTarget.RESPONSE,
-                value = "Featuring <tt>BulkActionResult</tt> as <tt>Entity</tt>")
-    })
-    @POST
-    @Path("{roleKey}/deassociate/{type}")
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    Response bulkDeassociation(@NotNull @PathParam("roleKey") Long roleKey,
-            @NotNull @PathParam("type") ResourceDeassociationActionType type,
-            @NotNull List<ResourceName> resourceNames);
-
-    /**
-     * Executes resource-related operations on given role.
-     *
-     * @param roleKey role id.
-     * @param type resource association action type
-     * @param resourceNames external resources to be used for propagation-related operations
-     * @return <tt>Response</tt> object featuring {@link BulkActionResult} as <tt>Entity</tt>
-     */
-    @Descriptions({
-        @Description(target = DocTarget.RESPONSE,
-                value = "Featuring <tt>BulkActionResult</tt> as <tt>Entity</tt>")
-    })
-    @POST
-    @Path("{roleKey}/associate/{type}")
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    Response bulkAssociation(@NotNull @PathParam("roleKey") Long roleKey,
-            @NotNull @PathParam("type") ResourceAssociationActionType type,
-            @NotNull List<ResourceName> resourceNames);
-
-    /**
-     * Executes the provided bulk action.
-     *
-     * @param bulkAction list of role ids against which the bulk action will be performed.
-     * @return Bulk action result
-     */
-    @POST
-    @Path("bulk")
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    BulkActionResult bulk(@NotNull BulkAction bulkAction);
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java
index 92aa86b..b74ec2b 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/WorkflowService.java
@@ -39,7 +39,7 @@ public interface WorkflowService extends JAXRSService {
     /**
      * Exports workflow definition for matching kind.
      *
-     * @param kind user or role
+     * @param kind user or group
      * @return workflow definition for matching kind
      */
     @GET
@@ -49,7 +49,7 @@ public interface WorkflowService extends JAXRSService {
     /**
      * Exports workflow diagram representation.
      *
-     * @param kind user or role
+     * @param kind user or group
      * @return workflow diagram representation
      */
     @GET
@@ -60,7 +60,7 @@ public interface WorkflowService extends JAXRSService {
     /**
      * Imports workflow definition for matching kind.
      *
-     * @param kind user or role
+     * @param kind user or group
      * @param definition workflow definition for matching kind
      */
     @PUT

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/core/logic/src/main/java/org/apache/syncope/core/logic/ConfigurationLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ConfigurationLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ConfigurationLogic.java
index 9874e4d..17e1a7e 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/ConfigurationLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ConfigurationLogic.java
@@ -29,7 +29,7 @@ import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 import org.apache.syncope.core.persistence.api.entity.conf.CPlainAttr;
 import org.apache.syncope.core.persistence.api.entity.conf.CPlainSchema;
 import org.apache.syncope.core.provisioning.api.data.ConfigurationDataBinder;
-import org.apache.syncope.core.workflow.api.RoleWorkflowAdapter;
+import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
 import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -55,7 +55,7 @@ public class ConfigurationLogic extends AbstractTransactionalLogic<ConfTO> {
     private UserWorkflowAdapter uwfAdapter;
 
     @Autowired
-    private RoleWorkflowAdapter rwfAdapter;
+    private GroupWorkflowAdapter gwfAdapter;
 
     @PreAuthorize("hasRole('CONFIGURATION_DELETE')")
     public void delete(final String key) {
@@ -96,7 +96,7 @@ public class ConfigurationLogic extends AbstractTransactionalLogic<ConfTO> {
     @Transactional(readOnly = true)
     public void export(final OutputStream os) {
         try {
-            exporter.export(os, uwfAdapter.getPrefix(), rwfAdapter.getPrefix());
+            exporter.export(os, uwfAdapter.getPrefix(), gwfAdapter.getPrefix());
             LOG.debug("Database content successfully exported");
         } catch (Exception e) {
             LOG.error("While exporting database content", e);

http://git-wip-us.apache.org/repos/asf/syncope/blob/4095f1e8/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
new file mode 100644
index 0000000..a3fd17a
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
@@ -0,0 +1,405 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Resource;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.mod.GroupMod;
+import org.apache.syncope.common.lib.to.BulkAction;
+import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.to.PropagationStatus;
+import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.SubjectType;
+import org.apache.syncope.core.persistence.api.GroupEntitlementUtil;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.SubjectSearchDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+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.group.Group;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.AttributableTransformer;
+import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
+import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
+import org.apache.syncope.core.misc.security.AuthContextUtil;
+import org.apache.syncope.core.misc.security.UnauthorizedGroupException;
+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;
+import org.springframework.transaction.interceptor.TransactionInterceptor;
+
+/**
+ * Note that this controller does not extend {@link AbstractTransactionalLogic}, hence does not provide any
+ * Spring's Transactional logic at class level.
+ */
+@Component
+public class GroupLogic extends AbstractSubjectLogic<GroupTO, GroupMod> {
+
+    @Autowired
+    protected GroupDAO groupDAO;
+
+    @Autowired
+    protected UserDAO userDAO;
+
+    @Autowired
+    protected SubjectSearchDAO searchDAO;
+
+    @Autowired
+    protected GroupDataBinder binder;
+
+    @Autowired
+    protected PropagationManager propagationManager;
+
+    @Autowired
+    protected PropagationTaskExecutor taskExecutor;
+
+    @Autowired
+    protected AttributableTransformer attrTransformer;
+
+    @Resource(name = "anonymousUser")
+    protected String anonymousUser;
+
+    @Autowired
+    protected GroupProvisioningManager provisioningManager;
+
+    @PreAuthorize("hasAnyRole('GROUP_READ', T(org.apache.syncope.common.lib.SyncopeConstants).ANONYMOUS_ENTITLEMENT)")
+    @Transactional(readOnly = true)
+    @Override
+    public GroupTO read(final Long groupKey) {
+        Group group;
+        // bypass group entitlements check
+        if (anonymousUser.equals(AuthContextUtil.getAuthenticatedUsername())) {
+            group = groupDAO.find(groupKey);
+        } else {
+            group = groupDAO.authFetch(groupKey);
+        }
+
+        if (group == null) {
+            throw new NotFoundException("Group " + groupKey);
+        }
+
+        return binder.getGroupTO(group);
+    }
+
+    @PreAuthorize("isAuthenticated() "
+            + "and not(hasRole(T(org.apache.syncope.common.lib.SyncopeConstants).ANONYMOUS_ENTITLEMENT))")
+    @Transactional(readOnly = true)
+    public GroupTO readSelf(final Long groupKey) {
+        // Explicit search instead of using binder.getGroupFromId() in order to bypass auth checks - will do here
+        Group group = groupDAO.find(groupKey);
+        if (group == null) {
+            throw new NotFoundException("Group " + groupKey);
+        }
+
+        Set<Long> ownedGroupIds;
+        User authUser = userDAO.find(AuthContextUtil.getAuthenticatedUsername());
+        if (authUser == null) {
+            ownedGroupIds = Collections.<Long>emptySet();
+        } else {
+            ownedGroupIds = authUser.getGroupKeys();
+        }
+
+        Set<Long> allowedGroupIds = GroupEntitlementUtil.getGroupKeys(AuthContextUtil.getOwnedEntitlementNames());
+        allowedGroupIds.addAll(ownedGroupIds);
+        if (!allowedGroupIds.contains(group.getKey())) {
+            throw new UnauthorizedGroupException(group.getKey());
+        }
+
+        return binder.getGroupTO(group);
+    }
+
+    @PreAuthorize("hasRole('GROUP_READ')")
+    @Transactional(readOnly = true)
+    public GroupTO parent(final Long groupKey) {
+        Group group = groupDAO.authFetch(groupKey);
+
+        Set<Long> allowedGroupIds = GroupEntitlementUtil.getGroupKeys(AuthContextUtil.getOwnedEntitlementNames());
+        if (group.getParent() != null && !allowedGroupIds.contains(group.getParent().getKey())) {
+            throw new UnauthorizedGroupException(group.getParent().getKey());
+        }
+
+        GroupTO result = group.getParent() == null
+                ? null
+                : binder.getGroupTO(group.getParent());
+
+        return result;
+    }
+
+    @PreAuthorize("hasRole('GROUP_READ')")
+    @Transactional(readOnly = true)
+    public List<GroupTO> children(final Long groupKey) {
+        Group group = groupDAO.authFetch(groupKey);
+
+        Set<Long> allowedGroupIds = GroupEntitlementUtil.getGroupKeys(AuthContextUtil.getOwnedEntitlementNames());
+
+        List<Group> children = groupDAO.findChildren(group);
+        List<GroupTO> childrenTOs = new ArrayList<>(children.size());
+        for (Group child : children) {
+            if (allowedGroupIds.contains(child.getKey())) {
+                childrenTOs.add(binder.getGroupTO(child));
+            }
+        }
+
+        return childrenTOs;
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    @Transactional(readOnly = true, rollbackFor = { Throwable.class })
+    @Override
+    public int count() {
+        return groupDAO.count();
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    @Transactional(readOnly = true)
+    @Override
+    public List<GroupTO> list(final int page, final int size, final List<OrderByClause> orderBy) {
+        List<Group> groups = groupDAO.findAll(page, size, orderBy);
+
+        List<GroupTO> groupTOs = new ArrayList<>(groups.size());
+        for (Group group : groups) {
+            groupTOs.add(binder.getGroupTO(group));
+        }
+
+        return groupTOs;
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    @Transactional(readOnly = true, rollbackFor = { Throwable.class })
+    @Override
+    public int searchCount(final SearchCond searchCondition) {
+        final Set<Long> adminGroupIds = GroupEntitlementUtil.getGroupKeys(AuthContextUtil.getOwnedEntitlementNames());
+        return searchDAO.count(adminGroupIds, searchCondition, SubjectType.GROUP);
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    @Transactional(readOnly = true, rollbackFor = { Throwable.class })
+    @Override
+    public List<GroupTO> search(final SearchCond searchCondition, final int page, final int size,
+            final List<OrderByClause> orderBy) {
+
+        final List<Group> matchingGroups = searchDAO.search(GroupEntitlementUtil.getGroupKeys(AuthContextUtil.
+                getOwnedEntitlementNames()),
+                searchCondition, page, size, orderBy, SubjectType.GROUP);
+
+        final List<GroupTO> result = new ArrayList<>(matchingGroups.size());
+        for (Group group : matchingGroups) {
+            result.add(binder.getGroupTO(group));
+        }
+
+        return result;
+    }
+
+    @PreAuthorize("hasRole('GROUP_CREATE')")
+    public GroupTO create(final GroupTO groupTO) {
+        // Check that this operation is allowed to be performed by caller
+        Set<Long> allowedGroupIds = GroupEntitlementUtil.getGroupKeys(AuthContextUtil.getOwnedEntitlementNames());
+        if (groupTO.getParent() != 0 && !allowedGroupIds.contains(groupTO.getParent())) {
+            throw new UnauthorizedGroupException(groupTO.getParent());
+        }
+
+        // Attributable transformation (if configured)
+        GroupTO actual = attrTransformer.transform(groupTO);
+        LOG.debug("Transformed: {}", actual);
+
+        /*
+         * Actual operations: workflow, propagation
+         */
+        Map.Entry<Long, List<PropagationStatus>> created = provisioningManager.create(groupTO);
+        final GroupTO savedTO = binder.getGroupTO(created.getKey());
+        savedTO.getPropagationStatusTOs().addAll(created.getValue());
+        return savedTO;
+    }
+
+    @PreAuthorize("hasRole('GROUP_UPDATE')")
+    @Override
+    public GroupTO update(final GroupMod groupMod) {
+        // Check that this operation is allowed to be performed by caller
+        groupDAO.authFetch(groupMod.getKey());
+
+        // Attribute value transformation (if configured)
+        GroupMod actual = attrTransformer.transform(groupMod);
+        LOG.debug("Transformed: {}", actual);
+
+        Map.Entry<Long, List<PropagationStatus>> updated = provisioningManager.update(groupMod);
+
+        final GroupTO updatedTO = binder.getGroupTO(updated.getKey());
+        updatedTO.getPropagationStatusTOs().addAll(updated.getValue());
+        return updatedTO;
+    }
+
+    @PreAuthorize("hasRole('GROUP_DELETE')")
+    @Override
+    public GroupTO delete(final Long groupKey) {
+        List<Group> ownedGroups = groupDAO.findOwnedByGroup(groupKey);
+        if (!ownedGroups.isEmpty()) {
+            List<String> owned = new ArrayList<>(ownedGroups.size());
+            for (Group group : ownedGroups) {
+                owned.add(group.getKey() + " " + group.getName());
+            }
+
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.GroupOwnership);
+            sce.getElements().addAll(owned);
+            throw sce;
+        }
+
+        List<PropagationStatus> statuses = provisioningManager.delete(groupKey);
+
+        GroupTO groupTO = new GroupTO();
+        groupTO.setKey(groupKey);
+
+        groupTO.getPropagationStatusTOs().addAll(statuses);
+
+        return groupTO;
+    }
+
+    @PreAuthorize("(hasRole('GROUP_DELETE') and #bulkAction.operation == #bulkAction.operation.DELETE)")
+    public BulkActionResult bulk(final BulkAction bulkAction) {
+        BulkActionResult res = new BulkActionResult();
+
+        if (bulkAction.getOperation() == BulkAction.Type.DELETE) {
+            for (String groupKey : bulkAction.getTargets()) {
+                try {
+                    res.add(delete(Long.valueOf(groupKey)).getKey(), BulkActionResult.Status.SUCCESS);
+                } catch (Exception e) {
+                    LOG.error("Error performing delete for group {}", groupKey, e);
+                    res.add(groupKey, BulkActionResult.Status.FAILURE);
+                }
+            }
+        } else {
+            LOG.warn("Unsupported bulk action: {}", bulkAction.getOperation());
+        }
+
+        return res;
+    }
+
+    @PreAuthorize("hasRole('GROUP_UPDATE')")
+    @Transactional(rollbackFor = { Throwable.class })
+    @Override
+    public GroupTO unlink(final Long groupKey, final Collection<String> resources) {
+        final GroupMod groupMod = new GroupMod();
+        groupMod.setKey(groupKey);
+        groupMod.getResourcesToRemove().addAll(resources);
+        final Long updatedResult = provisioningManager.unlink(groupMod);
+
+        return binder.getGroupTO(updatedResult);
+    }
+
+    @PreAuthorize("hasRole('GROUP_UPDATE')")
+    @Transactional(rollbackFor = { Throwable.class })
+    @Override
+    public GroupTO link(final Long groupKey, final Collection<String> resources) {
+        final GroupMod groupMod = new GroupMod();
+        groupMod.setKey(groupKey);
+        groupMod.getResourcesToAdd().addAll(resources);
+        return binder.getGroupTO(provisioningManager.link(groupMod));
+    }
+
+    @PreAuthorize("hasRole('GROUP_UPDATE')")
+    @Transactional(rollbackFor = { Throwable.class })
+    @Override
+    public GroupTO unassign(final Long groupKey, final Collection<String> resources) {
+        final GroupMod groupMod = new GroupMod();
+        groupMod.setKey(groupKey);
+        groupMod.getResourcesToRemove().addAll(resources);
+        return update(groupMod);
+    }
+
+    @PreAuthorize("hasRole('GROUP_UPDATE')")
+    @Transactional(rollbackFor = { Throwable.class })
+    @Override
+    public GroupTO assign(
+            final Long groupKey, final Collection<String> resources, final boolean changePwd, final String password) {
+
+        final GroupMod userMod = new GroupMod();
+        userMod.setKey(groupKey);
+        userMod.getResourcesToAdd().addAll(resources);
+        return update(userMod);
+    }
+
+    @PreAuthorize("hasRole('GROUP_UPDATE')")
+    @Transactional(rollbackFor = { Throwable.class })
+    @Override
+    public GroupTO deprovision(final Long groupKey, final Collection<String> resources) {
+        final Group group = groupDAO.authFetch(groupKey);
+
+        List<PropagationStatus> statuses = provisioningManager.deprovision(groupKey, resources);
+
+        final GroupTO updatedTO = binder.getGroupTO(group);
+        updatedTO.getPropagationStatusTOs().addAll(statuses);
+        return updatedTO;
+    }
+
+    @PreAuthorize("hasRole('GROUP_UPDATE')")
+    @Transactional(rollbackFor = { Throwable.class })
+    @Override
+    public GroupTO provision(
+            final Long groupKey, final Collection<String> resources, final boolean changePwd, final String password) {
+        final GroupTO original = binder.getGroupTO(groupKey);
+
+        //trick: assign and retrieve propagation statuses ...
+        original.getPropagationStatusTOs().addAll(
+                assign(groupKey, resources, changePwd, password).getPropagationStatusTOs());
+
+        // .... rollback.
+        TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
+        return original;
+    }
+
+    @Override
+    protected GroupTO resolveReference(final Method method, final Object... args) throws UnresolvedReferenceException {
+        Long key = null;
+
+        if (ArrayUtils.isNotEmpty(args)) {
+            for (int i = 0; key == null && i < args.length; i++) {
+                if (args[i] instanceof Long) {
+                    key = (Long) args[i];
+                } else if (args[i] instanceof GroupTO) {
+                    key = ((GroupTO) args[i]).getKey();
+                } else if (args[i] instanceof GroupMod) {
+                    key = ((GroupMod) args[i]).getKey();
+                }
+            }
+        }
+
+        if ((key != null) && !key.equals(0l)) {
+            try {
+                return binder.getGroupTO(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/4095f1e8/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
index b85b74c..baeb1ce 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
@@ -34,7 +34,7 @@ import org.apache.syncope.common.lib.types.SubjectType;
 import org.apache.syncope.core.persistence.api.dao.DuplicateException;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.RoleDAO;
+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.AttributableUtil;
 import org.apache.syncope.core.persistence.api.entity.AttributableUtilFactory;
@@ -68,7 +68,7 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
     private UserDAO userDAO;
 
     @Autowired
-    private RoleDAO roleDAO;
+    private GroupDAO groupDAO;
 
     @Autowired
     private ResourceDataBinder binder;
@@ -170,7 +170,7 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
 
         Subject<?, ?, ?> subject = type == SubjectType.USER
                 ? userDAO.find(id)
-                : roleDAO.find(id);
+                : groupDAO.find(id);
         if (subject == null) {
             throw new NotFoundException(type + " " + id);
         }


Mime
View raw message