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/02/02 12:30:11 UTC

syncope git commit: [SYNCOPE-719] Added front end validation layer

Repository: syncope
Updated Branches:
  refs/heads/master bd757e36d -> 78483975c


[SYNCOPE-719] Added front end validation layer


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

Branch: refs/heads/master
Commit: 78483975cbea586fe8f5af581a1c69e05709c8bb
Parents: bd757e3
Author: giacomolm <gi...@hotmail.it>
Authored: Tue Feb 2 12:29:52 2016 +0100
Committer: giacomolm <gi...@hotmail.it>
Committed: Tue Feb 2 12:29:52 2016 +0100

----------------------------------------------------------------------
 .../META-INF/resources/app/css/app.css          | 15 ++++-
 .../META-INF/resources/app/css/editUser.css     |  8 +++
 .../resources/META-INF/resources/app/index.html |  7 +-
 .../resources/META-INF/resources/app/js/app.js  |  8 ++-
 .../app/js/controllers/UserController.js        |  8 +--
 .../resources/app/js/directives/equals.js       | 49 --------------
 .../app/js/directives/navigationButtons.js      | 38 ++++++++---
 .../resources/app/js/directives/validate.js     | 37 +++++++++++
 .../app/js/directives/validationMessage.js      | 36 +++++++++++
 .../META-INF/resources/app/js/policy/policy.js  | 67 ++++++++++++++++++++
 .../resources/app/js/policy/policyValidator.js  | 42 ++++++++++++
 .../resources/app/js/util/genericUtil.js        | 57 +++++++++++++++++
 .../app/views/dynamicPlainAttribute.html        | 28 ++++----
 .../app/views/dynamicPlainAttributes.html       |  1 +
 .../META-INF/resources/app/views/editUser.html  | 16 ++---
 .../resources/app/views/navigationButtons.html  |  2 +-
 .../resources/app/views/user-credentials.html   | 15 ++---
 .../resources/app/views/validationMessage.html  | 22 +++++++
 pom.xml                                         |  4 +-
 19 files changed, 362 insertions(+), 98 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/client/enduser/src/main/resources/META-INF/resources/app/css/app.css
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/css/app.css b/client/enduser/src/main/resources/META-INF/resources/app/css/app.css
index afc71c7..e42387d 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/css/app.css
+++ b/client/enduser/src/main/resources/META-INF/resources/app/css/app.css
@@ -57,4 +57,17 @@ under the License.
 #captchaImg{
   display: block;
   margin: 0 auto;
-}
\ No newline at end of file
+}
+
+.disable-link{
+  pointer-events: none;
+  cursor: default;
+}
+
+.treasure-overlay-spinner-container{
+  z-index : 3;
+}
+
+treasure-overlay-spinner.treasure-overlay-spinner-active-remove{
+  transition: all 150ms ease-in 0s
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/client/enduser/src/main/resources/META-INF/resources/app/css/editUser.css
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/css/editUser.css b/client/enduser/src/main/resources/META-INF/resources/app/css/editUser.css
index cd86da1..878b2c9 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/css/editUser.css
+++ b/client/enduser/src/main/resources/META-INF/resources/app/css/editUser.css
@@ -218,6 +218,14 @@ under the License.
   border-left: 10px solid #adadad;
 }
 
+.btn-breadcrumb .btn.btn-default.active:not(:last-child):after {
+  border-left: 10px solid #e6e6e6;
+}
+
+.btn-breadcrumb .btn.btn-default.active {
+  box-shadow: 0 0px 0px rgba(0, 0, 0, 0.125)
+}
+
 .breadcrumb-disabled-link {
   pointer-events: none;
   cursor: default;

http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/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 1fb18f6..7a6dc35 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
@@ -106,15 +106,20 @@ under the License.
     <script src="js/directives/dynamicVirtualAttributes.js"></script>
     <script src="js/directives/navigationButtons.js"></script>
     <script src="js/directives/loader.js"></script>
-    <script src="js/directives/equals.js"></script>
     <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>
+    <script src="js/directives/validate.js"></script>
+    <script src="js/directives/validationMessage.js"></script>
+    <!--policy-->
+    <script src="js/policy/policy.js"></script>
+    <script src="js/policy/policyValidator.js"></script>
     <!--filters-->
     <script src="js/filters/propsFilter.js"></script>
     <!--util-->
     <script src="js/util/userUtil.js"></script>
+    <script src="js/util/genericUtil.js"></script>
 
     <link rel="shortcut icon" href="img/favicon.png" type="image/png"/>
     <link href="css/login.css" rel="stylesheet" type="text/css" />

http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/app.js b/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
index f55bd33..983f4aa 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/app.js
@@ -202,6 +202,7 @@ app.config(['$stateProvider', '$urlRouterProvider', '$httpProvider',
           return config || $q.when(config);
         },
         'response': function (response) {
+          //$http.pendingRequests.length
           $rootScope.spinner.off();
           return response || $q.when(response);
         },
@@ -259,6 +260,9 @@ app.run(['$rootScope', '$location', '$cookies', '$state',
       } else if (toState.name === 'update') {
         $state.go('update.credentials');
       }
+      else {
+        $state.go(toState);
+      }
     });
 //        $rootScope.$on('$locationChangeStart', function (event, next, current) {
 //            // redirect to login page if not logged in
@@ -289,6 +293,8 @@ app.controller('ApplicationController', ['$scope', '$rootScope', 'InfoService',
       $rootScope.version = "";
       $rootScope.pwdResetRequiringSecurityQuestions = false;
       $rootScope.captchaEnabled = false;
+      //setting default validation
+      $rootScope.validationEnabled = true;
       //info settings are initialized every time an user open the login page
       InfoService.getInfo().then(
               function (response) {
@@ -375,7 +381,7 @@ app.controller('ApplicationController', ['$scope', '$rootScope', 'InfoService',
       });
       //Intercepting xhr start event
       $scope.$on('xhrStarted', function (event, next, current) {
-        $scope.hideNotifications(0);           
+        $scope.hideNotifications(0);
       });
     }
   }]);

http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/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 71f8054..05c76a2 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
@@ -302,14 +302,14 @@ angular.module("self").controller("UserController", ['$scope', '$rootScope', '$l
       initRealms();
       //retrieve security available questions
       initSecurityQuestions();
+      //initialize available groups
+      initGroups();
       //initialize available auxiliary classes
       initAuxClasses();
       // initialize user attributes starting from any object schemas
       initUserSchemas();
       // initialize available resources
       initResources();
-      //initialize available groups
-      initGroups();
 
       //Event management
       $scope.$on('auxClassAdded', function (event, auxClass) {
@@ -394,7 +394,7 @@ angular.module("self").controller("UserController", ['$scope', '$rootScope', '$l
     $scope.resetPassword = function (user) {
       if (user && user.username) {
         $scope.retrieveSecurityQuestion(user);
-        UserSelfService.passwordReset(user,$scope.captchaInput.value).then(function (data) {
+        UserSelfService.passwordReset(user, $scope.captchaInput.value).then(function (data) {
           $scope.showSuccess(data, $scope.notification);
           $location.path('/self');
         }, function (response) {
@@ -411,5 +411,5 @@ angular.module("self").controller("UserController", ['$scope', '$rootScope', '$l
       } else {
         $scope.showError("You should use a valid and non-empty username", $scope.notification);
       }
-    };
+    };    
   }]);

http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/client/enduser/src/main/resources/META-INF/resources/app/js/directives/equals.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/directives/equals.js b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/equals.js
deleted file mode 100644
index 54c2022..0000000
--- a/client/enduser/src/main/resources/META-INF/resources/app/js/directives/equals.js
+++ /dev/null
@@ -1,49 +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.
- */
-
-angular.module('self')
-        .directive('equals', function () {
-          return {
-            restrict: 'A',
-            require: '?ngModel',
-            link: function (scope, elem, attrs, ngModel) {
-              if (!ngModel)
-                return; // do nothing if no ng-model
-
-              // watch own value and re-validate on change
-              scope.$watch(attrs.ngModel, function () {
-                validate();
-              });
-
-              // observe the other value and re-validate on change
-              attrs.$observe('equals', function (val) {
-                validate();
-              });
-
-              var validate = function () {
-                // values
-                var val1 = ngModel.$viewValue;
-                var val2 = attrs.equals;
-
-                // set validity
-                ngModel.$setValidity('equals', !val1 || !val2 || val1 === val2);
-              };
-            }
-          };
-        });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/client/enduser/src/main/resources/META-INF/resources/app/js/directives/navigationButtons.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/directives/navigationButtons.js b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/navigationButtons.js
index ff3eebf..bd619d0 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/js/directives/navigationButtons.js
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/navigationButtons.js
@@ -19,13 +19,33 @@
 'use strict';
 
 angular.module('self')
-        .directive('navigationButtons', function () {
-          return {
-            restrict: 'E',
-            templateUrl: 'views/navigationButtons.html',
-            scope: {
-              next: "@",
-              previous: "@"
+        .directive('navigationButtons', ['$state','GenericUtil', 'PolicyValidator', function ($state, GenericUtil, PolicyValidator) {
+            return {
+              restrict: 'E',
+              templateUrl: 'views/navigationButtons.html',
+              scope: {
+                next: "@",
+                previous: "@",
+              },
+              controller: function ($scope) {
+
+                $scope.validateAndNext = function (event, state) {
+                  //getting the enclosing form in order to access to its name                
+                  var currentForm = GenericUtil.getEnclosingForm(event.target);
+                  if (currentForm != null) {
+                    if (PolicyValidator.validate(currentForm, $scope.$parent)) {
+                      $scope.nextTab(state);
+                    }
+                  }
+
+                };
+
+                $scope.nextTab = function (state) {
+                  //change route through parent event
+                  $state.go(state);
+                };
+              }
+
             }
-          };
-        });
+            ;
+          }]);

http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/client/enduser/src/main/resources/META-INF/resources/app/js/directives/validate.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/directives/validate.js b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/validate.js
new file mode 100644
index 0000000..514d110
--- /dev/null
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/validate.js
@@ -0,0 +1,37 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+//Live validation
+angular.module('SyncopeEnduserApp').
+        directive('validate', function () {
+          return {
+            require: 'ngModel',
+            link: function (scope, elem, attrs, ngModel) {
+              scope.$watch(attrs.ngModel, function (value) {                
+                //trigger the live validation only if user interacts with the input
+                if (scope.validationEnabled && !ngModel.$pristine) {
+                  //broadcasting from root scope element validity
+                  scope.$root.$broadcast(attrs.name, {errors: ngModel.$error});
+                }
+              });
+            }
+          };
+        });

http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/client/enduser/src/main/resources/META-INF/resources/app/js/directives/validationMessage.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/directives/validationMessage.js b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/validationMessage.js
new file mode 100644
index 0000000..4ee6252
--- /dev/null
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/validationMessage.js
@@ -0,0 +1,36 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+angular.module('SyncopeEnduserApp').
+        directive('validationMessage', function () {
+          return {
+            templateUrl: 'views/validationMessage.html',
+            scope: {
+              "name": "@"
+            },
+            link: function (scope, el, attrs) {              
+              //listener
+              scope.$on(attrs.name, function (el, errors) {
+                scope.errors = errors;               
+              });
+            }
+          };
+        });

http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/client/enduser/src/main/resources/META-INF/resources/app/js/policy/policy.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/policy/policy.js b/client/enduser/src/main/resources/META-INF/resources/app/js/policy/policy.js
new file mode 100644
index 0000000..77ef6cd
--- /dev/null
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/policy/policy.js
@@ -0,0 +1,67 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+angular.module('SyncopeEnduserApp').
+        directive('checktype', function () {
+          return {
+            require: 'ngModel',
+            link: function (scope, elem, attr, ngModel) {
+              var expectedType = attr.checktype;
+
+              //For DOM -> model validation
+              ngModel.$parsers.unshift(function (value) {
+                var valid = (typeof value) === expectedType;
+                ngModel.$setValidity('checktype', valid);
+                return valid ? value : undefined;
+              });
+
+              //For model -> DOM validation
+              ngModel.$formatters.unshift(function (value) {
+                ngModel.$setValidity('checktype', typeof value === expectedType);
+                return value;
+              });
+            }
+          };
+        });
+
+angular.module('SyncopeEnduserApp')
+        .directive('equals', function () {
+          return {
+            restrict: 'A',
+            require: '?ngModel',
+            scope: {
+              equals: "="
+            },
+            link: function (scope, elem, attrs, ngModel) {
+
+              ngModel.$parsers.unshift(function (value) {
+                var match = (value === scope.equals);
+                ngModel.$setValidity('equals', match);
+                return value;
+              });
+
+              scope.$watch("equals", function (value) {
+                var confirmPassword = value || "";
+                ngModel.$setValidity('equals', confirmPassword === ngModel.$viewValue);
+              });
+            }
+          };
+        });

http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/client/enduser/src/main/resources/META-INF/resources/app/js/policy/policyValidator.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/policy/policyValidator.js b/client/enduser/src/main/resources/META-INF/resources/app/js/policy/policyValidator.js
new file mode 100644
index 0000000..8203d81
--- /dev/null
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/policy/policyValidator.js
@@ -0,0 +1,42 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+angular.module('SyncopeEnduserApp')
+        .factory('PolicyValidator', ['GenericUtil',
+          function (GenericUtil) {
+
+            var policyValidator = {};
+
+            //scope parameter must contain the policy object, including the ones defined in the policy.js
+            policyValidator.validate = function (form, scope) {              
+              if(scope.validationEnabled){
+                var currentForm = scope[form.name];              
+                angular.forEach(currentForm.$error, function (field) {
+                  for (var i in field) {                  
+                    scope.$root.$broadcast(field[i].$name, {errors: field[i].$error});
+                  }
+                });
+                currentForm.$setSubmitted();
+                return $.isEmptyObject(currentForm.$error);
+              }
+              else return true;
+            };
+            return policyValidator;
+          }]);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/client/enduser/src/main/resources/META-INF/resources/app/js/util/genericUtil.js
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/util/genericUtil.js b/client/enduser/src/main/resources/META-INF/resources/app/js/util/genericUtil.js
new file mode 100644
index 0000000..c671f32
--- /dev/null
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/util/genericUtil.js
@@ -0,0 +1,57 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+angular.module('SyncopeEnduserApp')
+        .factory('GenericUtil', ['$resource', '$q',
+          function ($resource, $q) {
+
+            var genericUtil = {};
+
+            genericUtil.getEnclosingFormName = function (element) {
+              var form = $(element).closest('form');
+              if (form.length) {
+                return form[0].name;
+              }
+              return null;
+            };
+
+            genericUtil.getEnclosingFormController = function (element, scope) {
+              var form = $(element).closest('form');
+              if (form.length) {
+                var currentForm = scope[form[0].name];
+                if (currentForm != null) {
+                  return currentForm;
+                }
+              }
+              return null;
+            };
+
+            genericUtil.getEnclosingForm = function (element, scope) {
+              var form = $(element).closest('form');
+              if (form.length) {
+                return form[0];
+              }
+              return null;
+            };
+
+            return genericUtil;
+
+          }]);

http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttribute.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttribute.html b/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttribute.html
index be7a6a6..70ae05b 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttribute.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttribute.html
@@ -19,22 +19,23 @@ under the License.
 <div ng-switch="schema.type" >
   <input ng-switch-when="String" class="form-control" type="text"
          ng-model="user.plainAttrs[schema.key].values[index]"
-         ng-required="schema.mandatoryCondition" 
-         ng-disabled="schema.readonly" ng-init="initAttribute(schema, index)"/>
+         ng-required="{{schema.mandatoryCondition}}" validate="true"
+         ng-disabled="schema.readonly" ng-init="initAttribute(schema, index)" name="{{schema.key}}"/>
   <input ng-switch-when="Encrypted" class="form-control" type="text"
          ng-model="user.plainAttrs[schema.key].values[index]"
-         ng-required="schema.mandatoryCondition" 
-         ng-disabled="schema.readonly" ng-init="initAttribute(schema, index)"/>
+         ng-required="{{schema.mandatoryCondition}}" validate="true"
+         ng-disabled="schema.readonly" ng-init="initAttribute(schema, index)"
+         ame="{{schema.key}}"/>
   <div ng-switch-when="Boolean">
-    <input type="checkbox" ng-model="user.plainAttrs[schema.key].values[index]" ng-required="schema.mandatoryCondition" 
+    <input type="checkbox" ng-model="user.plainAttrs[schema.key].values[index]" ng-required="{{schema.mandatoryCondition}}" 
            ng-init="initAttribute(schema, index)" />
   </div>
   <input ng-switch-when="Long" class="form-control" type="number" ng-model="user.plainAttrs[schema.key].values[index]" 
-         ng-required="schema.mandatoryCondition"
-         ng-init="initAttribute(schema, index)" />
+         ng-required="{{schema.mandatoryCondition}}" validate="true"
+         ng-init="initAttribute(schema, index)" name="{{schema.key}}"/>
   <input ng-switch-when="Double" class="form-control" type="number" ng-model="user.plainAttrs[schema.key].values[index]"
-         ng-required="schema.mandatoryCondition"
-         ng-init="initAttribute(schema, index)" />
+         ng-required="{{schema.mandatoryCondition}}" validate="true"
+         ng-init="initAttribute(schema, index)" name="{{schema.key}}"/>
   <p ng-switch-when="Date" class="input-group" >
     <input type="text" class="form-control" 
            uib-datepicker-popup="{{format}}"
@@ -42,7 +43,7 @@ under the License.
            ng-change="bindDateToModel(selectedDate, format)"
            min-date="minDate" max-date="maxDate"
            is-open="status.opened" datepicker-options="dateOptions"
-           ng-required="schema.mandatoryCondition" close-text="Close" ng-init="initAttribute(schema, index)"/>
+           ng-required="{{schema.mandatoryCondition}}" close-text="Close" ng-init="initAttribute(schema, index)"/>
   <span class="input-group-btn">
     <button type="button" class="btn btn-default" ng-click="open($event)"><i class="glyphicon glyphicon-calendar"></i></button>
   </span>
@@ -51,14 +52,14 @@ under the License.
 <div ng-switch-when="Enum" ng-init="initAttribute(schema, index)">
   <select class="form-control"
           ng-model="user.plainAttrs[schema.key].values[index]"
-          ng-required="schema.mandatoryCondition">
+          ng-required="{{schema.mandatoryCondition}}">
     <option ng-repeat="value in enumerationValues" value="{{value}}">{{schema.enumerationKeys[$index] || value}}</option>
   </select>
 </div>
 
 <div ng-switch-when="Binary" ng-init="initAttribute(schema, index)">
   <div enctype="multipart/form-data" accept-charset="UTF-8">
-    <input id="fileInput" type="file" ng-required="schema.mandatoryCondition"/>
+    <input id="fileInput" type="file" ng-required="{{schema.mandatoryCondition}}"/>
     <button type="button" title="Download file" class="fileButton btn btn-default btn-sm" ng-click="download()">
       <i class="glyphicon glyphicon-download" ></i>
     </button>
@@ -69,10 +70,9 @@ under the License.
   </div>
 
 </div>
-
 <input ng-switch-default class="form-control" type="text"
        ng-model="user.plainAttrs[schema.key].values[index]"
-       ng-required="schema.mandatoryCondition" 
+       ng-required="{{schema.mandatoryCondition}}" 
        ng-disabled="schema.readonly" ng-init="initAttribute(schema, index)"/>
 
 </div>

http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttributes.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttributes.html b/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttributes.html
index c356e82..6ca8c0e 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttributes.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttributes.html
@@ -20,6 +20,7 @@ under the License.
   <label for="plainSchema.key">{{plainSchema.key}} <span ng-if="plainSchema.mandatoryCondition === 'true'">*</span></label>
   <div ng-if="!plainSchema.multivalue">
     <dynamic-plain-attribute schema="plainSchema" user="user" index="0"></dynamic-plain-attribute>
+    <validation-message name="{{plainSchema.key}}"/>
   </div>
 
   <div ng-if="plainSchema.multivalue">

http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/client/enduser/src/main/resources/META-INF/resources/app/views/editUser.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/editUser.html b/client/enduser/src/main/resources/META-INF/resources/app/views/editUser.html
index c5cdf67..c250264 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/editUser.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/editUser.html
@@ -36,20 +36,20 @@ under the License.
             <div id="status-buttons" class="btn-group btn-breadcrumb">
               <a href="#/self" class="btn btn-default"><i class="glyphicon glyphicon-home"></i></a>
               <!--add class breadcrumb-disabled-link to buttons to prevent click-->
-              <a ui-sref-active="active" ui-sref=".credentials" class="btn btn-default">Credentials</a>
-              <a ui-sref-active="active" ui-sref=".groups" class="btn btn-default">Groups</a>
-              <a ui-sref-active="active" ui-sref=".plainSchemas" class="btn btn-default">Plain Schemas</a>
-              <a ui-sref-active="active" ui-sref=".derivedSchemas" class="btn btn-default">Derived Schemas</a>
-              <a ui-sref-active="active" ui-sref=".virtualSchemas" class="btn btn-default">Virtual Schemas</a>              
-              <a ui-sref-active="active" ui-sref=".resources" class="btn btn-default">Resources</a>
-              <a ui-sref-active="active" ui-sref=".finish" class="btn btn-default">Finish</a>
+              <a ui-sref-active="active" ui-sref=".credentials" class="btn btn-default" ng-class="createMode ? 'disable-link' : ''">Credentials</a>
+              <a ui-sref-active="active" ui-sref=".groups" class="btn btn-default" ng-class="createMode ? 'disable-link' : ''">Groups</a>
+              <a ui-sref-active="active" ui-sref=".plainSchemas" class="btn btn-default" ng-class="createMode ? 'disable-link' : ''">Plain Schemas</a>
+              <a ui-sref-active="active" ui-sref=".derivedSchemas" class="btn btn-default" ng-class="createMode ? 'disable-link' : ''">Derived Schemas</a>
+              <a ui-sref-active="active" ui-sref=".virtualSchemas" class="btn btn-default" ng-class="createMode ? 'disable-link' : ''">Virtual Schemas</a>              
+              <a ui-sref-active="active" ui-sref=".resources" class="btn btn-default" ng-class="createMode ? 'disable-link' : ''">Resources</a>
+              <a ui-sref-active="active" ui-sref=".finish" class="btn btn-default" ng-class="createMode ? 'disable-link' : ''">Finish</a>
             </div>
           </div>
         </div>
         <form class="signup-form" name="userForm" ng-submit="saveUser(user)" novalidate>
 
           <div id="form-views" ui-view>
-
+            
             <!--            <div class="form-group row">
                           <div class="col-xs-6 col-xs-offset-3">
                             <a id="next" ui-sref="create.credentials" class="btn btn-block btn-signin">

http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/client/enduser/src/main/resources/META-INF/resources/app/views/navigationButtons.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/navigationButtons.html b/client/enduser/src/main/resources/META-INF/resources/app/views/navigationButtons.html
index 65af91a..1f1d7a4 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/navigationButtons.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/navigationButtons.html
@@ -17,7 +17,7 @@ specific language governing permissions and limitations
 under the License.
 -->
 <div class="pull-right">
-  <a ng-show="next !== 'none'" id="next" ui-sref="{{next}}" class="btn btn-default">
+  <a ng-show="next !== 'none'" id="next" class="btn btn-default" ng-click="validateAndNext($event, next)">
     Next <span class="glyphicon glyphicon-circle-arrow-right"></span>
   </a>
   <a ng-show="previous !== 'none'" id="previous" ui-sref="{{previous}}" class="btn btn-default">

http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/client/enduser/src/main/resources/META-INF/resources/app/views/user-credentials.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/user-credentials.html b/client/enduser/src/main/resources/META-INF/resources/app/views/user-credentials.html
index 1121d4b..cbaff42 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/user-credentials.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/user-credentials.html
@@ -17,22 +17,21 @@ specific language governing permissions and limitations
 under the License.
 -->
 <div id="attribute" class="form-group">
-  <label for="user.username">Username</label>
-  <input name="username" type="text" class="form-control" ng-model="user.username" required placeholder="username" >
-  <p ng-show="(userForm.username.$error.required && !userForm.username.$pristine)" class="text-validation-error">Username is required</p>
+  <label for="user.username">Username <span>*</span></label>
+  <input name="username" type="text" class="form-control" ng-model="user.username" required placeholder="username" validate="true"/>
+  <validation-message name="username"/>
 </div>
 
 <div id="attribute" class="form-group">
   <label for="user.password">Password</label>
-  <input type="password" class="form-control" name="password" ng-model="user.password" equals="{{confirmPassword.value}}"
-         placeholder="password">
+  <input type="password" class="form-control" name="password" ng-model="user.password" placeholder="password">
 </div>
 
 <div id="attribute" class="form-group">
   <label for="confirmPassword">Confirm Password</label>
-  <input name="confirmPassword" type="password" class="form-control" equals="{{user.password}}" ng-model="confirmPassword.value"
-         placeholder="confirm password">
-  <p ng-show="userForm.confirmPassword.$error.equals" class="text-validation-error">Password and confirm password must be equal</p>
+  <input name="password" type="password" class="form-control" equals="user.password" ng-model="confirmPassword.value"
+         placeholder="confirm password" validate="true">
+  <validation-message name="password"/>
 </div>
 
 <div id="attribute" class="form-group">

http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/client/enduser/src/main/resources/META-INF/resources/app/views/validationMessage.html
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/validationMessage.html b/client/enduser/src/main/resources/META-INF/resources/app/views/validationMessage.html
new file mode 100644
index 0000000..1e05605
--- /dev/null
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/validationMessage.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<p class="text-validation-error" ng-repeat="(key, value) in errors.errors">
+{{key}} policy is not satisfied
+</p>

http://git-wip-us.apache.org/repos/asf/syncope/blob/78483975/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index e3f4a5e..a9af048 100644
--- a/pom.xml
+++ b/pom.xml
@@ -400,8 +400,8 @@ under the License.
     <angular-resource.version>1.4.8</angular-resource.version>
     <angular-cookies.version>1.4.8</angular-cookies.version>
     <angular-animate.version>1.4.8</angular-animate.version>
-    <angular-ui-router.version>0.2.15</angular-ui-router.version>
-    <angular-ui-bootstrap.version>1.0.3</angular-ui-bootstrap.version>
+    <angular-ui-router.version>0.2.17</angular-ui-router.version>
+    <angular-ui-bootstrap.version>1.1.1-1</angular-ui-bootstrap.version>
     <angular-ui-select.version>0.13.1</angular-ui-select.version>
     <angular-sanitize.version>1.4.8</angular-sanitize.version>
     <angular-treasure-overlay-spinner.version>1.1.0</angular-treasure-overlay-spinner.version>