You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by gi...@apache.org on 2016/01/19 16:43:57 UTC

syncope git commit: [SYNCOPE-719] Auxiliary classes can be managed dyamically during self creation/update

Repository: syncope
Updated Branches:
  refs/heads/master 53afc1308 -> 43c4d8ea8


[SYNCOPE-719] Auxiliary classes can be managed dyamically during self creation/update


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/43c4d8ea
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/43c4d8ea
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/43c4d8ea

Branch: refs/heads/master
Commit: 43c4d8ea89c4f97b193451e506bc876d6c5c99be
Parents: 53afc13
Author: giacomolm <gi...@hotmail.it>
Authored: Tue Jan 19 16:36:22 2016 +0100
Committer: giacomolm <gi...@hotmail.it>
Committed: Tue Jan 19 16:36:34 2016 +0100

----------------------------------------------------------------------
 .../enduser/SyncopeEnduserApplication.java      |  22 ++
 .../client/enduser/adapters/UserTOAdapter.java  |   8 +-
 .../client/enduser/model/UserTORequest.java     |  19 +-
 .../enduser/resources/SchemaResource.java       |  14 +-
 .../resources/SyncopeAnyClassTypeResource.java  |  86 +++++
 .../resources/SyncopeAnyTypeResource.java       |  82 +++++
 .../resources/META-INF/resources/app/index.html |   2 +
 .../app/js/controllers/UserController.js        | 351 ++++++++++++-------
 .../resources/app/js/directives/auxClasses.js   |  60 ++++
 .../app/js/directives/dynamicPlainAttributes.js |  42 +--
 .../resources/app/js/services/anyService.js     |  53 +++
 .../resources/app/js/services/schemaService.js  |   6 +-
 .../resources/app/views/auxClasses.html         |  10 +
 .../resources/app/views/user-groups.html        |   4 +
 14 files changed, 595 insertions(+), 164 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/43c4d8ea/client/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserApplication.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserApplication.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserApplication.java
index 17a92ed..902a276 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserApplication.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserApplication.java
@@ -34,6 +34,8 @@ import org.apache.syncope.client.enduser.resources.LoginResource;
 import org.apache.syncope.client.enduser.resources.LogoutResource;
 import org.apache.syncope.client.enduser.resources.SchemaResource;
 import org.apache.syncope.client.enduser.resources.SecurityQuestionResource;
+import org.apache.syncope.client.enduser.resources.SyncopeAnyClassTypeResource;
+import org.apache.syncope.client.enduser.resources.SyncopeAnyTypeResource;
 import org.apache.syncope.client.enduser.resources.SyncopeGroupResource;
 import org.apache.syncope.client.enduser.resources.SyncopeResourceResource;
 import org.apache.syncope.client.enduser.resources.UserSelfCreateResource;
@@ -267,6 +269,26 @@ public class SyncopeEnduserApplication extends WebApplication implements Seriali
             }
         });
 
+        mountResource("/api/auxiliaryClasses", new ResourceReference("auxClasses") {
+
+            private static final long serialVersionUID = -128426276529456602L;
+
+            @Override
+            public IResource getResource() {
+                return new SyncopeAnyClassTypeResource();
+            }
+        });
+
+        mountResource("/api/anyTypes", new ResourceReference("anyType") {
+
+            private static final long serialVersionUID = -128426276529456602L;
+
+            @Override
+            public IResource getResource() {
+                return new SyncopeAnyTypeResource();
+            }
+        });
+
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/43c4d8ea/client/enduser/src/main/java/org/apache/syncope/client/enduser/adapters/UserTOAdapter.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/adapters/UserTOAdapter.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/adapters/UserTOAdapter.java
index 22aa139..5585a41 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/adapters/UserTOAdapter.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/adapters/UserTOAdapter.java
@@ -58,6 +58,8 @@ public class UserTOAdapter {
         userTO.getResources().addAll(userTORequest.getResources());
         // add memberships
         userTO.getMemberships().addAll(userTORequest.getMemberships());
+        // add auxiliary classes
+        userTO.getAuxClasses().addAll(userTORequest.getAuxClasses());
 
         return userTO;
     }
@@ -74,11 +76,13 @@ public class UserTOAdapter {
         userTORequest.getPlainAttrs().putAll(userTO.getPlainAttrMap());
         userTORequest.getDerAttrs().putAll(userTO.getDerAttrMap());
         userTORequest.getVirAttrs().putAll(userTO.getVirAttrMap());
-        
+
         userTORequest.getResources().addAll(userTO.getResources());
-               
+
         userTORequest.getMemberships().addAll(userTO.getMemberships());
 
+        userTORequest.getAuxClasses().addAll(userTO.getAuxClasses());
+
         return userTORequest;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/43c4d8ea/client/enduser/src/main/java/org/apache/syncope/client/enduser/model/UserTORequest.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/model/UserTORequest.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/model/UserTORequest.java
index be841a4..0af6a76 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/model/UserTORequest.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/model/UserTORequest.java
@@ -52,9 +52,11 @@ public class UserTORequest implements Serializable {
     private Map<String, AttrTO> virAttrs = new HashMap<>();
 
     private Set<String> resources = new HashSet<>();
-    
+
     private List<MembershipTO> memberships = new ArrayList<>();
 
+    private List<String> auxClasses = new ArrayList<>();
+
     public UserTORequest() {
     }
 
@@ -137,7 +139,7 @@ public class UserTORequest implements Serializable {
     public void setResources(final Set<String> resources) {
         this.resources = resources;
     }
-    
+
     public List<MembershipTO> getMemberships() {
         return memberships;
     }
@@ -146,6 +148,14 @@ public class UserTORequest implements Serializable {
         this.memberships = memberships;
     }
 
+    public List<String> getAuxClasses() {
+        return this.auxClasses;
+    }
+
+    public void setAuxClasses(final List<String> auxClasses) {
+        this.auxClasses = auxClasses;
+    }
+
     public UserTORequest key(final Long value) {
         this.key = value;
         return this;
@@ -196,6 +206,11 @@ public class UserTORequest implements Serializable {
         return this;
     }
 
+    public UserTORequest auxClasses(final List<String> value) {
+        this.auxClasses = value;
+        return this;
+    }
+
     @Override
     public String toString() {
         return ToStringBuilder.reflectionToString(this);

http://git-wip-us.apache.org/repos/asf/syncope/blob/43c4d8ea/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java
index 12ce2f1..4783b79 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java
@@ -19,6 +19,7 @@
 package org.apache.syncope.client.enduser.resources;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.core.Response;
@@ -72,15 +73,22 @@ public class SchemaResource extends AbstractBaseResource {
 
             final AnyTypeTO anyTypeUserTO = anyTypeService.read(AnyTypeKind.USER.name());
 
+            List<String> classes = new ArrayList<>();
+            String parameter = attributes.getParameters().get("anyTypeClass").toString();
+            if (parameter != null) {
+                classes.add(parameter);
+            } else {
+                classes = anyTypeUserTO.getClasses();
+            }
             final List<PlainSchemaTO> plainSchemas = schemaService.list(
                     new SchemaQuery.Builder().type(SchemaType.PLAIN).
-                    anyTypeClasses(anyTypeUserTO.getClasses()).build());
+                    anyTypeClasses(classes).build());
             final List<DerSchemaTO> derSchemas = schemaService.list(
                     new SchemaQuery.Builder().type(SchemaType.DERIVED).
-                    anyTypeClasses(anyTypeUserTO.getClasses()).build());
+                    anyTypeClasses(classes).build());
             final List<VirSchemaTO> virSchemas = schemaService.list(
                     new SchemaQuery.Builder().type(SchemaType.VIRTUAL).
-                    anyTypeClasses(anyTypeUserTO.getClasses()).build());
+                    anyTypeClasses(classes).build());
 
             response.setWriteCallback(new AbstractResource.WriteCallback() {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/43c4d8ea/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SyncopeAnyClassTypeResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SyncopeAnyClassTypeResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SyncopeAnyClassTypeResource.java
new file mode 100644
index 0000000..c2c3efc
--- /dev/null
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SyncopeAnyClassTypeResource.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.resources;
+
+import java.io.IOException;
+import java.util.List;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.enduser.adapters.GroupTOAdapter;
+import org.apache.syncope.common.lib.to.AnyTypeClassTO;
+import org.apache.syncope.common.rest.api.service.AnyTypeClassService;
+import org.apache.syncope.core.misc.serialization.POJOHelper;
+import org.apache.wicket.request.resource.AbstractResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SyncopeAnyClassTypeResource extends AbstractBaseResource {
+
+    private static final long serialVersionUID = 7475706378304995200L;
+
+    private static final Logger LOG = LoggerFactory.getLogger(SyncopeAnyClassTypeResource.class);
+
+    private final AnyTypeClassService anyTypeClassService;
+
+    private final GroupTOAdapter groupTOAdapter;
+
+    public SyncopeAnyClassTypeResource() {
+        groupTOAdapter = new GroupTOAdapter();
+        anyTypeClassService = SyncopeEnduserSession.get().getService(AnyTypeClassService.class);
+    }
+
+    @Override
+    protected ResourceResponse newResourceResponse(final Attributes attributes) {
+
+        LOG.debug("Get all available auxiliary classes");
+
+        ResourceResponse response = new ResourceResponse();
+
+        try {
+
+            HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
+            if (!xsrfCheck(request)) {
+                LOG.error("XSRF TOKEN does not match");
+                response.setError(Response.Status.BAD_REQUEST.getStatusCode(), "XSRF TOKEN does not match");
+                return response;
+            }
+
+            final List<AnyTypeClassTO> anyTypeClassTOs = anyTypeClassService.list();
+
+            response.setWriteCallback(new AbstractResource.WriteCallback() {
+
+                @Override
+                public void writeData(final Attributes attributes) throws IOException {
+                    attributes.getResponse().write(POJOHelper.serialize(anyTypeClassTOs));
+                }
+            });
+            response.setStatusCode(Response.Status.OK.getStatusCode());
+        } catch (Exception e) {
+            LOG.error("Error retrieving available auxiliary classes", e);
+            response.setError(Response.Status.BAD_REQUEST.getStatusCode(), new StringBuilder()
+                    .append("ErrorMessage{{ ")
+                    .append(e.getMessage())
+                    .append(" }}")
+                    .toString());
+        }
+        return response;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/43c4d8ea/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SyncopeAnyTypeResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SyncopeAnyTypeResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SyncopeAnyTypeResource.java
new file mode 100644
index 0000000..7d2bcde
--- /dev/null
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SyncopeAnyTypeResource.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.enduser.resources;
+
+import java.io.IOException;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.common.lib.to.AnyTypeTO;
+import org.apache.syncope.common.rest.api.service.AnyTypeService;
+import org.apache.syncope.core.misc.serialization.POJOHelper;
+import org.apache.wicket.request.resource.AbstractResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SyncopeAnyTypeResource extends AbstractBaseResource {
+
+    private static final long serialVersionUID = 7475706378304995200L;
+
+    private static final Logger LOG = LoggerFactory.getLogger(SyncopeAnyTypeResource.class);
+
+    private final AnyTypeService anyTypeService;
+
+    public SyncopeAnyTypeResource() {
+        anyTypeService = SyncopeEnduserSession.get().getService(AnyTypeService.class);
+    }
+
+    @Override
+    protected ResourceResponse newResourceResponse(final Attributes attributes) {
+
+        LOG.debug("Get all available auxiliary classes");
+
+        ResourceResponse response = new ResourceResponse();
+
+        try {
+
+            HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
+            if (!xsrfCheck(request)) {
+                LOG.error("XSRF TOKEN does not match");
+                response.setError(Response.Status.BAD_REQUEST.getStatusCode(), "XSRF TOKEN does not match");
+                return response;
+            }
+
+            String kind = attributes.getParameters().get(0).toString();
+            final AnyTypeTO anyTypeTO = anyTypeService.read(kind);
+
+            response.setWriteCallback(new AbstractResource.WriteCallback() {
+
+                @Override
+                public void writeData(final Attributes attributes) throws IOException {
+                    attributes.getResponse().write(POJOHelper.serialize(anyTypeTO));
+                }
+            });
+            response.setStatusCode(Response.Status.OK.getStatusCode());
+        } catch (Exception e) {
+            LOG.error("Error retrieving available any type details", e);
+            response.setError(Response.Status.BAD_REQUEST.getStatusCode(), new StringBuilder()
+                    .append("ErrorMessage{{ ")
+                    .append(e.getMessage())
+                    .append(" }}")
+                    .toString());
+        }
+        return response;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/43c4d8ea/client/enduser/src/main/resources/META-INF/resources/app/index.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/index.html b/client/enduser/src/main/resources/META-INF/resources/app/index.html
index b888051..6ab3b31 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/index.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/index.html
@@ -86,6 +86,7 @@ under the License.
     <script src="js/services/infoService.js"></script>
     <script src="js/services/resourceService.js"></script>
     <script src="js/services/groupService.js"></script>
+    <script src="js/services/anyService.js"></script>
     <script src="js/services/captchaService.js"></script>
     <!--controllers-->
     <script src="js/controllers/HomeController.js"></script>
@@ -105,6 +106,7 @@ under the License.
     <script src="js/directives/captcha.js"></script>
     <script src="js/directives/resources.js"></script>
     <script src="js/directives/groups.js"></script>
+    <script src="js/directives/auxClasses.js"></script>
     <!--filters-->
     <script src="js/filters/propsFilter.js"></script>
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/43c4d8ea/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/UserController.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/UserController.js b/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/UserController.js
index 1d35bc6..0390b7a 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/UserController.js
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/controllers/UserController.js
@@ -21,9 +21,9 @@
 
 angular.module("self").controller("UserController", ['$scope', '$rootScope', '$location', '$compile', 'AuthService',
   'UserSelfService', 'SchemaService', 'RealmService', 'ResourceService', 'SecurityQuestionService', 'CaptchaService',
-  'GroupService',
+  'GroupService', 'AnyService',
   function ($scope, $rootScope, $location, $compile, AuthService, UserSelfService, SchemaService, RealmService,
-          ResourceService, SecurityQuestionService, CaptchaService, GroupService) {
+          ResourceService, SecurityQuestionService, CaptchaService, GroupService, AnyService) {
 
     $scope.user = {};
     $scope.confirmPassword = {
@@ -48,104 +48,111 @@ angular.module("self").controller("UserController", ['$scope', '$rootScope', '$l
         virSchemas: [],
         resources: [],
         groups: [],
+        auxClasses: [],
+        anyUserType: [],
         errorMessage: '',
         attributeTable: {},
         virtualAttributeTable: {},
         selectedResources: [],
-        selectedGroups: []
+        selectedGroups: [],
+        selectedAuxClasses: []
       };
 
-      var initSchemas = function () {
+      var initUserSchemas = function (anyTypeClass) {
         // initialization is done here synchronously to have all schema fields populated correctly
-        SchemaService.getUserSchemas().then(function (schemas) {
-          $scope.dynamicForm.plainSchemas = schemas.plainSchemas;
-          $scope.dynamicForm.derSchemas = schemas.derSchemas;
-          $scope.dynamicForm.virSchemas = schemas.virSchemas;
+        SchemaService.getUserSchemas(anyTypeClass).then(function (schemas) {
+          //initializing user schemas values
+          initSchemaValues(schemas);
+        }, function (response) {
+          var errorMessage;
+          // parse error response 
+          if (response !== undefined) {
+            errorMessage = response.split("ErrorMessage{{")[1];
+            errorMessage = errorMessage.split("}}")[0];
+          }
+          console.log("Error retrieving user schemas: ", errorMessage);
+        });
+      };
 
-          // initialize plain attributes
-          for (var i = 0; i < schemas.plainSchemas.length; i++) {
+      var initSchemaValues = function (schemas) {
+        // initialize plain attributes
+        for (var i = 0; i < schemas.plainSchemas.length; i++) {
 
-            var plainSchemaKey = schemas.plainSchemas[i].key;
+          var plainSchemaKey = schemas.plainSchemas[i].key;
 
-            if (!$scope.user.plainAttrs[plainSchemaKey]) {
+          if (!$scope.user.plainAttrs[plainSchemaKey]) {
 
-              $scope.user.plainAttrs[plainSchemaKey] = {
-                schema: plainSchemaKey,
-                values: [],
-                readonly: schemas.plainSchemas[i].readonly
-              };
+            $scope.user.plainAttrs[plainSchemaKey] = {
+              schema: plainSchemaKey,
+              values: [],
+              readonly: schemas.plainSchemas[i].readonly
+            };
 
-              // initialize multivalue schema and support table: create mode, only first value
-              if (schemas.plainSchemas[i].multivalue) {
-                $scope.dynamicForm.attributeTable[schemas.plainSchemas[i].key] = {
-                  fields: [schemas.plainSchemas[i].key + "_" + 0]
-                };
-              }
-            } else if (schemas.plainSchemas[i].multivalue) {
-              // initialize multivalue schema and support table: update mode, all provided values
+            // initialize multivalue schema and support table: create mode, only first value
+            if (schemas.plainSchemas[i].multivalue) {
               $scope.dynamicForm.attributeTable[schemas.plainSchemas[i].key] = {
                 fields: [schemas.plainSchemas[i].key + "_" + 0]
               };
-              // add other values
-              for (var j = 1; j < $scope.user.plainAttrs[plainSchemaKey].values.length; j++) {
-                $scope.dynamicForm.attributeTable[schemas.plainSchemas[i].key].fields.push(schemas.plainSchemas[i].key + "_" + j);
-              }
+            }
+          } else if (schemas.plainSchemas[i].multivalue) {
+            // initialize multivalue schema and support table: update mode, all provided values
+            $scope.dynamicForm.attributeTable[schemas.plainSchemas[i].key] = {
+              fields: [schemas.plainSchemas[i].key + "_" + 0]
+            };
+            // add other values
+            for (var j = 1; j < $scope.user.plainAttrs[plainSchemaKey].values.length; j++) {
+              $scope.dynamicForm.attributeTable[schemas.plainSchemas[i].key].fields.push(schemas.plainSchemas[i].key + "_" + j);
             }
           }
+        }
 
-          // initialize derived attributes
-          for (var i = 0; i < schemas.derSchemas.length; i++) {
+        // initialize derived attributes
+        for (var i = 0; i < schemas.derSchemas.length; i++) {
 
-            var derSchemaKey = schemas.derSchemas[i].key;
+          var derSchemaKey = schemas.derSchemas[i].key;
 
-            if (!$scope.user.derAttrs[derSchemaKey]) {
+          if (!$scope.user.derAttrs[derSchemaKey]) {
 
-              $scope.user.derAttrs[derSchemaKey] = {
-                schema: derSchemaKey,
-                values: [],
-                readonly: true
-              };
+            $scope.user.derAttrs[derSchemaKey] = {
+              schema: derSchemaKey,
+              values: [],
+              readonly: true
+            };
 
-            }
           }
+        }
 
-          // initialize virtual attributes
-          for (var i = 0; i < schemas.virSchemas.length; i++) {
+        // initialize virtual attributes
+        for (var i = 0; i < schemas.virSchemas.length; i++) {
 
-            var virSchemaKey = schemas.virSchemas[i].key;
+          var virSchemaKey = schemas.virSchemas[i].key;
 
-            if (!$scope.user.virAttrs[virSchemaKey]) {
+          if (!$scope.user.virAttrs[virSchemaKey]) {
 
-              $scope.user.virAttrs[virSchemaKey] = {
-                schema: virSchemaKey,
-                values: [],
-                readonly: schemas.virSchemas[i].readonly
-              };
-              // initialize multivalue schema and support table: create mode, only first value
-              $scope.dynamicForm.virtualAttributeTable[schemas.virSchemas[i].key] = {
-                fields: [schemas.virSchemas[i].key + "_" + 0]
-              };
-            } else {
-              // initialize multivalue schema and support table: update mode, all provided values
-              $scope.dynamicForm.virtualAttributeTable[schemas.virSchemas[i].key] = {
-                fields: [schemas.virSchemas[i].key + "_" + 0]
-              };
-              // add other values
-              for (var j = 1; j < $scope.user.virAttrs[virSchemaKey].values.length; j++) {
-                $scope.dynamicForm.virtualAttributeTable[schemas.virSchemas[i].key].fields.push(schemas.virSchemas[i].key + "_" + j);
-              }
+            $scope.user.virAttrs[virSchemaKey] = {
+              schema: virSchemaKey,
+              values: [],
+              readonly: schemas.virSchemas[i].readonly
+            };
+            // initialize multivalue schema and support table: create mode, only first value
+            $scope.dynamicForm.virtualAttributeTable[schemas.virSchemas[i].key] = {
+              fields: [schemas.virSchemas[i].key + "_" + 0]
+            };
+          } else {
+            // initialize multivalue schema and support table: update mode, all provided values
+            $scope.dynamicForm.virtualAttributeTable[schemas.virSchemas[i].key] = {
+              fields: [schemas.virSchemas[i].key + "_" + 0]
+            };
+            // add other values
+            for (var j = 1; j < $scope.user.virAttrs[virSchemaKey].values.length; j++) {
+              $scope.dynamicForm.virtualAttributeTable[schemas.virSchemas[i].key].fields.push(schemas.virSchemas[i].key + "_" + j);
             }
           }
-
-        }, function (response) {
-          var errorMessage;
-          // parse error response 
-          if (response !== undefined) {
-            errorMessage = response.split("ErrorMessage{{")[1];
-            errorMessage = errorMessage.split("}}")[0];
-          }
-          console.log("Error retrieving user schemas: ", errorMessage);
-        });
+        }
+        //appending new schemas
+        $scope.dynamicForm.plainSchemas = $scope.dynamicForm.plainSchemas.concat(schemas.plainSchemas);
+        $scope.dynamicForm.derSchemas = $scope.dynamicForm.derSchemas.concat(schemas.derSchemas);
+        $scope.dynamicForm.virSchemas = $scope.dynamicForm.virSchemas.concat(schemas.virSchemas);
       };
 
       var initSecurityQuestions = function () {
@@ -188,6 +195,26 @@ angular.module("self").controller("UserController", ['$scope', '$rootScope', '$l
         });
       };
 
+      var initAuxClasses = function () {
+
+        //fetching default user classes, that should remain in any case
+        AnyService.getAnyType("USER").then(function (response) {
+          $scope.dynamicForm.anyUserType = response.classes;
+          AnyService.getAuxClasses().then(function (response) {
+            for (var i = 0; i < response.length; i++) {
+              //we should only add schemas that aren't in the anyUserType
+              if ($scope.dynamicForm.anyUserType.indexOf(response[i].key) == -1) {
+                $scope.dynamicForm.auxClasses.push(response[i].key);
+              }
+            }
+          }, function (e) {
+            $scope.showError("An error occur during retrieving auxiliary classes " + e, $scope.notification)
+          });
+        }, function (e) {
+          $scope.showError("An error occur during retrieving auxiliary classes " + e, $scope.notification)
+        });
+      };
+
       var readUser = function () {
         UserSelfService.read().then(function (response) {
           $scope.user = response;
@@ -203,11 +230,48 @@ angular.module("self").controller("UserController", ['$scope', '$rootScope', '$l
                       "groupName": $scope.user.memberships[index]["groupName"]
                     });
           }
-        }, function () {
-          console.log("Error");
+          //initialize already assigned auxiliary classes
+          $scope.dynamicForm.selectedAuxClasses = $scope.user.auxClasses;
+          //we need to initialize axiliar attribute schemas
+          for (var index in $scope.user.auxClasses) {
+            $scope.$emit("auxClassAdded", $scope.user.auxClasses[index]);
+          }
+        }, function (e) {
+          console.log("Error during user read ", e);
         });
       };
 
+      var removeUserSchemas = function (anyTypeClass) {
+
+        //removing plain schemas
+        for (var i = 0; i < $scope.dynamicForm.plainSchemas.length; i++) {
+          if ($scope.dynamicForm.plainSchemas[i].anyTypeClass == anyTypeClass) {
+            //cleaning both form and user model
+            delete $scope.user.plainAttrs[$scope.dynamicForm.plainSchemas[i].key];
+            $scope.dynamicForm.plainSchemas.splice(i, 1);
+            i--;
+          }
+        }
+        //removing derived schemas
+        for (var i = 0; i < $scope.dynamicForm.derSchemas.length; i++) {
+          if ($scope.dynamicForm.derSchemas[i].anyTypeClass == anyTypeClass) {
+            //cleaning both form and user model
+            delete $scope.user.derAttrs[$scope.dynamicForm.derSchemas[i].key];
+            $scope.dynamicForm.derSchemas.splice(i, 1);
+            i--;
+          }
+        }
+        //removing virtual schemas
+        for (var i = 0; i < $scope.dynamicForm.virSchemas.length; i++) {
+          if ($scope.dynamicForm.virSchemas[i].anyTypeClass == anyTypeClass) {
+            //cleaning both form and user model
+            delete $scope.user.virAttrs[$scope.dynamicForm.virSchemas[i].key];
+            $scope.dynamicForm.virSchemas.splice(i, 1);
+            i--;
+          }
+        }
+      };
+
       if ($scope.createMode) {
 
         $scope.user = {
@@ -219,10 +283,15 @@ angular.module("self").controller("UserController", ['$scope', '$rootScope', '$l
           plainAttrs: {},
           derAttrs: {},
           virAttrs: {},
-          resources: []
+          resources: [],
+          auxClasses: []
         };
         // retrieve user realm or all available realms
         initUserRealm();
+        // initialize auxiliary schemas in case of pre-existing classes
+        for (var index in $scope.dynamicForm.selectedAuxClasses) {
+          initUserSchemas($scope.dynamicForm.selectedAuxClasses[index]);
+        }
       } else {
         // read user from syncope core
         readUser();
@@ -231,12 +300,26 @@ angular.module("self").controller("UserController", ['$scope', '$rootScope', '$l
       initRealms();
       //retrieve security available questions
       initSecurityQuestions();
+      //initialize available auxiliary classes
+      initAuxClasses();
       // initialize user attributes starting from any object schemas
-      initSchemas();
+      initUserSchemas();
       // initialize available resources
       initResources();
       //initialize available groups
       initGroups();
+
+      //Event management
+      $scope.$on('auxClassAdded', function (event, auxClass) {
+        if (auxClass)
+          initUserSchemas(auxClass);
+      });
+
+      $scope.$on('auxClassRemoved', function (event, auxClass) {
+        if (auxClass)
+          removeUserSchemas(auxClass);
+      });
+
     };
 
     $scope.saveUser = function (user) {
@@ -298,63 +381,63 @@ angular.module("self").controller("UserController", ['$scope', '$rootScope', '$l
         $scope.showError("Error: " + (errorMessage || response), $scope.notification);
         return;
       });
-    },
-            $scope.retrieveSecurityQuestion = function (user) {
-              if ($rootScope.pwdResetRequiringSecurityQuestions) {
-                if (user && user.username && user.username.length) {
-                  return SecurityQuestionService.
-                          getSecurityQuestionByUser(user.username).then(function (data) {
-                    $scope.userSecurityQuestion = data.content;
-                  }, function (response) {
-                    var errorMessage;
-                    // parse error response 
-                    if (response !== undefined) {
-                      errorMessage = response.split("ErrorMessage{{")[1];
-                      errorMessage = errorMessage.split("}}")[0];
-                      $scope.userSecurityQuestion = "";
-                    }
-                    $scope.showError("Error retrieving user security question: " + errorMessage, $scope.notification);
-                  });
-                }
-                else {
-                  $scope.userSecurityQuestion = "";
-                }
-              }
-            },
-            $scope.resetPassword = function (user) {
-              if (user && user.username) {
-                $scope.retrieveSecurityQuestion(user);
-                CaptchaService.validate($scope.captchaInput).then(function (response) {
-                  if (!(response === 'true')) {
-                    $scope.showError("Captcha inserted is not valid, please digit the correct captcha", $scope.notification);
-                    return;
-                  }
-                  UserSelfService.passwordReset(user).then(function (data) {
-                    $scope.showSuccess(data, $scope.notification);
-                    $location.path('/self');
-                  }, function (response) {
-                    var errorMessage;
-                    // parse error response 
-                    if (response !== undefined) {
-                      errorMessage = response.split("ErrorMessage{{")[1];
-                      errorMessage = errorMessage.split("}}")[0];
-                      $scope.showError("An error occured during password reset: " + errorMessage, $scope.notification);
-                      //we need to refresh captcha after a valid request
-                      $scope.$broadcast("refreshCaptcha");
-                    }
-                  });
-                }, function (response) {
-                  var errorMessage;
-                  // parse error response 
-                  if (response !== undefined) {
-                    errorMessage = response.split("ErrorMessage{{")[1];
-                    errorMessage = errorMessage.split("}}")[0];
-                  }
-                  $scope.showError("Error: " + (errorMessage || response), $scope.notification);
-                  return;
-                });
-              } else {
-                $scope.showError("You should use a valid and non-empty username", $scope.notification);
-              }
-            };
+    };
+    $scope.retrieveSecurityQuestion = function (user) {
+      if ($rootScope.pwdResetRequiringSecurityQuestions) {
+        if (user && user.username && user.username.length) {
+          return SecurityQuestionService.
+                  getSecurityQuestionByUser(user.username).then(function (data) {
+            $scope.userSecurityQuestion = data.content;
+          }, function (response) {
+            var errorMessage;
+            // parse error response 
+            if (response !== undefined) {
+              errorMessage = response.split("ErrorMessage{{")[1];
+              errorMessage = errorMessage.split("}}")[0];
+              $scope.userSecurityQuestion = "";
+            }
+            $scope.showError("Error retrieving user security question: " + errorMessage, $scope.notification);
+          });
+        }
+        else {
+          $scope.userSecurityQuestion = "";
+        }
+      }
+    };
+    $scope.resetPassword = function (user) {
+      if (user && user.username) {
+        $scope.retrieveSecurityQuestion(user);
+        CaptchaService.validate($scope.captchaInput).then(function (response) {
+          if (!(response === 'true')) {
+            $scope.showError("Captcha inserted is not valid, please digit the correct captcha", $scope.notification);
+            return;
+          }
+          UserSelfService.passwordReset(user).then(function (data) {
+            $scope.showSuccess(data, $scope.notification);
+            $location.path('/self');
+          }, function (response) {
+            var errorMessage;
+            // parse error response 
+            if (response !== undefined) {
+              errorMessage = response.split("ErrorMessage{{")[1];
+              errorMessage = errorMessage.split("}}")[0];
+              $scope.showError("An error occured during password reset: " + errorMessage, $scope.notification);
+              //we need to refresh captcha after a valid request
+              $scope.$broadcast("refreshCaptcha");
+            }
+          });
+        }, function (response) {
+          var errorMessage;
+          // parse error response 
+          if (response !== undefined) {
+            errorMessage = response.split("ErrorMessage{{")[1];
+            errorMessage = errorMessage.split("}}")[0];
+          }
+          $scope.showError("Error: " + (errorMessage || response), $scope.notification);
+          return;
+        });
+      } else {
+        $scope.showError("You should use a valid and non-empty username", $scope.notification);
+      }
+    };
   }]);

http://git-wip-us.apache.org/repos/asf/syncope/blob/43c4d8ea/client/enduser/src/main/resources/META-INF/resources/app/js/directives/auxClasses.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/directives/auxClasses.js b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/auxClasses.js
new file mode 100644
index 0000000..99a6c1e
--- /dev/null
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/auxClasses.js
@@ -0,0 +1,60 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+'use strict';
+
+angular.module('self')
+        .directive('auxiliary', function () {
+          return {
+            restrict: 'E',
+            templateUrl: 'views/auxClasses.html',
+            scope: {
+              dynamicForm: "=form",
+              user: "="
+            },
+            controller: function ($scope, $filter) {
+
+              $scope.init = function () {
+                if (!$scope.user.auxClasses) {
+                  $scope.user.auxClasses = new Array();                  
+                }
+                $scope.auxClassDisabled = false;
+              };
+
+              $scope.addAuxClass = function (item, model) {
+                var auxClass = item;
+                $scope.user.auxClasses.push(auxClass);
+                $scope.$emit("auxClassAdded", auxClass);
+              };
+
+              $scope.removeAuxClass = function (item, model) {
+                var auxClassIndex = $scope.getIndex(item);
+                $scope.user.auxClasses.splice(auxClassIndex, 1);
+                $scope.$emit("auxClassRemoved", item);
+              };
+
+              $scope.getIndex = function (selectedAuxClass) {
+                var auxClassIndex = $scope.user.auxClasses.map(function (auxClassName) {
+                  return auxClassName;
+                }).indexOf(selectedAuxClass);
+                return auxClassIndex;
+              };
+
+            }
+          };
+        });

http://git-wip-us.apache.org/repos/asf/syncope/blob/43c4d8ea/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicPlainAttributes.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicPlainAttributes.js b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicPlainAttributes.js
index 672f024..4d11a49 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicPlainAttributes.js
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicPlainAttributes.js
@@ -20,26 +20,26 @@
 
 angular.module('self')
         .directive('dynamicPlainAttributes', function (SchemaService) {
-            return {
-              restrict: 'E',
-              templateUrl: 'views/dynamicPlainAttributes.html',
-              scope: {
-                dynamicForm: "=form",
-                user: "="
-              },
-              controller: function ($scope) {
+          return {
+            restrict: 'E',
+            templateUrl: 'views/dynamicPlainAttributes.html',
+            scope: {
+              dynamicForm: "=form",
+              user: "="
+            },
+            controller: function ($scope) {
 
-                $scope.addAttributeField = function (plainSchemaKey) {
-                  console.log("Add PLAIN value: ", plainSchemaKey + "_" + ($scope.dynamicForm.attributeTable[plainSchemaKey].fields.length));
-                  $scope.dynamicForm.attributeTable[plainSchemaKey].fields.push(plainSchemaKey + "_" + ($scope.dynamicForm.attributeTable[plainSchemaKey].fields.length));
-                };
+              $scope.addAttributeField = function (plainSchemaKey) {
+                console.log("Add PLAIN value: ", plainSchemaKey + "_" + ($scope.dynamicForm.attributeTable[plainSchemaKey].fields.length));
+                $scope.dynamicForm.attributeTable[plainSchemaKey].fields.push(plainSchemaKey + "_" + ($scope.dynamicForm.attributeTable[plainSchemaKey].fields.length));
+              };
 
-                $scope.removeAttributeField = function (plainSchemaKey, index) {
-                  console.log("Remove PLAIN value: " + plainSchemaKey + " attribute index: " + index);
-                  $scope.dynamicForm.attributeTable[plainSchemaKey].fields.splice(index, 1);
-                  // clean user model
-                  $scope.user.plainAttrs[plainSchemaKey].values.splice(index, 1);
-                };
-              }
-            };
-          });
+              $scope.removeAttributeField = function (plainSchemaKey, index) {
+                console.log("Remove PLAIN value: " + plainSchemaKey + " attribute index: " + index);
+                $scope.dynamicForm.attributeTable[plainSchemaKey].fields.splice(index, 1);
+                // clean user model
+                $scope.user.plainAttrs[plainSchemaKey].values.splice(index, 1);
+              };
+            }
+          };
+        });

http://git-wip-us.apache.org/repos/asf/syncope/blob/43c4d8ea/client/enduser/src/main/resources/META-INF/resources/app/js/services/anyService.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/services/anyService.js b/client/enduser/src/main/resources/META-INF/resources/app/js/services/anyService.js
new file mode 100644
index 0000000..f62841c
--- /dev/null
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/services/anyService.js
@@ -0,0 +1,53 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+angular.module('self')
+        .factory('AnyService', ['$resource', '$q', '$http',
+          function ($auxClass, $q, $http) {
+
+            var any = {};
+
+            any.getAuxClasses = function () {
+              return  $http.get("/syncope-enduser/api/auxiliaryClasses")
+                      .then(function (response) {
+                        console.log("auxiliaryClasses API response: ", response);
+                        return response.data;
+                      }, function (response) {
+                        console.log("Something went wrong during auxiliaryClasses retrieval, exit with status: ", response);
+                        return $q.reject(response.data || response.statusText);
+                      });
+            };
+
+            any.getAnyType = function (kind) {
+              return  $http.get("/syncope-enduser/api/anyTypes/" + encodeURI(kind))
+                      .then(function (response) {
+                        console.log("anyType user API response: ", response);
+                        return response.data;
+                      }, function (response) {
+                        console.log("Something went wrong during anyType user API retrieval, exit with status: ", response);
+                        return $q.reject(response.data || response.statusText);
+                      });
+            };
+
+            return any;
+          }]);
+
+

http://git-wip-us.apache.org/repos/asf/syncope/blob/43c4d8ea/client/enduser/src/main/resources/META-INF/resources/app/js/services/schemaService.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/services/schemaService.js b/client/enduser/src/main/resources/META-INF/resources/app/js/services/schemaService.js
index be9f510..faec8c2 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/js/services/schemaService.js
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/services/schemaService.js
@@ -25,9 +25,11 @@ angular.module('self')
 
             var schemaService = {};
 
-            schemaService.getUserSchemas = function () {
+            schemaService.getUserSchemas = function (anyTypeClass) {
 
-              return  $http.get("/syncope-enduser/api/schemas")
+              var classParam = anyTypeClass ? "?anyTypeClass=" + encodeURI(anyTypeClass) : "";
+
+              return  $http.get("/syncope-enduser/api/schemas" + classParam)
                       .then(function (response) {
                         console.log("schemaAPI response: ", response);
                         return response.data;

http://git-wip-us.apache.org/repos/asf/syncope/blob/43c4d8ea/client/enduser/src/main/resources/META-INF/resources/app/views/auxClasses.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/auxClasses.html b/client/enduser/src/main/resources/META-INF/resources/app/views/auxClasses.html
new file mode 100644
index 0000000..1c3bcd6
--- /dev/null
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/auxClasses.html
@@ -0,0 +1,10 @@
+<div ng-init='init()'>
+  <ui-select on-select="addAuxClass($item, $model)" on-remove="removeAuxClass($item, $model)" multiple
+             ng-model="dynamicForm.selectedAuxClasses" theme="select2" class="attribute-ui-select" 
+             ng-disabled="{{auxClassDisabled}}">
+    <ui-select-match placeholder="Click to select auxiliary class...">{{$item}}</ui-select-match>
+    <ui-select-choices repeat="auxClass in dynamicForm.auxClasses">
+      <div ng-bind-html="auxClass | highlight: $select.search"></div>
+    </ui-select-choices>
+  </ui-select>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/43c4d8ea/client/enduser/src/main/resources/META-INF/resources/app/views/user-groups.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/user-groups.html b/client/enduser/src/main/resources/META-INF/resources/app/views/user-groups.html
index 26ca1ce..591b68c 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/user-groups.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/user-groups.html
@@ -31,6 +31,10 @@ under the License.
   <label>Groups</label><br/>
   <groups form="dynamicForm" user="user"></groups>
 </div>
+<div id="attribute" class="form-group row upper-select">
+  <label>Auxilary Classes</label><br/>
+  <auxiliary form="dynamicForm" user="user"></auxiliary>
+</div>
 <div id="attribute" class="form-group row">
   <navigation-buttons ng-show="createMode" next="create.plainSchemas" previous="create.credentials"></navigation-buttons>
   <navigation-buttons ng-show="!createMode" next="update.plainSchemas" previous="update.credentials"></navigation-buttons>