You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by at...@apache.org on 2017/11/23 12:55:57 UTC

[1/4] ambari git commit: AMBARI-22508 Ambari 3.0: Implement new design for Admin View: User Management. (atkach)

Repository: ambari
Updated Branches:
  refs/heads/trunk 8e36662ae -> 99b19e580


http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/userManagement/UsersListCtrl_test.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/userManagement/UsersListCtrl_test.js b/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/userManagement/UsersListCtrl_test.js
new file mode 100644
index 0000000..fcafa59
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/userManagement/UsersListCtrl_test.js
@@ -0,0 +1,344 @@
+/**
+ * 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.
+ */
+
+describe('#Cluster', function () {
+
+  describe('UsersListCtrl', function() {
+
+    var scope, ctrl, $t, $httpBackend;
+
+    beforeEach(module('ambariAdminConsole', function () {}));
+
+    beforeEach(inject(function($rootScope, $controller, _$translate_, _$httpBackend_) {
+      scope = $rootScope.$new();
+      $t = _$translate_.instant;
+      $httpBackend = _$httpBackend_;
+      ctrl = $controller('UsersListCtrl', {
+        $scope: scope
+      });
+    }));
+
+    describe('#clearFilters()', function () {
+
+      it('should clear filters and reset pagination', function () {
+        scope.currentPage = 2;
+        scope.filters.name = 'a';
+        scope.filters.status = {
+          label: $t('common.local'),
+          value: false
+        };
+        scope.filters.type = {
+          label: $t('common.local'),
+          value: 'LOCAL'
+        };
+        scope.clearFilters();
+        expect(scope.filters.name).toEqual('');
+        expect(scope.filters.status).toEqual({
+          label: $t('common.all'),
+          value: '*'
+        });
+        expect(scope.filters.type).toEqual({
+          label: $t('common.all'),
+          value: '*'
+        });
+        expect(scope.currentPage).toEqual(1);
+      });
+
+    });
+
+    describe('#isNotEmptyFilter', function () {
+
+      var cases = [
+        {
+          currentNameFilter: '',
+          currentTypeFilter: null,
+          currentActiveFilter: null,
+          isNotEmptyFilter: false,
+          title: 'no filters'
+        },
+        {
+          currentNameFilter: '',
+          currentTypeFilter: {
+            value: '*'
+          },
+          currentActiveFilter: {
+            value: '*'
+          },
+          isNotEmptyFilter: false,
+          title: 'empty filters'
+        },
+        {
+          currentNameFilter: 'a',
+          currentTypeFilter: {
+            value: '*'
+          },
+          currentActiveFilter: {
+            value: '*'
+          },
+          isNotEmptyFilter: true,
+          title: 'name filter'
+        },
+        {
+          currentNameFilter: '0',
+          currentTypeFilter: {
+            value: '*'
+          },
+          currentActiveFilter: {
+            value: '*'
+          },
+          isNotEmptyFilter: true,
+          title: 'name filter with "0" as string'
+        },
+        {
+          currentNameFilter: '',
+          currentTypeFilter: {
+            value: 'LOCAL'
+          },
+          currentActiveFilter: {
+            value: '*'
+          },
+          isNotEmptyFilter: true,
+          title: 'type filter'
+        },
+        {
+          currentNameFilter: '',
+          currentTypeFilter: {
+            value: '*'
+          },
+          currentActiveFilter: {
+            value: false
+          },
+          isNotEmptyFilter: true,
+          title: 'activity filter'
+        },
+        {
+          currentNameFilter: 'a',
+          currentTypeFilter: {
+            value: 'LOCAL'
+          },
+          currentActiveFilter: {
+            value: '*'
+          },
+          isNotEmptyFilter: true,
+          title: 'name and type filters'
+        },
+        {
+          currentNameFilter: 'a',
+          currentTypeFilter: {
+            value: '*'
+          },
+          currentActiveFilter: {
+            value: false
+          },
+          isNotEmptyFilter: true,
+          title: 'name and activity filters'
+        },
+        {
+          currentNameFilter: 'a',
+          currentTypeFilter: {
+            value: '*'
+          },
+          currentActiveFilter: {
+            value: '*'
+          },
+          isNotEmptyFilter: true,
+          title: 'name and admin filters'
+        },
+        {
+          currentNameFilter: '0',
+          currentTypeFilter: {
+            value: 'LOCAL'
+          },
+          currentActiveFilter: {
+            value: '*'
+          },
+          isNotEmptyFilter: true,
+          title: 'name and type filters with "0" as string'
+        },
+        {
+          currentNameFilter: '0',
+          currentTypeFilter: {
+            value: '*'
+          },
+          currentActiveFilter: {
+            value: false
+          },
+          isNotEmptyFilter: true,
+          title: 'name and activity filters with "0" as string'
+        },
+        {
+          currentNameFilter: '0',
+          currentTypeFilter: {
+            value: '*'
+          },
+          currentActiveFilter: {
+            value: '*'
+          },
+          isNotEmptyFilter: true,
+          title: 'name and admin filters with "0" as string'
+        },
+        {
+          currentNameFilter: '',
+          currentTypeFilter: {
+            value: 'LOCAL'
+          },
+          currentActiveFilter: {
+            value: false
+          },
+          isNotEmptyFilter: true,
+          title: 'type and activity filters'
+        },
+        {
+          currentNameFilter: '',
+          currentTypeFilter: {
+            value: 'LOCAL'
+          },
+          currentActiveFilter: {
+            value: '*'
+          },
+          isNotEmptyFilter: true,
+          title: 'type and admin filters'
+        },
+        {
+          currentNameFilter: '',
+          currentTypeFilter: {
+            value: '*'
+          },
+          currentActiveFilter: {
+            value: false
+          },
+          isNotEmptyFilter: true,
+          title: 'activity and admin filters'
+        },
+        {
+          currentNameFilter: '',
+          currentTypeFilter: {
+            value: 'LOCAL'
+          },
+          currentActiveFilter: {
+            value: false
+          },
+          isNotEmptyFilter: true,
+          title: 'all filters except name one'
+        },
+        {
+          currentNameFilter: 'a',
+          currentTypeFilter: {
+            value: '*'
+          },
+          currentActiveFilter: {
+            value: false
+          },
+          isNotEmptyFilter: true,
+          title: 'all filters except type one'
+        },
+        {
+          currentNameFilter: 'a',
+          currentTypeFilter: {
+            value: 'LOCAL'
+          },
+          currentActiveFilter: {
+            value: '*'
+          },
+          isNotEmptyFilter: true,
+          title: 'all filters except activity one'
+        },
+        {
+          currentNameFilter: 'a',
+          currentTypeFilter: {
+            value: 'LOCAL'
+          },
+          currentActiveFilter: {
+            value: false
+          },
+          isNotEmptyFilter: true,
+          title: 'all filters except admin one'
+        },
+        {
+          currentNameFilter: '0',
+          currentTypeFilter: {
+            value: '*'
+          },
+          currentActiveFilter: {
+            value: false
+          },
+          isNotEmptyFilter: true,
+          title: 'all filters with "0" as string except type one'
+        },
+        {
+          currentNameFilter: '0',
+          currentTypeFilter: {
+            value: 'LOCAL'
+          },
+          currentActiveFilter: {
+            value: '*'
+          },
+          isNotEmptyFilter: true,
+          title: 'all filters with "0" as string except activity one'
+        },
+        {
+          currentNameFilter: '0',
+          currentTypeFilter: {
+            value: 'LOCAL'
+          },
+          currentActiveFilter: {
+            value: false
+          },
+          isNotEmptyFilter: true,
+          title: 'all filters with "0" as string except admin one'
+        },
+        {
+          currentNameFilter: 'a',
+          currentTypeFilter: {
+            value: false
+          },
+          currentActiveFilter: {
+            value: 'LOCAL'
+          },
+          isNotEmptyFilter: true,
+          title: 'all filters'
+        },
+        {
+          currentNameFilter: '0',
+          currentTypeFilter: {
+            value: false
+          },
+          currentActiveFilter: {
+            value: 'LOCAL'
+          },
+          isNotEmptyFilter: true,
+          title: 'all filters with "0" as string'
+        }
+      ];
+
+      cases.forEach(function (item) {
+        it(item.title, function () {
+          $httpBackend.expectGET(/\/api\/v1\/users/).respond(200);
+          scope.filters.name = item.currentNameFilter;
+          scope.filters.status = item.currentActiveFilter;
+          scope.filters.type = item.currentTypeFilter;
+          scope.$digest();
+          expect(scope.isNotEmptyFilter).toEqual(item.isNotEmptyFilter);
+        });
+      });
+
+    });
+
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/users/UsersListCtrl_test.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/users/UsersListCtrl_test.js b/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/users/UsersListCtrl_test.js
deleted file mode 100644
index 9d6cd54..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/users/UsersListCtrl_test.js
+++ /dev/null
@@ -1,383 +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.
- */
-
-describe('#Cluster', function () {
-
-  describe('UsersListCtrl', function() {
-
-    var scope, ctrl, $t, $httpBackend;
-
-    beforeEach(module('ambariAdminConsole', function () {}));
-
-    beforeEach(inject(function($rootScope, $controller, _$translate_, _$httpBackend_) {
-      scope = $rootScope.$new();
-      $t = _$translate_.instant;
-      $httpBackend = _$httpBackend_;
-      ctrl = $controller('UsersListCtrl', {
-        $scope: scope
-      });
-    }));
-
-    describe('#clearFilters()', function () {
-
-      it('should clear filters and reset pagination', function () {
-        scope.currentPage = 2;
-        scope.currentNameFilter = 'a';
-        scope.currentActiveFilter = {
-          label: $t('common.local'),
-          value: false
-        };
-        scope.currentTypeFilter = {
-          label: $t('common.local'),
-          value: 'LOCAL'
-        };
-        scope.adminFilter = true;
-        scope.clearFilters();
-        expect(scope.currentNameFilter).toEqual('');
-        expect(scope.currentActiveFilter).toEqual({
-          label: $t('common.all'),
-          value: '*'
-        });
-        expect(scope.currentTypeFilter).toEqual({
-          label: $t('common.all'),
-          value: '*'
-        });
-        expect(scope.currentPage).toEqual(1);
-        expect(scope.adminFilter).toBe(false);
-      });
-
-    });
-
-    describe('#isNotEmptyFilter', function () {
-
-      var cases = [
-        {
-          currentNameFilter: '',
-          currentTypeFilter: null,
-          currentActiveFilter: null,
-          isNotEmptyFilter: false,
-          adminFilter: false,
-          title: 'no filters'
-        },
-        {
-          currentNameFilter: '',
-          currentTypeFilter: {
-            value: '*'
-          },
-          currentActiveFilter: {
-            value: '*'
-          },
-          adminFilter: false,
-          isNotEmptyFilter: false,
-          title: 'empty filters'
-        },
-        {
-          currentNameFilter: 'a',
-          currentTypeFilter: {
-            value: '*'
-          },
-          currentActiveFilter: {
-            value: '*'
-          },
-          adminFilter: false,
-          isNotEmptyFilter: true,
-          title: 'name filter'
-        },
-        {
-          currentNameFilter: '0',
-          currentTypeFilter: {
-            value: '*'
-          },
-          currentActiveFilter: {
-            value: '*'
-          },
-          adminFilter: false,
-          isNotEmptyFilter: true,
-          title: 'name filter with "0" as string'
-        },
-        {
-          currentNameFilter: '',
-          currentTypeFilter: {
-            value: 'LOCAL'
-          },
-          currentActiveFilter: {
-            value: '*'
-          },
-          adminFilter: false,
-          isNotEmptyFilter: true,
-          title: 'type filter'
-        },
-        {
-          currentNameFilter: '',
-          currentTypeFilter: {
-            value: '*'
-          },
-          currentActiveFilter: {
-            value: false
-          },
-          adminFilter: false,
-          isNotEmptyFilter: true,
-          title: 'activity filter'
-        },
-        {
-          currentNameFilter: '',
-          currentTypeFilter: {
-            value: '*'
-          },
-          currentActiveFilter: {
-            value: '*'
-          },
-          adminFilter: true,
-          isNotEmptyFilter: true,
-          title: 'admin filter'
-        },
-        {
-          currentNameFilter: 'a',
-          currentTypeFilter: {
-            value: 'LOCAL'
-          },
-          currentActiveFilter: {
-            value: '*'
-          },
-          adminFilter: false,
-          isNotEmptyFilter: true,
-          title: 'name and type filters'
-        },
-        {
-          currentNameFilter: 'a',
-          currentTypeFilter: {
-            value: '*'
-          },
-          currentActiveFilter: {
-            value: false
-          },
-          adminFilter: false,
-          isNotEmptyFilter: true,
-          title: 'name and activity filters'
-        },
-        {
-          currentNameFilter: 'a',
-          currentTypeFilter: {
-            value: '*'
-          },
-          currentActiveFilter: {
-            value: '*'
-          },
-          adminFilter: true,
-          isNotEmptyFilter: true,
-          title: 'name and admin filters'
-        },
-        {
-          currentNameFilter: '0',
-          currentTypeFilter: {
-            value: 'LOCAL'
-          },
-          currentActiveFilter: {
-            value: '*'
-          },
-          adminFilter: false,
-          isNotEmptyFilter: true,
-          title: 'name and type filters with "0" as string'
-        },
-        {
-          currentNameFilter: '0',
-          currentTypeFilter: {
-            value: '*'
-          },
-          currentActiveFilter: {
-            value: false
-          },
-          adminFilter: false,
-          isNotEmptyFilter: true,
-          title: 'name and activity filters with "0" as string'
-        },
-        {
-          currentNameFilter: '0',
-          currentTypeFilter: {
-            value: '*'
-          },
-          currentActiveFilter: {
-            value: '*'
-          },
-          adminFilter: true,
-          isNotEmptyFilter: true,
-          title: 'name and admin filters with "0" as string'
-        },
-        {
-          currentNameFilter: '',
-          currentTypeFilter: {
-            value: 'LOCAL'
-          },
-          currentActiveFilter: {
-            value: false
-          },
-          adminFilter: false,
-          isNotEmptyFilter: true,
-          title: 'type and activity filters'
-        },
-        {
-          currentNameFilter: '',
-          currentTypeFilter: {
-            value: 'LOCAL'
-          },
-          currentActiveFilter: {
-            value: '*'
-          },
-          adminFilter: true,
-          isNotEmptyFilter: true,
-          title: 'type and admin filters'
-        },
-        {
-          currentNameFilter: '',
-          currentTypeFilter: {
-            value: '*'
-          },
-          currentActiveFilter: {
-            value: false
-          },
-          adminFilter: true,
-          isNotEmptyFilter: true,
-          title: 'activity and admin filters'
-        },
-        {
-          currentNameFilter: '',
-          currentTypeFilter: {
-            value: 'LOCAL'
-          },
-          currentActiveFilter: {
-            value: false
-          },
-          adminFilter: true,
-          isNotEmptyFilter: true,
-          title: 'all filters except name one'
-        },
-        {
-          currentNameFilter: 'a',
-          currentTypeFilter: {
-            value: '*'
-          },
-          currentActiveFilter: {
-            value: false
-          },
-          adminFilter: true,
-          isNotEmptyFilter: true,
-          title: 'all filters except type one'
-        },
-        {
-          currentNameFilter: 'a',
-          currentTypeFilter: {
-            value: 'LOCAL'
-          },
-          currentActiveFilter: {
-            value: '*'
-          },
-          adminFilter: true,
-          isNotEmptyFilter: true,
-          title: 'all filters except activity one'
-        },
-        {
-          currentNameFilter: 'a',
-          currentTypeFilter: {
-            value: 'LOCAL'
-          },
-          currentActiveFilter: {
-            value: false
-          },
-          adminFilter: false,
-          isNotEmptyFilter: true,
-          title: 'all filters except admin one'
-        },
-        {
-          currentNameFilter: '0',
-          currentTypeFilter: {
-            value: '*'
-          },
-          currentActiveFilter: {
-            value: false
-          },
-          adminFilter: true,
-          isNotEmptyFilter: true,
-          title: 'all filters with "0" as string except type one'
-        },
-        {
-          currentNameFilter: '0',
-          currentTypeFilter: {
-            value: 'LOCAL'
-          },
-          currentActiveFilter: {
-            value: '*'
-          },
-          adminFilter: true,
-          isNotEmptyFilter: true,
-          title: 'all filters with "0" as string except activity one'
-        },
-        {
-          currentNameFilter: '0',
-          currentTypeFilter: {
-            value: 'LOCAL'
-          },
-          currentActiveFilter: {
-            value: false
-          },
-          adminFilter: false,
-          isNotEmptyFilter: true,
-          title: 'all filters with "0" as string except admin one'
-        },
-        {
-          currentNameFilter: 'a',
-          currentTypeFilter: {
-            value: false
-          },
-          currentActiveFilter: {
-            value: 'LOCAL'
-          },
-          adminFilter: true,
-          isNotEmptyFilter: true,
-          title: 'all filters'
-        },
-        {
-          currentNameFilter: '0',
-          currentTypeFilter: {
-            value: false
-          },
-          currentActiveFilter: {
-            value: 'LOCAL'
-          },
-          adminFilter: true,
-          isNotEmptyFilter: true,
-          title: 'all filters with "0" as string'
-        }
-      ];
-
-      cases.forEach(function (item) {
-        it(item.title, function () {
-          $httpBackend.expectGET(/\/api\/v1\/users/).respond(200);
-          scope.currentNameFilter = item.currentNameFilter;
-          scope.currentActiveFilter = item.currentActiveFilter;
-          scope.currentTypeFilter = item.currentTypeFilter;
-          scope.adminFilter = item.adminFilter;
-          scope.$digest();
-          expect(scope.isNotEmptyFilter).toEqual(item.isNotEmptyFilter);
-        });
-      });
-
-    });
-
-  });
-
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/test/unit/services/Utility_test.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/test/unit/services/Utility_test.js b/ambari-admin/src/main/resources/ui/admin-web/test/unit/services/Utility_test.js
index 1b76dcf..7f67de4 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/test/unit/services/Utility_test.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/test/unit/services/Utility_test.js
@@ -25,7 +25,13 @@ describe('Utility Service', function () {
 
   beforeEach(function () {
     module('ambariAdminConsole', function ($provide) {
-      $provide.value('$window', {});
+      $provide.value('$window', {
+        localStorage: {
+          getItem: function() {return '{}';},
+          setItem: function() {}
+        },
+        location: {}
+      });
     });
     inject(function (_Utility_, _$httpBackend_, $rootScope, $controller, _Cluster_, _$q_) {
       Utility = _Utility_;
@@ -54,7 +60,7 @@ describe('Utility Service', function () {
       httpBackend.whenGET(/\/api\/v1\/views.+/).respond(200, {
         items: []
       });
-      httpBackend.whenGET("views/clusterInformation.html").respond(200, {});
+      httpBackend.whenGET("views/clusters/clusterInformation.html").respond(200, {});
     });
   });
 


[4/4] ambari git commit: AMBARI-22508 Ambari 3.0: Implement new design for Admin View: User Management. (atkach)

Posted by at...@apache.org.
AMBARI-22508 Ambari 3.0: Implement new design for Admin View: User Management. (atkach)


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

Branch: refs/heads/trunk
Commit: 99b19e5805cf3ac291eca9c8e6a1290637b08ab6
Parents: 8e36662
Author: Andrii Tkach <at...@apache.org>
Authored: Thu Nov 23 14:02:00 2017 +0200
Committer: Andrii Tkach <at...@apache.org>
Committed: Thu Nov 23 14:02:00 2017 +0200

----------------------------------------------------------------------
 .../main/resources/ui/admin-web/app/index.html  |  17 +-
 .../resources/ui/admin-web/app/scripts/app.js   |   3 +-
 .../controllers/ClusterInformationCtrl.js       | 106 ---
 .../controllers/ambariViews/ViewsListCtrl.js    |   4 +-
 .../clusters/ClusterInformationCtrl.js          | 106 +++
 .../clusters/ClustersManageAccessCtrl.js        |  97 ---
 .../controllers/clusters/UserAccessListCtrl.js  | 351 --------
 .../controllers/groups/GroupsCreateCtrl.js      |  65 --
 .../controllers/groups/GroupsEditCtrl.js        | 180 ----
 .../controllers/groups/GroupsListCtrl.js        | 106 ---
 .../userManagement/GroupCreateCtrl.js           | 112 +++
 .../controllers/userManagement/GroupEditCtrl.js | 182 ++++
 .../userManagement/GroupsListCtrl.js            | 170 ++++
 .../userManagement/UserCreateCtrl.js            | 108 +++
 .../controllers/userManagement/UserEditCtrl.js  | 290 +++++++
 .../userManagement/UserManagementCtrl.js        |  23 +
 .../controllers/userManagement/UsersListCtrl.js | 178 ++++
 .../controllers/users/UsersCreateCtrl.js        |  75 --
 .../scripts/controllers/users/UsersListCtrl.js  | 122 ---
 .../scripts/controllers/users/UsersShowCtrl.js  | 290 -------
 .../ui/admin-web/app/scripts/i18n.config.js     |   9 +-
 .../ui/admin-web/app/scripts/routes.js          |  61 +-
 .../ui/admin-web/app/scripts/services/User.js   |   5 +-
 .../resources/ui/admin-web/app/styles/main.css  |  46 +-
 .../ui/admin-web/app/styles/user-management.css |  30 +
 .../resources/ui/admin-web/app/styles/views.css |  18 -
 .../app/views/ambariViews/viewsList.html        |  26 +-
 .../admin-web/app/views/clusterInformation.html |  87 --
 .../app/views/clusters/clusterInformation.html  |  87 ++
 .../app/views/clusters/manageAccess.html        |  63 --
 .../app/views/clusters/userAccessList.html      | 102 ---
 .../ui/admin-web/app/views/groups/create.html   |  43 -
 .../ui/admin-web/app/views/groups/edit.html     |  98 ---
 .../ui/admin-web/app/views/groups/list.html     |  80 --
 .../ui/admin-web/app/views/sideNav.html         |  22 +-
 .../app/views/userManagement/groupEdit.html     |  99 +++
 .../app/views/userManagement/groupsList.html    |  94 +++
 .../app/views/userManagement/main.html          |  36 +
 .../userManagement/modals/changePassword.html   |  46 ++
 .../userManagement/modals/groupCreate.html      |  86 ++
 .../views/userManagement/modals/userCreate.html | 147 ++++
 .../app/views/userManagement/userEdit.html      | 122 +++
 .../app/views/userManagement/usersList.html     | 119 +++
 .../ui/admin-web/app/views/users/create.html    |  82 --
 .../ui/admin-web/app/views/users/list.html      |  97 ---
 .../app/views/users/modals/changePassword.html  |  46 --
 .../ui/admin-web/app/views/users/show.html      | 122 ---
 .../clusters/UserAccessListCtrl_test.js         | 820 -------------------
 .../controllers/groups/GroupsListCtrl_test.js   | 129 ---
 .../userManagement/GroupsListCtrl_test.js       | 129 +++
 .../userManagement/UsersListCtrl_test.js        | 344 ++++++++
 .../controllers/users/UsersListCtrl_test.js     | 383 ---------
 .../test/unit/services/Utility_test.js          |  10 +-
 53 files changed, 2591 insertions(+), 3682 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/index.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/index.html b/ambari-admin/src/main/resources/ui/admin-web/app/index.html
index 4a77e62..e3b817e 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/index.html
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/index.html
@@ -121,26 +121,25 @@
 <script src="scripts/app.js"></script>
 <script src="scripts/routes.js"></script>
 <script src="scripts/i18n.config.js"></script>
-<script src="scripts/controllers/ClusterInformationCtrl.js"></script>
+<script src="scripts/controllers/clusters/ClusterInformationCtrl.js"></script>
 <script src="scripts/controllers/AppCtrl.js"></script>
 <script src="scripts/controllers/SideNavCtrl.js"></script>
 <script src="scripts/controllers/authentication/AuthenticationMainCtrl.js"></script>
 <script src="scripts/controllers/loginActivities/LoginActivitiesMainCtrl.js"></script>
 <script src="scripts/controllers/loginActivities/LoginMessageMainCtrl.js"></script>
 <script src="scripts/controllers/loginActivities/HomeDirectoryCtrl.js"></script>
-<script src="scripts/controllers/users/UsersCreateCtrl.js"></script>
-<script src="scripts/controllers/users/UsersListCtrl.js"></script>
-<script src="scripts/controllers/users/UsersShowCtrl.js"></script>
-<script src="scripts/controllers/groups/GroupsListCtrl.js"></script>
-<script src="scripts/controllers/groups/GroupsCreateCtrl.js"></script>
-<script src="scripts/controllers/groups/GroupsEditCtrl.js"></script>
+<script src="scripts/controllers/userManagement/UserManagementCtrl.js"></script>
+<script src="scripts/controllers/userManagement/UserCreateCtrl.js"></script>
+<script src="scripts/controllers/userManagement/UsersListCtrl.js"></script>
+<script src="scripts/controllers/userManagement/UserEditCtrl.js"></script>
+<script src="scripts/controllers/userManagement/GroupsListCtrl.js"></script>
+<script src="scripts/controllers/userManagement/GroupCreateCtrl.js"></script>
+<script src="scripts/controllers/userManagement/GroupEditCtrl.js"></script>
 <script src="scripts/controllers/ambariViews/ViewsListCtrl.js"></script>
 <script src="scripts/controllers/ambariViews/ViewsEditCtrl.js"></script>
 <script src="scripts/controllers/ambariViews/ViewUrlCtrl.js"></script>
 <script src="scripts/controllers/ambariViews/ViewUrlEditCtrl.js"></script>
 <script src="scripts/controllers/ambariViews/CreateViewInstanceCtrl.js"></script>
-<script src="scripts/controllers/clusters/ClustersManageAccessCtrl.js"></script>
-<script src="scripts/controllers/clusters/UserAccessListCtrl.js"></script>
 <script src="scripts/controllers/stackVersions/StackVersionsCreateCtrl.js"></script>
 <script src="scripts/controllers/stackVersions/StackVersionsListCtrl.js"></script>
 <script src="scripts/controllers/stackVersions/StackVersionsEditCtrl.js"></script>

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/app.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/app.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/app.js
index 80e2813..225eb12 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/app.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/app.js
@@ -33,7 +33,8 @@ angular.module('ambariAdminConsole', [
   isLDAPConfigurationSupported: false,
   isLoginActivitiesSupported: false,
   maxStackTraceLength: 1000,
-  errorStorageSize: 500000
+  errorStorageSize: 500000,
+  minRowsToShowPagination: 10
 })
 .config(['RestangularProvider', '$httpProvider', '$provide', 'Settings', function(RestangularProvider, $httpProvider, $provide, Settings) {
   // Config Ajax-module

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ClusterInformationCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ClusterInformationCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ClusterInformationCtrl.js
deleted file mode 100644
index 059f399..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ClusterInformationCtrl.js
+++ /dev/null
@@ -1,106 +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.
- */
-'use strict';
-
-angular.module('ambariAdminConsole')
-.controller('ClusterInformationCtrl',
-['$scope', '$http', '$location', 'Cluster', '$routeParams', '$translate', '$rootScope', 'ConfirmationModal', 'Alert',
-function($scope, $http, $location, Cluster, $routeParams, $translate, $rootScope, ConfirmationModal, Alert) {
-  var $t = $translate.instant;
-  $scope.isDataLoaded = false;
-  $scope.edit = {
-    clusterName: null
-  };
-  $scope.isClusterNameEdited = false;
-
-  $scope.$watch(function() {
-    return $rootScope.cluster;
-  }, function() {
-    $scope.cluster = $rootScope.cluster;
-    if ($scope.cluster) {
-      $scope.edit.clusterName = $scope.cluster.Clusters.cluster_name;
-      $scope.getBlueprint();
-    }
-  }, true);
-
-  $scope.getBlueprint = function () {
-    Cluster.getBlueprint({
-      clusterName: $scope.cluster.Clusters.cluster_name
-    }).then(function (data) {
-      console.debug($t('exportBlueprint.dataLoaded'), data);
-      $scope.isDataLoaded = true;
-      var response = JSON.stringify(data, null, 4),
-        lt = /&lt;/g,
-        gt = /&gt;/g,
-        ap = /&#39;/g,
-        ic = /&#34;/g;
-      $scope.blueprint = response ? response.toString().replace(lt, "<").replace(gt, ">").replace(ap, "'").replace(ic, '"') : "";
-    });
-  };
-
-  $scope.downloadBlueprint = function () {
-    if (window.navigator.msSaveOrOpenBlob) {
-      var blob = new Blob([decodeURIComponent(encodeURI($scope.blueprint))], {
-        type: "text/csv;charset=utf-8;"
-      });
-      navigator.msSaveBlob(blob, 'blueprint.json');
-    } else {
-      var a = document.createElement('a');
-      a.href = 'data:attachment/csv;charset=utf-8,' + encodeURI($scope.blueprint);
-      a.target = '_blank';
-      a.download = 'blueprint.json';
-      document.body.appendChild(a);
-      a.click();
-    }
-  };
-
-  $scope.toggleSaveButton = function() {
-    var value = $scope.edit.clusterName;
-    $scope.isClusterNameEdited = (value !== null && $scope.cluster.Clusters.cluster_name !== value);
-  };
-
-  $scope.confirmClusterNameChange = function() {
-    ConfirmationModal.show(
-      $t('common.clusterNameChangeConfirmation.title'),
-      $t('common.clusterNameChangeConfirmation.message', {
-        clusterName: $scope.edit.clusterName
-      })
-    )
-      .then(function () {
-        $scope.saveClusterName();
-      }).catch(function () {
-      // user clicked cancel
-      $scope.edit.clusterName = $scope.cluster.Clusters.cluster_name;
-      $scope.toggleSaveButton();
-    });
-  };
-
-  $scope.saveClusterName = function() {
-    var oldClusterName = $scope.cluster.Clusters.cluster_name,
-        newClusterName = $scope.edit.clusterName;
-
-    Cluster.editName(oldClusterName, newClusterName).then(function(data) {
-      $scope.cluster.Clusters.cluster_name = newClusterName;
-      $scope.edit.clusterName = newClusterName;
-      $scope.toggleSaveButton();
-      Alert.success($t('common.alerts.clusterRenamed', {clusterName: newClusterName}));
-    }).catch(function(data) {
-      Alert.error($t('common.alerts.cannotRenameCluster', {clusterName: newClusterName}), data.data.message);
-    });
-  };
-}]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsListCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsListCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsListCtrl.js
index aa77b63..8b37dca 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsListCtrl.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsListCtrl.js
@@ -18,11 +18,11 @@
 'use strict';
 
 angular.module('ambariAdminConsole')
-.controller('ViewsListCtrl',['$scope', 'View','$modal', 'Alert', 'ConfirmationModal', '$translate', function($scope, View, $modal, Alert, ConfirmationModal, $translate) {
+.controller('ViewsListCtrl',['$scope', 'View','$modal', 'Alert', 'ConfirmationModal', '$translate', 'Settings', function($scope, View, $modal, Alert, ConfirmationModal, $translate, Settings) {
   var $t = $translate.instant;
   var VIEWS_VERSION_STATUS_TIMEOUT = 5000;
   $scope.isLoading = false;
-  $scope.minInstanceForPagination = 10;
+  $scope.minInstanceForPagination = Settings.minRowsToShowPagination;
 
   function checkViewVersionStatus(view, versionObj, versionNumber) {
     var deferred = View.checkViewVersionStatus(view.view_name, versionNumber);

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/clusters/ClusterInformationCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/clusters/ClusterInformationCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/clusters/ClusterInformationCtrl.js
new file mode 100644
index 0000000..059f399
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/clusters/ClusterInformationCtrl.js
@@ -0,0 +1,106 @@
+/**
+ * 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('ambariAdminConsole')
+.controller('ClusterInformationCtrl',
+['$scope', '$http', '$location', 'Cluster', '$routeParams', '$translate', '$rootScope', 'ConfirmationModal', 'Alert',
+function($scope, $http, $location, Cluster, $routeParams, $translate, $rootScope, ConfirmationModal, Alert) {
+  var $t = $translate.instant;
+  $scope.isDataLoaded = false;
+  $scope.edit = {
+    clusterName: null
+  };
+  $scope.isClusterNameEdited = false;
+
+  $scope.$watch(function() {
+    return $rootScope.cluster;
+  }, function() {
+    $scope.cluster = $rootScope.cluster;
+    if ($scope.cluster) {
+      $scope.edit.clusterName = $scope.cluster.Clusters.cluster_name;
+      $scope.getBlueprint();
+    }
+  }, true);
+
+  $scope.getBlueprint = function () {
+    Cluster.getBlueprint({
+      clusterName: $scope.cluster.Clusters.cluster_name
+    }).then(function (data) {
+      console.debug($t('exportBlueprint.dataLoaded'), data);
+      $scope.isDataLoaded = true;
+      var response = JSON.stringify(data, null, 4),
+        lt = /&lt;/g,
+        gt = /&gt;/g,
+        ap = /&#39;/g,
+        ic = /&#34;/g;
+      $scope.blueprint = response ? response.toString().replace(lt, "<").replace(gt, ">").replace(ap, "'").replace(ic, '"') : "";
+    });
+  };
+
+  $scope.downloadBlueprint = function () {
+    if (window.navigator.msSaveOrOpenBlob) {
+      var blob = new Blob([decodeURIComponent(encodeURI($scope.blueprint))], {
+        type: "text/csv;charset=utf-8;"
+      });
+      navigator.msSaveBlob(blob, 'blueprint.json');
+    } else {
+      var a = document.createElement('a');
+      a.href = 'data:attachment/csv;charset=utf-8,' + encodeURI($scope.blueprint);
+      a.target = '_blank';
+      a.download = 'blueprint.json';
+      document.body.appendChild(a);
+      a.click();
+    }
+  };
+
+  $scope.toggleSaveButton = function() {
+    var value = $scope.edit.clusterName;
+    $scope.isClusterNameEdited = (value !== null && $scope.cluster.Clusters.cluster_name !== value);
+  };
+
+  $scope.confirmClusterNameChange = function() {
+    ConfirmationModal.show(
+      $t('common.clusterNameChangeConfirmation.title'),
+      $t('common.clusterNameChangeConfirmation.message', {
+        clusterName: $scope.edit.clusterName
+      })
+    )
+      .then(function () {
+        $scope.saveClusterName();
+      }).catch(function () {
+      // user clicked cancel
+      $scope.edit.clusterName = $scope.cluster.Clusters.cluster_name;
+      $scope.toggleSaveButton();
+    });
+  };
+
+  $scope.saveClusterName = function() {
+    var oldClusterName = $scope.cluster.Clusters.cluster_name,
+        newClusterName = $scope.edit.clusterName;
+
+    Cluster.editName(oldClusterName, newClusterName).then(function(data) {
+      $scope.cluster.Clusters.cluster_name = newClusterName;
+      $scope.edit.clusterName = newClusterName;
+      $scope.toggleSaveButton();
+      Alert.success($t('common.alerts.clusterRenamed', {clusterName: newClusterName}));
+    }).catch(function(data) {
+      Alert.error($t('common.alerts.cannotRenameCluster', {clusterName: newClusterName}), data.data.message);
+    });
+  };
+}]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/clusters/ClustersManageAccessCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/clusters/ClustersManageAccessCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/clusters/ClustersManageAccessCtrl.js
deleted file mode 100644
index 3a9ad67..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/clusters/ClustersManageAccessCtrl.js
+++ /dev/null
@@ -1,97 +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.
- */
-'use strict';
-
-angular.module('ambariAdminConsole')
-.controller('ClustersManageAccessCtrl', ['$scope', '$location', 'Cluster', '$routeParams', 'Alert', 'PermissionLoader', 'PermissionSaver', '$translate', 'RoleDetailsModal', '$timeout', function($scope, $location, Cluster, $routeParams, Alert, PermissionLoader, PermissionSaver, $translate, RoleDetailsModal, $timeout) {
-  var $t = $translate.instant;
-  $scope.getConstant = function (key) {
-    return $t(key).toLowerCase();
-  };
-  $scope.identity = angular.identity;
-  function reloadClusterData(){
-    PermissionLoader.getClusterPermissions({
-      clusterId: $routeParams.id
-    }).then(function(permissions) {
-      // Refresh data for rendering
-      $scope.permissionsEdit = permissions;
-      $scope.permissions = angular.copy(permissions);
-      //"$scope.isDataLoaded" should be set to true on initial load after "$scope.permissionsEdit" watcher
-      $timeout(function() {
-        $scope.isDataLoaded = true;
-      });
-      var orderedRoles = Cluster.orderedRoles;
-      var pms = [];
-      for (var key=0;key<orderedRoles.length;key++) {
-        pms.push($scope.permissions[orderedRoles[key]]);
-      }
-      $scope.permissions = pms;
-    })
-    .catch(function(data) {
-      Alert.error($t('clusters.alerts.cannotLoadClusterData'), data.data.message);
-    });
-  }
-
-  $scope.isDataLoaded = false;
-  reloadClusterData();
-  $scope.isEditMode = false;
-  $scope.permissions = {};
-  $scope.clusterName = $routeParams.id;
-
-
-  $scope.toggleEditMode = function() {
-    $scope.isEditMode = !$scope.isEditMode;
-  };
-
-  $scope.cancel = function() {
-    $scope.isEditMode = false;
-    $scope.permissionsEdit = angular.copy($scope.permissions); // Reset textedit areaes
-  };
-
-  $scope.save = function() {
-    PermissionSaver.saveClusterPermissions(
-      $scope.permissionsEdit,
-      {
-        clusterId: $routeParams.id
-      }
-    ).then(reloadClusterData)
-    .catch(function(data) {
-      Alert.error($t('common.alerts.cannotSavePermissions'), data.data.message);
-      reloadClusterData();
-    });
-    $scope.isEditMode = false;
-  };
-
-  $scope.$watch(function() {
-    return $scope.permissionsEdit;
-  }, function(newValue) {
-    if (newValue && $scope.isDataLoaded) {
-      $scope.save();
-    }
-  }, true);
-
-  $scope.switchToList = function() {
-    $location.url('/clusters/' + $routeParams.id + '/userAccessList');
-  };
-
-  $scope.showHelpPage = function() {
-    Cluster.getRolesWithAuthorizations().then(function(roles) {
-      RoleDetailsModal.show(roles);
-    });
-  };
-}]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/clusters/UserAccessListCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/clusters/UserAccessListCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/clusters/UserAccessListCtrl.js
deleted file mode 100644
index 9e83b91..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/clusters/UserAccessListCtrl.js
+++ /dev/null
@@ -1,351 +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.
- */
-'use strict';
-
-angular.module('ambariAdminConsole')
-.controller('UserAccessListCtrl',['$scope', '$location', 'Cluster', '$modal', '$rootScope', '$routeParams', 'PermissionSaver', 'Alert', '$translate', 'RoleDetailsModal',
-function($scope, $location, Cluster, $modal, $rootScope, $routeParams, PermissionSaver, Alert, $translate, RoleDetailsModal) {
-  var $t = $translate.instant;
-  $scope.constants = {
-    users: $t('common.users').toLowerCase(),
-    groups: $t('common.groups').toLowerCase()
-  };
-  $scope.users = [];
-  $scope.usersPerPage = 10;
-  $scope.currentPage = 1;
-  $scope.totalUsers = 1;
-  $scope.currentNameFilter = '';
-  $scope.maxVisiblePages = 20;
-  $scope.roles = [];
-  $scope.clusterId = $routeParams.id;
-  $scope.tableInfo = {
-    total: 0,
-    showed: 0,
-    filtered: 0
-  };
-  $scope.isNotEmptyFilter = true;
-  $scope.NONE_ROLE = {
-    "permission_label" : $t('common.none'),
-    "permission_name" : "CLUSTER.NONE"
-  };
-  $scope.ALL_ROLE = {
-    "permission_label" : $t('common.all'),
-    "permission_name" : ""
-  };
-  $scope.AMBARI_ADMIN_ROLE = {
-    "permission_label" : $t('users.roles.ambariAdmin'),
-    "permission_name" : "AMBARI.ADMINISTRATOR"
-  };
-
-  $scope.pageChanged = function() {
-    $scope.loadUsers();
-  };
-  $scope.usersPerPageChanges = function() {
-    $scope.resetPagination();
-  };
-
-  $scope.loadUsers = function(){
-    Cluster.getPrivilegesWithFilters({
-      nameFilter: $scope.currentNameFilter,
-      typeFilter: $scope.currentTypeFilter,
-      roleFilter: $scope.currentRoleFilter,
-      currentPage: $scope.currentPage,
-      usersPerPage: $scope.usersPerPage
-    }).then(function(data) {
-      $scope.totalUsers = data.itemTotal;
-      $scope.users = data.items.map(function (user) {
-        var privilege = $scope.pickEffectivePrivilege(user.privileges);
-        // Redefine principal_name and principal type in case of None
-        privilege.principal_name = user.Users? user.Users.user_name : user.Groups.group_name;
-        if (privilege.permission_label === $t('users.roles.none')) {
-          privilege.principal_type = user.Users ? 'USER' : 'GROUP';
-        }
-        var name = encodeURIComponent(privilege.principal_name);
-        privilege.encoded_name = name;
-        privilege.original_perm = privilege.permission_name;
-        privilege.url = user.Users? ('users/' + name) : ('groups/' + name + '/edit');
-        privilege.editable = Cluster.ineditableRoles.indexOf(privilege.permission_name) == -1;
-        return privilege;
-      });
-      $scope.tableInfo.total = data.itemTotal;
-      $scope.tableInfo.showed = data.items.length;
-    });
-  };
-
-  $scope.pickEffectivePrivilege = function(privileges) {
-    if (privileges && privileges.length > 1) {
-      return privileges.reduce(function(prev, cur) {
-        var prevIndex = $scope.getRoleRank(prev.PrivilegeInfo.permission_name);
-        var curIndex = $scope.getRoleRank(cur.PrivilegeInfo.permission_name)
-        return (prevIndex < curIndex) ? prev : cur;
-      }).PrivilegeInfo;
-    } else if (privileges && privileges.length == 1 && privileges[0].PrivilegeInfo.permission_name !== "VIEW.USER") {
-      return privileges[0].PrivilegeInfo;
-    } else {
-      return angular.copy($scope.NONE_ROLE);
-    }
-  };
-
-  $scope.loadRoles = function() {
-    Cluster.getPermissions().then(function(data) {
-      $scope.roles = data.map(function(item) {
-        return item.PermissionInfo;
-      });
-      // [All, Administrator, ...roles..., None]
-      $scope.roles.unshift(angular.copy($scope.AMBARI_ADMIN_ROLE));
-      $scope.roles.unshift(angular.copy($scope.ALL_ROLE));
-      $scope.roles.push(angular.copy($scope.NONE_ROLE));
-
-      // create filter select list
-      $scope.roleFilterOptions = angular.copy($scope.roles);
-      $scope.roleFilterOptions.pop();  // filter does not support None
-      $scope.roleFilterOptions = $scope.roleFilterOptions.map(function(o) {
-        return {label: o.permission_label, value: o.permission_name};
-      });
-      $scope.currentRoleFilter = $scope.roleFilterOptions[0];
-
-      // create value select list
-      $scope.roleValueOptions = angular.copy($scope.roles)
-      $scope.roleValueOptions.shift(); // value change does not support all/administrator
-      $scope.roleValueOptions.shift();
-    });
-  };
-
-  $scope.getRoleRank = function(permission_name) {
-    var orderedRoles = Cluster.orderedRoles.concat(['VIEW.USER','CLUSTER.NONE']);
-    var index = orderedRoles.indexOf(permission_name);
-    return index;
-  };
-
-  $scope.save = function(user) {
-    var fromNone = (user.original_perm === $scope.NONE_ROLE.permission_name);
-    if (fromNone) {
-      $scope.addPrivilege(user);
-      return;
-    }
-
-    if ($scope.isUserActive) {
-      Cluster.getPrivilegesForResource({
-          nameFilter : user.user_name,
-          typeFilter : $scope.currentTypeFilter
-      }).then(function(data) {
-        var arrayOfPrivileges = data.items[0].privileges;
-        var privilegesOfTypeUser = [];
-        var privilegesOfTypeGroup = [];
-        for (var i = 0; i < arrayOfPrivileges.length; i++) {
-          if(arrayOfPrivileges[i].PrivilegeInfo.permission_name != "VIEW.USER") {
-            if(arrayOfPrivileges[i].PrivilegeInfo.principal_type === "GROUP"){
-              privilegesOfTypeGroup.push(arrayOfPrivileges[i]);
-            } else {
-              privilegesOfTypeUser.push(arrayOfPrivileges[i].PrivilegeInfo);
-            }
-          }
-        }
-
-        var effectivePrivilege = $scope.pickEffectivePrivilege(arrayOfPrivileges);
-        var effectivePrivilegeFromGroups = $scope.pickEffectivePrivilege(privilegesOfTypeGroup);
-        user.principal_type = 'USER';
-        user.original_perm = effectivePrivilege.permission_name;
-        user.editable = (Cluster.ineditableRoles.indexOf(effectivePrivilege.permission_name) === -1);
-
-        var userIndex = $scope.getRoleRank(user.permission_name);
-        var groupIndex = $scope.getRoleRank(effectivePrivilegeFromGroups.permission_name);
-
-        // Process when it's NONE privilege or higher than current effective group privilege
-        if (userIndex <= groupIndex || user.permission_name == $scope.NONE_ROLE.permission_name) {
-          var privilege_ids = privilegesOfTypeUser.filter(function(privilegeOfTypeUser) {
-            return privilegeOfTypeUser.principal_type !== 'ROLE';
-          }).map(function (privilegeOfTypeUser) {
-            return privilegeOfTypeUser.privilege_id;
-          });
-
-          // Purge existing user level privileges if there is any
-          if(privilege_ids.length !== 0) {
-            Cluster.deleteMultiplePrivileges(
-                $routeParams.id,
-                privilege_ids
-            )
-            .then(function() {
-              $scope.addPrivilege(user);
-            });
-          } else {
-            $scope.addPrivilege(user);
-          }
-        } else {
-          Alert.error($t('common.alerts.cannotSavePermissions'),
-              $t('users.alerts.usersEffectivePrivilege', {user_name : user.user_name})
-          );
-          $scope.loadUsers();
-        }
-      });
-    } else {
-      Cluster.getPrivilegesForResource({
-          nameFilter : user.group_name,
-          typeFilter : $scope.currentTypeFilter
-      }).then(function(data) {
-        var arrayOfPrivileges = data.items[0].privileges;
-        var privilegesOfTypeGroup = [];
-        var privilege = $scope.pickEffectivePrivilege(arrayOfPrivileges);
-        user.principal_type = 'GROUP';
-        user.original_perm = privilege.permission_name;
-        user.editable = (Cluster.ineditableRoles.indexOf(privilege.permission_name) === -1);
-
-        arrayOfPrivileges.forEach(function(privilegeOfTypeGroup) {
-          if(privilegeOfTypeGroup.PrivilegeInfo.permission_name != "VIEW.USER") {
-            if (privilegeOfTypeGroup.PrivilegeInfo.principal_type === "GROUP") {
-              privilegesOfTypeGroup.push(privilegeOfTypeGroup.PrivilegeInfo);
-            }
-          }
-        });
-
-        var privilege_ids = [];
-        privilegesOfTypeGroup.forEach(function(privilegeOfTypeGroup) {
-          privilege_ids.push(privilegeOfTypeGroup.privilege_id);
-        });
-
-        //delete all privileges of type GROUP, if they exist
-        //then add the privilege for the group, after which the group displays the effective privilege
-        if(privilege_ids.length !== 0) {
-          Cluster.deleteMultiplePrivileges(
-              $routeParams.id,
-              privilege_ids
-          )
-          .then(function() {
-            $scope.addPrivilege(user);
-          });
-        } else {
-          $scope.addPrivilege(user);
-        }
-      });
-    }
-  };
-
-  $scope.cancel = function(user) {
-    user.permission_name = user.original_perm;
-  };
-
-  $scope.addPrivilege = function(user) {
-    var changeToNone = user.permission_name == $scope.NONE_ROLE.permission_name;
-    if (changeToNone) {
-      if ($scope.isUserActive) {
-        Alert.success($t('users.alerts.roleChangedToNone', {
-            user_name : user.user_name
-        }));
-      } else {
-        $scope.showSuccess(user);
-      }
-      $scope.loadUsers();
-      return;
-    }
-    Cluster.createPrivileges(
-      {
-        clusterId: $routeParams.id
-      },
-      [{PrivilegeInfo: {
-        permission_name: user.permission_name,
-        principal_name: user.principal_name,
-        principal_type: user.principal_type
-      }}]
-    ).then(function() {
-        $scope.showSuccess(user);
-        $scope.loadUsers();
-      })
-      .catch(function(data) {
-        Alert.error($t('common.alerts.cannotSavePermissions'), data.data.message);
-        $scope.loadUsers();
-      });
-  };
-
-  $scope.showSuccess = function(user) {
-    Alert.success($t('users.alerts.roleChanged', {
-      name: user.principal_name,
-      role: $scope.roles.filter(function(r){
-          return r.permission_name == user.permission_name}
-      )[0].permission_label
-    }));
-  };
-
-  $scope.resetPagination = function() {
-    $scope.currentPage = 1;
-    $scope.loadUsers();
-  };
-  $scope.currentRoleFilter = { label:$t('common.all'), value: '' };
-
-
-  $scope.typeFilterOptions = [
-    {label: $t('common.user'), value: 'USER'},
-    {label: $t('common.group'), value: 'GROUP'}
-  ];
-
-  $scope.isUserActive = true;
-
-  $scope.currentTypeFilter = $scope.typeFilterOptions[0];
-
-  $scope.switchToUser = function() {
-    if (!$scope.isUserActive) {
-      $scope.currentTypeFilter = $scope.typeFilterOptions[0];
-      $scope.isUserActive = true;
-      $scope.resetPagination();
-    }
-  };
-
-  $scope.switchToGroup = function() {
-    if ($scope.isUserActive) {
-      $scope.currentTypeFilter = $scope.typeFilterOptions[1];
-      $scope.isUserActive = false;
-      $scope.resetPagination();
-    }
-  };
-
-  $scope.clearFilters = function() {
-    $scope.currentNameFilter = '';
-    $scope.currentRoleFilter = $scope.roleFilterOptions[0];
-    $scope.resetPagination();
-  };
-
-  $scope.loadRoles();
-  $scope.loadUsers();
-
-  $scope.$watch(
-    function (scope) {
-      return Boolean(scope.currentNameFilter || (scope.currentRoleFilter && scope.currentRoleFilter.value));
-    },
-    function (newValue, oldValue, scope) {
-      scope.isNotEmptyFilter = newValue;
-    }
-  );
-
-  $rootScope.$watch(function(scope) {
-    return scope.LDAPSynced;
-  }, function(LDAPSynced) {
-    if(LDAPSynced === true){
-      $rootScope.LDAPSynced = false;
-      $scope.loadUsers();
-    }
-  });
-
-  $scope.switchToBlock = function() {
-    $location.url('/clusters/' + $routeParams.id + '/manageAccess');
-  };
-
-  $scope.showHelpPage = function() {
-    Cluster.getRolesWithAuthorizations().then(function(roles) {
-      RoleDetailsModal.show(roles);
-    });
-  };
-}]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsCreateCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsCreateCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsCreateCtrl.js
deleted file mode 100644
index 67743a0..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsCreateCtrl.js
+++ /dev/null
@@ -1,65 +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.
- */
-'use strict';
-
-angular.module('ambariAdminConsole')
-.controller('GroupsCreateCtrl',['$scope', 'Group', '$location', 'Alert', 'UnsavedDialog', '$translate', function($scope, Group, $location, Alert, UnsavedDialog, $translate) {
-  var $t = $translate.instant;
-  $scope.group = new Group();
-  var targetUrl = '/groups';
-
-  $scope.createGroup = function() {
-    $scope.form.submitted = true;
-    if ($scope.form.$valid){
-      $scope.group.save().then(function() {
-        Alert.success($t('groups.alerts.groupCreated', {groupName: $scope.group.group_name}));
-        $scope.form.$setPristine();
-        $location.path(targetUrl);
-      })
-      .catch(function(data) {
-        Alert.error($t('groups.alerts.groupCreationError'), data.data.message);
-      });
-    }
-  };
-
-  $scope.cancel = function() {
-    $scope.form.$setPristine();
-    $location.path('/groups');
-  };
-
-  $scope.$on('$locationChangeStart', function(event, __targetUrl) {
-    if( $scope.form.$dirty ){
-      UnsavedDialog().then(function(action) {
-        targetUrl = __targetUrl.split('#').pop();
-        switch(action){
-          case 'save':
-            $scope.createGroup();
-            break;
-          case 'discard':
-            $scope.form.$setPristine();
-            $location.path(targetUrl);
-            break;
-          case 'cancel':
-            targetUrl = '/groups';
-            break;
-        }
-      });
-      event.preventDefault();
-    }
-  });
-}]);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsEditCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsEditCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsEditCtrl.js
deleted file mode 100644
index a63ebe2..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsEditCtrl.js
+++ /dev/null
@@ -1,180 +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.
- */
-'use strict';
-
-angular.module('ambariAdminConsole')
-.controller('GroupsEditCtrl',['$scope', 'Group', '$routeParams', 'Cluster', 'View', 'Alert', 'ConfirmationModal', '$location', 'GroupConstants', '$translate', function($scope, Group, $routeParams, Cluster, View, Alert, ConfirmationModal, $location, GroupConstants, $translate) {
-  var $t = $translate.instant;
-  $scope.constants = {
-    group: $t('common.group'),
-    view: $t('common.view').toLowerCase(),
-    cluster: $t('common.cluster').toLowerCase()
-  };
-  $scope.editMode = false;
-  $scope.group = new Group($routeParams.id);
-  $scope.group.editingUsers = [];
-  $scope.groupMembers = [];
-  $scope.dataLoaded = false;
-  
-  $scope.isMembersEditing = false;
-
-  $scope.$watch(function() {
-    return $scope.group.editingUsers;
-  }, function(newValue) {
-    if(newValue && !angular.equals(newValue, $scope.groupMembers)){
-      $scope.updateMembers();  
-    }
-  }, true);
-  
-  $scope.enableMembersEditing = function() {
-    $scope.isMembersEditing = true;
-    $scope.group.editingUsers = angular.copy($scope.groupMembers);
-  };
-  $scope.cancelUpdate = function() {
-    $scope.isMembersEditing = false;
-    $scope.group.editingUsers = '';
-  };
-  $scope.updateMembers = function() {
-    var newMembers = $scope.group.editingUsers.toString().split(',').filter(function(item) {
-      return item.trim();}
-    ).map(function(item) {
-        return item.trim()
-      }
-    );
-    $scope.group.members = newMembers;
-    $scope.group.saveMembers().catch(function(data) {
-        Alert.error($t('groups.alerts.cannotUpdateGroupMembers'), "<div class='break-word'>" + data.message + "</div>");
-      }).finally(function() {
-        loadMembers();
-      });
-    $scope.isMembersEditing = false;
-  };
-
-
-  function loadMembers(){
-    $scope.group.getMembers().then(function(members) {
-      $scope.group.groupTypeName = $t(GroupConstants.TYPES[$scope.group.group_type].LABEL_KEY);
-      $scope.groupMembers = members;
-      $scope.group.editingUsers = angular.copy($scope.groupMembers);
-    });
-  }    
-  
-  $scope.group.isLDAP().then(function(isLDAP) {
-    $scope.group.ldap_group = isLDAP;
-    $scope.group.getGroupType().then(function() {
-      $scope.group.groupTypeName = $t(GroupConstants.TYPES[$scope.group.group_type].LABEL_KEY);
-    });
-    loadMembers();
-  });
-
-  $scope.group.getGroupType();
-
-  $scope.deleteGroup = function(group) {
-    ConfirmationModal.show(
-      $t('common.delete', {
-        term: $t('common.group')
-      }),
-      $t('common.deleteConfirmation', {
-        instanceType: $t('common.group').toLowerCase(),
-        instanceName: '"' + group.group_name + '"'
-      })
-    ).then(function() {
-      Cluster.getPrivilegesForResource({
-        nameFilter : group.group_name,
-        typeFilter : {value: 'GROUP'}
-      }).then(function(data) {
-        var clusterPrivilegesIds = [];
-        var viewsPrivileges = [];
-        if (data.items && data.items.length) {
-          angular.forEach(data.items[0].privileges, function(privilege) {
-            if (privilege.PrivilegeInfo.principal_type === 'GROUP') {
-              if (privilege.PrivilegeInfo.type === 'VIEW') {
-                viewsPrivileges.push({
-                  id: privilege.PrivilegeInfo.privilege_id,
-                  view_name: privilege.PrivilegeInfo.view_name,
-                  version: privilege.PrivilegeInfo.version,
-                  instance_name: privilege.PrivilegeInfo.instance_name
-                });
-              } else {
-                clusterPrivilegesIds.push(privilege.PrivilegeInfo.privilege_id);
-              }
-            }
-          });
-        }
-        group.destroy().then(function() {
-          $location.path('/groups');
-          if (clusterPrivilegesIds.length) {
-            Cluster.getAllClusters().then(function (clusters) {
-              var clusterName = clusters[0].Clusters.cluster_name;
-              Cluster.deleteMultiplePrivileges(clusterName, clusterPrivilegesIds);
-            });
-          }
-          angular.forEach(viewsPrivileges, function(privilege) {
-            View.deletePrivilege(privilege);
-          });
-        });
-      });
-    });
-  };
-
-
-  $scope.removePrivilege = function(name, privilege) {
-    var privilegeObject = {
-        id: privilege.privilege_id,
-        view_name: privilege.view_name,
-        version: privilege.version,
-        instance_name: name
-    };
-    View.deletePrivilege(privilegeObject).then(function() {
-      loadPrivileges();
-    });
-  };
-
-function loadPrivileges() {
-  // Load privileges
-  Group.getPrivileges($routeParams.id).then(function(data) {
-    var privileges = {
-      clusters: {},
-      views: {}
-    };
-    angular.forEach(data.data.items, function(privilege) {
-      privilege = privilege.PrivilegeInfo;
-      if(privilege.type === 'CLUSTER'){
-        // This is cluster
-        privileges.clusters[privilege.cluster_name] = privileges.clusters[privilege.cluster_name] || [];
-        privileges.clusters[privilege.cluster_name].push(privilege.permission_label);
-      } else if ( privilege.type === 'VIEW'){
-        privileges.views[privilege.instance_name] = privileges.views[privilege.instance_name] || { privileges:[]};
-        privileges.views[privilege.instance_name].version = privilege.version;
-        privileges.views[privilege.instance_name].view_name = privilege.view_name;
-        privileges.views[privilege.instance_name].privilege_id = privilege.privilege_id;
-        privileges.views[privilege.instance_name].privileges.push(privilege.permission_label);
-      }
-    });
-
-    $scope.privileges = data.data.items.length ? privileges : null;
-    $scope.noClusterPriv = $.isEmptyObject(privileges.clusters);
-    $scope.noViewPriv = $.isEmptyObject(privileges.views);
-    $scope.hidePrivileges = $scope.noClusterPriv && $scope.noViewPriv;
-    $scope.dataLoaded = true;
-  }).catch(function(data) {
-    Alert.error($t('common.alerts.cannotLoadPrivileges'), data.data.message);
-  });
-}
-loadPrivileges();
-}]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsListCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsListCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsListCtrl.js
deleted file mode 100644
index 7cc590e..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsListCtrl.js
+++ /dev/null
@@ -1,106 +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.
- */
-'use strict';
-
-angular.module('ambariAdminConsole')
-.controller('GroupsListCtrl',['$scope', 'Group', '$modal', 'ConfirmationModal', '$rootScope', 'GroupConstants', '$translate', function($scope, Group, $modal, ConfirmationModal, $rootScope, GroupConstants, $translate) {
-  var $t = $translate.instant;
-  $scope.constants = {
-    groups: $t('common.groups').toLowerCase()
-  };
-  $scope.isLoading = false;
-  $scope.groups = [];
-
-  $scope.groupsPerPage = 10;
-  $scope.currentPage = 1;
-  $scope.totalGroups = 1;
-  $scope.currentNameFilter = '';
-  $scope.maxVisiblePages=20;
-  $scope.tableInfo = {
-    total: 0,
-    showed: 0
-  };
-  $scope.isNotEmptyFilter = true;
-
-  $scope.pageChanged = function() {
-    loadGroups();
-  };
-  $scope.groupsPerPageChanges = function() {
-    loadGroups();
-  };
-
-  $scope.resetPagination = function() {
-    $scope.currentPage = 1;
-    loadGroups();
-  };
-
-  function loadGroups(){
-    $scope.isLoading = true;
-    Group.all({
-      currentPage: $scope.currentPage, 
-      groupsPerPage: $scope.groupsPerPage, 
-      searchString: $scope.currentNameFilter,
-      group_type: $scope.currentTypeFilter.value
-    }).then(function(groups) {
-      $scope.isLoading = false;
-      $scope.totalGroups = groups.itemTotal;
-      $scope.groups = groups.map(Group.makeGroup);
-      $scope.tableInfo.total = groups.itemTotal;
-      $scope.tableInfo.showed = groups.length;
-    })
-    .catch(function(data) {
-      console.error($t('groups.alerts.getGroupsListError'));
-    });
-  }
-
-  $scope.typeFilterOptions = [{ label: $t('common.all'), value: '*'}]
-    .concat(Object.keys(GroupConstants.TYPES).map(function(key) {
-      return {
-        label: $t(GroupConstants.TYPES[key].LABEL_KEY),
-        value: GroupConstants.TYPES[key].VALUE
-      };
-  }));
-  $scope.currentTypeFilter = $scope.typeFilterOptions[0];
-
-  $scope.clearFilters = function () {
-    $scope.currentNameFilter = '';
-    $scope.currentTypeFilter = $scope.typeFilterOptions[0];
-    $scope.resetPagination();
-  };
-  
-  loadGroups();
-
-  $scope.$watch(
-    function (scope) {
-      return Boolean(scope.currentNameFilter || (scope.currentTypeFilter && scope.currentTypeFilter.value !== '*'));
-    },
-    function (newValue, oldValue, scope) {
-      scope.isNotEmptyFilter = newValue;
-    }
-  );
-
-  $rootScope.$watch(function(scope) {
-    return scope.LDAPSynced;
-  }, function(LDAPSynced) {
-    if(LDAPSynced === true){
-      $rootScope.LDAPSynced = false;
-      loadGroups();
-    }
-  });
-
-}]);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/GroupCreateCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/GroupCreateCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/GroupCreateCtrl.js
new file mode 100644
index 0000000..94a2c9f
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/GroupCreateCtrl.js
@@ -0,0 +1,112 @@
+/**
+ * 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('ambariAdminConsole')
+.controller('GroupCreateCtrl',
+['$scope', '$rootScope', 'Group', '$location', 'Alert', 'UnsavedDialog', '$translate', '$modalInstance', 'Cluster',
+function($scope, $rootScope, Group, $location, Alert, UnsavedDialog, $translate, $modalInstance, Cluster) {
+  var $t = $translate.instant;
+
+  $scope.form = {};
+  $scope.formData = {
+    groupName: '',
+    members: [],
+    role: ''
+  };
+  $scope.roleOptions = [];
+
+
+  function loadRoles() {
+    Cluster.getPermissions().then(function(data) {
+      $scope.roleOptions = data.map(function(item) {
+        return item.PermissionInfo;
+      });
+    });
+  }
+
+  function unsavedChangesCheck() {
+    if ($scope.form.groupCreateForm.$dirty) {
+      UnsavedDialog().then(function (action) {
+        switch (action) {
+          case 'save':
+            $scope.save();
+            break;
+          case 'discard':
+            $modalInstance.close('discard');
+            break;
+          case 'cancel':
+            break;
+        }
+      });
+    } else {
+      $modalInstance.close('discard');
+    }
+  }
+
+  function saveMembers(group, members) {
+    group.members = members.filter(function(item) {
+      return item.trim();
+    }).map(function(item) {
+      return item.trim();
+    });
+    group.saveMembers().catch(function(data) {
+      Alert.error($t('groups.alerts.cannotUpdateGroupMembers'), "<div class='break-word'>" + data.message + "</div>");
+    });
+  }
+
+  $scope.save = function () {
+    $scope.form.groupCreateForm.submitted = true;
+    if ($scope.form.groupCreateForm.$valid) {
+      var group = new Group($scope.formData.groupName);
+      group.save().then(function () {
+        saveMembers(group, $scope.formData.members);
+        saveRole();
+        $modalInstance.dismiss('created');
+        Alert.success($t('groups.alerts.groupCreated', {groupName: $scope.formData.groupName}));
+      })
+      .catch(function (data) {
+        Alert.error($t('groups.alerts.groupCreationError'), data.data.message);
+      });
+    }
+  };
+
+  function saveRole() {
+    Cluster.createPrivileges(
+      {
+        clusterId: $rootScope.cluster.Clusters.cluster_name
+      },
+      [{PrivilegeInfo: {
+        permission_name: $scope.roleOptions.filter(function(role) {
+          return role.permission_id == Number($scope.formData.role);
+        })[0].permission_name,
+        principal_name: $scope.formData.groupName,
+        principal_type: 'GROUP'
+      }}]
+    )
+      .catch(function(data) {
+        Alert.error($t('common.alerts.cannotSavePermissions'), data.data.message);
+      });
+  }
+
+  $scope.cancel = function () {
+    unsavedChangesCheck();
+  };
+
+  loadRoles();
+}]);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/GroupEditCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/GroupEditCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/GroupEditCtrl.js
new file mode 100644
index 0000000..ff705eb
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/GroupEditCtrl.js
@@ -0,0 +1,182 @@
+/**
+ * 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('ambariAdminConsole')
+.controller('GroupEditCtrl',
+['$scope', 'Group', '$routeParams', 'Cluster', 'View', 'Alert', 'ConfirmationModal', '$location', 'GroupConstants', '$translate',
+function($scope, Group, $routeParams, Cluster, View, Alert, ConfirmationModal, $location, GroupConstants, $translate) {
+  var $t = $translate.instant;
+  $scope.constants = {
+    group: $t('common.group'),
+    view: $t('common.view').toLowerCase(),
+    cluster: $t('common.cluster').toLowerCase()
+  };
+  $scope.editMode = false;
+  $scope.group = new Group($routeParams.id);
+  $scope.group.editingUsers = [];
+  $scope.groupMembers = [];
+  $scope.dataLoaded = false;
+  
+  $scope.isMembersEditing = false;
+
+  $scope.$watch(function() {
+    return $scope.group.editingUsers;
+  }, function(newValue) {
+    if(newValue && !angular.equals(newValue, $scope.groupMembers)){
+      $scope.updateMembers();  
+    }
+  }, true);
+  
+  $scope.enableMembersEditing = function() {
+    $scope.isMembersEditing = true;
+    $scope.group.editingUsers = angular.copy($scope.groupMembers);
+  };
+  $scope.cancelUpdate = function() {
+    $scope.isMembersEditing = false;
+    $scope.group.editingUsers = '';
+  };
+  $scope.updateMembers = function() {
+    var newMembers = $scope.group.editingUsers.toString().split(',').filter(function(item) {
+      return item.trim();}
+    ).map(function(item) {
+        return item.trim()
+      }
+    );
+    $scope.group.members = newMembers;
+    $scope.group.saveMembers().catch(function(data) {
+        Alert.error($t('groups.alerts.cannotUpdateGroupMembers'), "<div class='break-word'>" + data.message + "</div>");
+      }).finally(function() {
+        loadMembers();
+      });
+    $scope.isMembersEditing = false;
+  };
+
+
+  function loadMembers(){
+    $scope.group.getMembers().then(function(members) {
+      $scope.group.groupTypeName = $t(GroupConstants.TYPES[$scope.group.group_type].LABEL_KEY);
+      $scope.groupMembers = members;
+      $scope.group.editingUsers = angular.copy($scope.groupMembers);
+    });
+  }    
+  
+  $scope.group.isLDAP().then(function(isLDAP) {
+    $scope.group.ldap_group = isLDAP;
+    $scope.group.getGroupType().then(function() {
+      $scope.group.groupTypeName = $t(GroupConstants.TYPES[$scope.group.group_type].LABEL_KEY);
+    });
+    loadMembers();
+  });
+
+  $scope.group.getGroupType();
+
+  $scope.deleteGroup = function(group) {
+    ConfirmationModal.show(
+      $t('common.delete', {
+        term: $t('common.group')
+      }),
+      $t('common.deleteConfirmation', {
+        instanceType: $t('common.group').toLowerCase(),
+        instanceName: '"' + group.group_name + '"'
+      })
+    ).then(function() {
+      Cluster.getPrivilegesForResource({
+        nameFilter : group.group_name,
+        typeFilter : {value: 'GROUP'}
+      }).then(function(data) {
+        var clusterPrivilegesIds = [];
+        var viewsPrivileges = [];
+        if (data.items && data.items.length) {
+          angular.forEach(data.items[0].privileges, function(privilege) {
+            if (privilege.PrivilegeInfo.principal_type === 'GROUP') {
+              if (privilege.PrivilegeInfo.type === 'VIEW') {
+                viewsPrivileges.push({
+                  id: privilege.PrivilegeInfo.privilege_id,
+                  view_name: privilege.PrivilegeInfo.view_name,
+                  version: privilege.PrivilegeInfo.version,
+                  instance_name: privilege.PrivilegeInfo.instance_name
+                });
+              } else {
+                clusterPrivilegesIds.push(privilege.PrivilegeInfo.privilege_id);
+              }
+            }
+          });
+        }
+        group.destroy().then(function() {
+          $location.path('/userManagement');
+          if (clusterPrivilegesIds.length) {
+            Cluster.getAllClusters().then(function (clusters) {
+              var clusterName = clusters[0].Clusters.cluster_name;
+              Cluster.deleteMultiplePrivileges(clusterName, clusterPrivilegesIds);
+            });
+          }
+          angular.forEach(viewsPrivileges, function(privilege) {
+            View.deletePrivilege(privilege);
+          });
+        });
+      });
+    });
+  };
+
+
+  $scope.removePrivilege = function(name, privilege) {
+    var privilegeObject = {
+        id: privilege.privilege_id,
+        view_name: privilege.view_name,
+        version: privilege.version,
+        instance_name: name
+    };
+    View.deletePrivilege(privilegeObject).then(function() {
+      loadPrivileges();
+    });
+  };
+
+function loadPrivileges() {
+  // Load privileges
+  Group.getPrivileges($routeParams.id).then(function(data) {
+    var privileges = {
+      clusters: {},
+      views: {}
+    };
+    angular.forEach(data.data.items, function(privilege) {
+      privilege = privilege.PrivilegeInfo;
+      if(privilege.type === 'CLUSTER'){
+        // This is cluster
+        privileges.clusters[privilege.cluster_name] = privileges.clusters[privilege.cluster_name] || [];
+        privileges.clusters[privilege.cluster_name].push(privilege.permission_label);
+      } else if ( privilege.type === 'VIEW'){
+        privileges.views[privilege.instance_name] = privileges.views[privilege.instance_name] || { privileges:[]};
+        privileges.views[privilege.instance_name].version = privilege.version;
+        privileges.views[privilege.instance_name].view_name = privilege.view_name;
+        privileges.views[privilege.instance_name].privilege_id = privilege.privilege_id;
+        privileges.views[privilege.instance_name].privileges.push(privilege.permission_label);
+      }
+    });
+
+    $scope.privileges = data.data.items.length ? privileges : null;
+    $scope.noClusterPriv = $.isEmptyObject(privileges.clusters);
+    $scope.noViewPriv = $.isEmptyObject(privileges.views);
+    $scope.hidePrivileges = $scope.noClusterPriv && $scope.noViewPriv;
+    $scope.dataLoaded = true;
+  }).catch(function(data) {
+    Alert.error($t('common.alerts.cannotLoadPrivileges'), data.data.message);
+  });
+}
+loadPrivileges();
+}]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/GroupsListCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/GroupsListCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/GroupsListCtrl.js
new file mode 100644
index 0000000..af77ba9
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/GroupsListCtrl.js
@@ -0,0 +1,170 @@
+/**
+ * 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('ambariAdminConsole')
+.controller('GroupsListCtrl',
+['$scope', 'Group', '$modal', 'ConfirmationModal', '$rootScope', 'GroupConstants', '$translate', 'Settings', 'Cluster', 'View', '$location',
+function($scope, Group, $modal, ConfirmationModal, $rootScope, GroupConstants, $translate, Settings, Cluster, View, $location) {
+  var $t = $translate.instant;
+  $scope.constants = {
+    groups: $t('common.groups').toLowerCase()
+  };
+  $scope.minRowsToShowPagination = Settings.minRowsToShowPagination;
+  $scope.isLoading = false;
+  $scope.groups = [];
+
+  $scope.groupsPerPage = 10;
+  $scope.currentPage = 1;
+  $scope.totalGroups = 0;
+  $scope.filter = {
+    name: '',
+    type: null
+  };
+  $scope.maxVisiblePages=20;
+  $scope.tableInfo = {
+    total: 0,
+    showed: 0
+  };
+  $scope.isNotEmptyFilter = true;
+
+  $scope.pageChanged = function() {
+    loadGroups();
+  };
+  $scope.groupsPerPageChanges = function() {
+    loadGroups();
+  };
+
+  $scope.resetPagination = function() {
+    $scope.currentPage = 1;
+    loadGroups();
+  };
+
+  function loadGroups(){
+    $scope.isLoading = true;
+    Group.all({
+      currentPage: $scope.currentPage, 
+      groupsPerPage: $scope.groupsPerPage, 
+      searchString: $scope.filter.name,
+      group_type: $scope.filter.type.value
+    }).then(function(groups) {
+      $scope.isLoading = false;
+      $scope.totalGroups = groups.itemTotal;
+      $scope.groups = groups.map(Group.makeGroup);
+      $scope.tableInfo.total = groups.itemTotal;
+      $scope.tableInfo.showed = groups.length;
+    })
+    .catch(function(data) {
+      console.error($t('groups.alerts.getGroupsListError'));
+    });
+  }
+
+  $scope.typeFilterOptions = [{ label: $t('common.all'), value: '*'}]
+    .concat(Object.keys(GroupConstants.TYPES).map(function(key) {
+      return {
+        label: $t(GroupConstants.TYPES[key].LABEL_KEY),
+        value: GroupConstants.TYPES[key].VALUE
+      };
+  }));
+  $scope.filter.type = $scope.typeFilterOptions[0];
+
+  $scope.clearFilters = function () {
+    $scope.filter.name = '';
+    $scope.filter.type = $scope.typeFilterOptions[0];
+    $scope.resetPagination();
+  };
+  
+  loadGroups();
+
+  $scope.$watch(
+    function (scope) {
+      return Boolean(scope.filter.name || (scope.filter.type && scope.filter.type.value !== '*'));
+    },
+    function (newValue, oldValue, scope) {
+      scope.isNotEmptyFilter = newValue;
+    }
+  );
+
+  $rootScope.$watch(function(scope) {
+    return scope.LDAPSynced;
+  }, function(LDAPSynced) {
+    if(LDAPSynced === true){
+      $rootScope.LDAPSynced = false;
+      loadGroups();
+    }
+  });
+
+  $scope.createGroup = function () {
+    var modalInstance = $modal.open({
+      templateUrl: 'views/userManagement/modals/groupCreate.html',
+      controller: 'GroupCreateCtrl',
+      backdrop: 'static'
+    });
+
+    modalInstance.result.catch(loadGroups);
+  };
+
+  $scope.deleteGroup = function(group) {
+    ConfirmationModal.show(
+      $t('common.delete', {
+        term: $t('common.group')
+      }),
+      $t('common.deleteConfirmation', {
+        instanceType: $t('common.group').toLowerCase(),
+        instanceName: '"' + group.group_name + '"'
+      })
+    ).then(function() {
+      Cluster.getPrivilegesForResource({
+        nameFilter : group.group_name,
+        typeFilter : {value: 'GROUP'}
+      }).then(function(data) {
+        var clusterPrivilegesIds = [];
+        var viewsPrivileges = [];
+        if (data.items && data.items.length) {
+          angular.forEach(data.items[0].privileges, function(privilege) {
+            if (privilege.PrivilegeInfo.principal_type === 'GROUP') {
+              if (privilege.PrivilegeInfo.type === 'VIEW') {
+                viewsPrivileges.push({
+                  id: privilege.PrivilegeInfo.privilege_id,
+                  view_name: privilege.PrivilegeInfo.view_name,
+                  version: privilege.PrivilegeInfo.version,
+                  instance_name: privilege.PrivilegeInfo.instance_name
+                });
+              } else {
+                clusterPrivilegesIds.push(privilege.PrivilegeInfo.privilege_id);
+              }
+            }
+          });
+        }
+        group.destroy().then(function() {
+          if (clusterPrivilegesIds.length) {
+            Cluster.getAllClusters().then(function (clusters) {
+              var clusterName = clusters[0].Clusters.cluster_name;
+              Cluster.deleteMultiplePrivileges(clusterName, clusterPrivilegesIds);
+            });
+          }
+          angular.forEach(viewsPrivileges, function(privilege) {
+            View.deletePrivilege(privilege);
+          });
+          loadGroups();
+        });
+      });
+    });
+  };
+
+}]);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UserCreateCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UserCreateCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UserCreateCtrl.js
new file mode 100644
index 0000000..34637ae
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UserCreateCtrl.js
@@ -0,0 +1,108 @@
+/**
+ * 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('ambariAdminConsole')
+.controller('UserCreateCtrl',
+['$scope', '$rootScope', 'User', '$location', 'Alert', 'UnsavedDialog', '$translate', 'Cluster', '$modalInstance',
+function($scope, $rootScope, User, $location, Alert, UnsavedDialog, $translate, Cluster, $modalInstance) {
+  var $t = $translate.instant;
+
+  $scope.form = {};
+  $scope.formData = {
+    userName: '',
+    password: '',
+    confirmPassword: '',
+    role: null,
+    isAdmin: false,
+    isActive: true
+  };
+  $scope.roleOptions = [];
+
+  function loadRoles() {
+    Cluster.getPermissions().then(function(data) {
+      $scope.roleOptions = data.map(function(item) {
+        return item.PermissionInfo;
+      });
+    });
+  }
+
+  function unsavedChangesCheck() {
+    if ($scope.form.userCreateForm.$dirty) {
+      UnsavedDialog().then(function (action) {
+        switch (action) {
+          case 'save':
+            $scope.save();
+            break;
+          case 'discard':
+            $modalInstance.close('discard');
+            break;
+          case 'cancel':
+            break;
+        }
+      });
+    } else {
+      $modalInstance.close('discard');
+    }
+  }
+
+  $scope.save = function () {
+    $scope.form.userCreateForm.submitted = true;
+    if ($scope.form.userCreateForm.$valid) {
+      User.create({
+        'Users/user_name': $scope.formData.userName,
+        'Users/password': $scope.formData.password,
+        'Users/active': Boolean($scope.formData.isActive),
+        'Users/admin': Boolean($scope.formData.isAdmin)
+      }).then(function () {
+        saveRole();
+        $modalInstance.dismiss('created');
+        Alert.success($t('users.alerts.userCreated', {
+          userName: $scope.formData.userName,
+          encUserName: encodeURIComponent($scope.formData.userName)
+        }));
+      }).catch(function (data) {
+        Alert.error($t('users.alerts.userCreationError'), data.data.message);
+      });
+    }
+  };
+
+  function saveRole() {
+    Cluster.createPrivileges(
+      {
+        clusterId: $rootScope.cluster.Clusters.cluster_name
+      },
+      [{PrivilegeInfo: {
+        permission_name: $scope.roleOptions.filter(function(role) {
+          return role.permission_id == Number($scope.formData.role);
+        })[0].permission_name,
+        principal_name: $scope.formData.userName,
+        principal_type: 'USER'
+      }}]
+    )
+    .catch(function(data) {
+      Alert.error($t('common.alerts.cannotSavePermissions'), data.data.message);
+    });
+  }
+
+  $scope.cancel = function () {
+    unsavedChangesCheck();
+  };
+
+  loadRoles();
+}]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UserEditCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UserEditCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UserEditCtrl.js
new file mode 100644
index 0000000..001bb1b
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UserEditCtrl.js
@@ -0,0 +1,290 @@
+/**
+ * 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('ambariAdminConsole')
+.controller('UserEditCtrl', ['$scope', '$routeParams', 'Cluster', 'User', 'View', '$modal', '$location', 'ConfirmationModal', 'Alert', 'Auth', 'getDifference', 'Group', '$q', 'UserConstants', '$translate', function($scope, $routeParams, Cluster, User, View, $modal, $location, ConfirmationModal, Alert, Auth, getDifference, Group, $q, UserConstants, $translate) {
+
+  var $t = $translate.instant;
+
+  $scope.constants = {
+    user: $t('common.user'),
+    status: $t('users.status'),
+    admin: $t('users.admin'),
+    password: $t('users.password'),
+    view: $t('common.view').toLowerCase(),
+    cluster: $t('common.cluster').toLowerCase()
+  };
+
+  function loadUserInfo(){
+    User.get($routeParams.id).then(function(data) {
+      $scope.user = User.makeUser(data).Users;
+      $scope.isCurrentUser = $scope.user.user_name === Auth.getCurrentUser();
+      $scope.editingGroupsList = angular.copy($scope.user.groups);
+    });
+  }
+
+  loadUserInfo();
+  $scope.user;
+  $scope.isCurrentUser = true;
+  $scope.dataLoaded = false;
+
+  $scope.isGroupEditing = false;
+  $scope.enableGroupEditing = function() {
+    $scope.isGroupEditing = true;
+    $scope.editingGroupsList = angular.copy($scope.user.groups);
+  };
+
+  $scope.$watch(function() {
+    return $scope.editingGroupsList;
+  }, function(newValue) {
+    if(newValue){
+      if( !angular.equals(newValue, $scope.user.groups) ){
+        $scope.updateGroups();
+      }
+    }
+  }, true);
+
+  $scope.updateGroups = function() {
+    var groups = $scope.editingGroupsList.toString().split(',').filter(function(item) {return item.trim();}).map(function(item) {return item.trim()});
+    var diff = getDifference($scope.user.groups, groups);
+    var promises = [];
+    // Remove user from groups
+    angular.forEach(diff.del, function(groupName) {
+      promises.push(Group.removeMemberFromGroup(groupName, $scope.user.user_name).catch(function(data) {
+        Alert.error($t('users.alerts.removeUserError'), data.data.message);
+      }));
+    });
+    // Add user to groups
+    angular.forEach(diff.add, function(groupName) {
+      promises.push(Group.addMemberToGroup(groupName, $scope.user.user_name).catch(function(data) {
+        Alert.error($t('users.alerts.cannotAddUser'), data.data.message);
+      }));
+    });
+    $q.all(promises).then(function() {
+      loadUserInfo();
+    });
+    $scope.isGroupEditing = false;
+  };
+
+  $scope.getUserMembership = function(userType) {
+    if(userType) {
+	return $t(UserConstants.TYPES[userType].LABEL_KEY) + " " + $t('users.groupMembership');
+    }
+  };
+
+  $scope.cancelUpdate = function() {
+    $scope.isGroupEditing = false;
+    $scope.editingGroupsList = '';
+  };
+
+  $scope.openChangePwdDialog = function() {
+    var modalInstance = $modal.open({
+      templateUrl: 'views/userManagement/modals/changePassword.html',
+      resolve: {
+        userName: function() {
+          return $scope.user.user_name;
+        }
+      },
+      controller: ['$scope', 'userName', function($scope, userName) {
+        $scope.passwordData = {
+          password: '',
+          currentUserPassword: ''
+        };
+
+        $scope.form = {};
+        $scope.userName = userName;
+
+        $scope.ok = function() {
+          $scope.form.passwordChangeForm.submitted = true;
+          if($scope.form.passwordChangeForm.$valid){
+
+            modalInstance.close({
+              password: $scope.passwordData.password, 
+              currentUserPassword: $scope.passwordData.currentUserPassword
+            });
+          }
+        };
+        $scope.cancel = function() {
+          modalInstance.dismiss('cancel');
+        };
+      }]
+    });
+
+    modalInstance.result.then(function(data) {
+      User.setPassword($scope.user, data.password, data.currentUserPassword).then(function() {
+        Alert.success($t('users.alerts.passwordChanged'));
+      }).catch(function(data) {
+        Alert.error($t('users.alerts.cannotChangePassword'), data.data.message);
+      });
+    }); 
+  };
+
+  $scope.toggleUserActive = function() {
+    if(!$scope.isCurrentUser){
+      var newStatusKey = $scope.user.active ? 'inactive' : 'active',
+        newStatus = $t('users.' + newStatusKey).toLowerCase();
+      ConfirmationModal.show(
+        $t('users.changeStatusConfirmation.title'),
+        $t('users.changeStatusConfirmation.message', {
+          userName: $scope.user.user_name,
+          status: newStatus
+        })
+      ).then(function() {
+        User.setActive($scope.user.user_name, $scope.user.active)
+          .catch(function(data) {
+            Alert.error($t('common.alerts.cannotUpdateStatus'), data.data.message);
+            $scope.user.active = !$scope.user.active;
+          });
+      })
+      .catch(function() {
+        $scope.user.active = !$scope.user.active;
+      });
+    }
+  };    
+  $scope.toggleUserAdmin = function() {
+    if(!$scope.isCurrentUser){
+      var action = $scope.user.admin ?
+        $t('users.changePrivilegeConfirmation.revoke') : $t('users.changePrivilegeConfirmation.grant');
+      ConfirmationModal.show(
+        $t('users.changePrivilegeConfirmation.title'),
+        $t('users.changePrivilegeConfirmation.message', {
+          action: action,
+          userName: $scope.user.user_name
+        })
+      ).then(function() {
+        User.setAdmin($scope.user.user_name, $scope.user.admin)
+        .then(function() {
+          loadPrivileges();
+        })
+        .catch(function (data) {
+          Alert.error($t('common.alerts.cannotUpdateAdminStatus'), data.data.message);
+          $scope.user.admin = !$scope.user.admin;
+        });
+      })
+      .catch(function() {
+        $scope.user.admin = !$scope.user.admin;
+      });
+
+    }
+  };
+
+  $scope.removePrivilege = function(name, privilege) {
+    var privilegeObject = {
+        id: privilege.privilege_id,
+        view_name: privilege.view_name,
+        version: privilege.version,
+        instance_name: name
+    };
+    View.deletePrivilege(privilegeObject).then(function() {
+      loadPrivileges();
+    });
+  };
+
+  $scope.deleteUser = function() {
+    ConfirmationModal.show(
+      $t('common.delete', {
+        term: $t('common.user')
+      }),
+      $t('common.deleteConfirmation', {
+        instanceType: $t('common.user').toLowerCase(),
+        instanceName: '"' + $scope.user.user_name + '"'
+      })
+    ).then(function() {
+      Cluster.getPrivilegesForResource({
+        nameFilter : $scope.user.user_name,
+        typeFilter : {value: 'USER'}
+      }).then(function(data) {
+        var clusterPrivilegesIds = [];
+        var viewsPrivileges = [];
+        if (data.items && data.items.length) {
+          angular.forEach(data.items[0].privileges, function(privilege) {
+            if (privilege.PrivilegeInfo.principal_type === 'USER') {
+              if (privilege.PrivilegeInfo.type === 'VIEW') {
+                viewsPrivileges.push({
+                  id: privilege.PrivilegeInfo.privilege_id,
+                  view_name: privilege.PrivilegeInfo.view_name,
+                  version: privilege.PrivilegeInfo.version,
+                  instance_name: privilege.PrivilegeInfo.instance_name
+                });
+              } else {
+                clusterPrivilegesIds.push(privilege.PrivilegeInfo.privilege_id);
+              }
+            }
+          });
+        }
+        User.delete($scope.user.user_name).then(function() {
+          $location.path('/userManagement');
+          if (clusterPrivilegesIds.length) {
+            Cluster.getAllClusters().then(function (clusters) {
+              var clusterName = clusters[0].Clusters.cluster_name;
+              Cluster.deleteMultiplePrivileges(clusterName, clusterPrivilegesIds);
+            });
+          }
+          angular.forEach(viewsPrivileges, function(privilege) {
+            View.deletePrivilege(privilege);
+          });
+        });
+      });
+    });
+  };
+
+  // Load privileges
+  function loadPrivileges(){
+    User.getPrivileges($routeParams.id).then(function(data) {
+      var privileges = {
+        clusters: {},
+        views: {}
+      };
+      angular.forEach(data.data.items, function(privilege) {
+        privilege = privilege.PrivilegeInfo;
+        if(privilege.type === 'CLUSTER'){
+          // This is cluster
+          if (privileges.clusters[privilege.cluster_name]) {
+            var preIndex = Cluster.orderedRoles.indexOf(privileges.clusters[privilege.cluster_name].permission_name);
+            var curIndex = Cluster.orderedRoles.indexOf(privilege.permission_name);
+            // replace when cur is a more powerful role
+            if (curIndex < preIndex) {
+              privileges.clusters[privilege.cluster_name] = privilege;
+            }
+          } else {
+            privileges.clusters[privilege.cluster_name] = privilege;
+          }
+        } else if ( privilege.type === 'VIEW'){
+          privileges.views[privilege.instance_name] = privileges.views[privilege.instance_name] || { privileges:[]};
+          privileges.views[privilege.instance_name].version = privilege.version;
+          privileges.views[privilege.instance_name].view_name = privilege.view_name;
+          privileges.views[privilege.instance_name].privilege_id = privilege.privilege_id;
+          if (privileges.views[privilege.instance_name].privileges.indexOf(privilege.permission_label) == -1) {
+            privileges.views[privilege.instance_name].privileges.push(privilege.permission_label);
+          }
+        }
+      });
+
+      $scope.privileges = data.data.items.length ? privileges : null;
+      $scope.noClusterPriv = $.isEmptyObject(privileges.clusters);
+      $scope.noViewPriv = $.isEmptyObject(privileges.views);
+      $scope.hidePrivileges = $scope.noClusterPriv && $scope.noViewPriv;
+      $scope.dataLoaded = true;
+
+    }).catch(function(data) {
+      Alert.error($t('common.alerts.cannotLoadPrivileges'), data.data.message);
+    });
+  }
+  loadPrivileges();
+}]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UserManagementCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UserManagementCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UserManagementCtrl.js
new file mode 100644
index 0000000..e9ec6ab
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UserManagementCtrl.js
@@ -0,0 +1,23 @@
+/**
+ * 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('ambariAdminConsole')
+.controller('UserManagementCtrl', ['$scope', function($scope) {
+  $scope.activeTab = 'USERS';
+}]);


[2/4] ambari git commit: AMBARI-22508 Ambari 3.0: Implement new design for Admin View: User Management. (atkach)

Posted by at...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/modals/groupCreate.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/modals/groupCreate.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/modals/groupCreate.html
new file mode 100644
index 0000000..e0c1144
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/modals/groupCreate.html
@@ -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.
+-->
+
+<form id="create-group-form" role="form" novalidate name="form.groupCreateForm">
+  <div class="modal-header">
+    <h1 class="modal-title">
+      {{'groups.createLocal' | translate}}
+    </h1>
+  </div>
+  <div class="modal-body">
+    <div class="form-group"
+         ng-class="{ 'has-error': (form.groupCreateForm.groupName.$error.required || form.groupCreateForm.groupName.$error.pattern) && form.groupCreateForm.submitted }">
+      <label for="groupName">
+        {{'groups.name' | translate}}<span>&nbsp;*</span>&nbsp;
+      </label>
+      <input type="text"
+             placeholder="{{'groups.name' | translate}}"
+             ng-pattern="/^([a-zA-Z0-9._\s]+)$/"
+             autofocus
+             ng-maxlength="80"
+             autocomplete="off"
+             class="form-control"
+             ng-model="formData.groupName"
+             name="groupName"
+             id="groupName"
+             ng-change="checkIfInstanceExist()"
+             required>
+      <span class="help-block validation-block"
+            ng-show='form.groupCreateForm.groupName.$error.required && form.groupCreateForm.submitted'>
+        {{'common.alerts.fieldRequired' | translate}}
+      </span>
+      <span class="help-block validation-block"
+            ng-show='form.groupCreateForm.groupName.$error.pattern && form.groupCreateForm.submitted'>
+        {{'common.alerts.noSpecialChars' | translate}}
+      </span>
+    </div>
+
+    <div class="form-group">
+      <label>{{'groups.addUsers' | translate}}</label>
+      <div>
+        <editable-list items-source="formData.members" resource-type="User" editable="true"></editable-list>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="form-group col-sm-6"
+           ng-class="{ 'has-error': form.groupCreateForm.role.$error.required && form.groupCreateForm.submitted }">
+        <label for="role" class="nowrap">
+          {{'groups.role' | translate}}
+          <i class="fa fa-question-circle" aria-hidden="true"></i>
+        </label>
+        <select
+          class="form-control"
+          id="role"
+          name="role"
+          ng-model="formData.role">
+          <option value="" disabled selected>{{'common.select' | translate}}</option>
+          <option ng-repeat="role in roleOptions" value="{{role.permission_id}}">{{role.permission_label}}</option>
+        </select>
+        <span class="help-block validation-block" ng-show='form.groupCreateForm.role.$error.required && form.groupCreateForm.submitted'>
+          {{'common.alerts.fieldRequired' | translate}}
+        </span>
+      </div>
+    </div>
+
+  </div>
+  <div class="modal-footer">
+    <button class="btn btn-default" ng-click="cancel()">{{'common.controls.cancel' | translate}}</button>
+    <button class="btn btn-primary" ng-click="save()" type="submit">{{'common.controls.save' | translate}}</button>
+  </div>
+</form>

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/modals/userCreate.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/modals/userCreate.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/modals/userCreate.html
new file mode 100644
index 0000000..0af26eb
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/modals/userCreate.html
@@ -0,0 +1,147 @@
+<!--
+* 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.
+-->
+
+<form id="create-user-form" role="form" novalidate name="form.userCreateForm">
+  <div class="modal-header">
+    <h1 class="modal-title">
+      {{'users.create' | translate}}
+    </h1>
+  </div>
+  <div class="modal-body">
+    <div class="form-group"
+         ng-class="{ 'has-error': (form.userCreateForm.userName.$error.required || form.userCreateForm.userName.$error.pattern) && form.userCreateForm.submitted }">
+      <label for="userName">
+        {{'users.username' | translate}}<span>&nbsp;*</span>&nbsp;
+        <i class="fa fa-question-circle" aria-hidden="true"></i>
+      </label>
+      <input type="text"
+             autofocus
+             placeholder="{{'users.user.name' | translate}}"
+             ng-pattern="/^[^<>&`|\\]+$/"
+             ng-maxlength="80"
+             tooltip="{{'users.userNameTip' | translate}}"
+             autocomplete="off"
+             tooltip-trigger="focus"
+             class="form-control"
+             ng-model="formData.userName"
+             name="userName"
+             id="userName"
+             ng-change="checkIfInstanceExist()"
+             required>
+      <span class="help-block validation-block"
+            ng-show='form.userCreateForm.userName.$error.required && form.userCreateForm.submitted'>
+        {{'common.alerts.fieldRequired' | translate}}
+      </span>
+      <span class="help-block validation-block"
+            ng-show='form.userCreateForm.userName.$error.pattern && form.userCreateForm.submitted'>
+        {{'common.alerts.noSpecialChars' | translate}}
+      </span>
+    </div>
+
+    <div class="row">
+      <div class="form-group col-sm-6"
+           ng-class="{ 'has-error': form.userCreateForm.password.$error.required && form.userCreateForm.submitted }">
+        <label for="password">
+          {{'users.password' | translate}}<span>&nbsp;*</span>
+        </label>
+        <input type="password"
+               id="password"
+               class="form-control"
+               name="password"
+               placeholder="{{'users.password' | translate}}"
+               required
+               ng-model="formData.password"
+               autocomplete="off">
+        <span class="help-block validation-block"
+              ng-show='form.userCreateForm.password.$error.required && form.userCreateForm.submitted'>
+          {{'common.alerts.fieldRequired' | translate}}
+        </span>
+      </div>
+      <div class="form-group col-sm-6"
+           ng-class="{ 'has-error': form.userCreateForm.confirmPassword.$error.passwordVerify || (form.userCreateForm.confirmPassword.$error.required && form.userCreateForm.submitted) }">
+        <label for="confirmPassword">
+          {{'users.confirmPassword' | translate}}<span>&nbsp;*</span>
+        </label>
+        <input type="password"
+               id="confirmPassword"
+               class="form-control"
+               name="confirmPassword"
+               placeholder="{{'users.confirmPassword' | translate}}"
+               required
+               password-verify="formData.password"
+               ng-model="formData.confirmPassword"
+               autocomplete="off">
+        <span class="help-block validation-block"
+              ng-show='form.userCreateForm.confirmPassword.$error.required && form.userCreateForm.submitted'>
+          {{'common.alerts.fieldRequired' | translate}}
+        </span>
+        <span class="help-block validation-block"
+              ng-show='form.userCreateForm.confirmPassword.$error.passwordVerify'>
+          {{'users.alerts.wrongPassword' | translate}}
+        </span>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="form-group col-sm-6"
+           ng-class="{ 'has-error': form.userCreateForm.role.$error.required && form.userCreateForm.submitted }">
+        <label for="role" class="nowrap">
+          {{'users.role' | translate}}<span>&nbsp;*</span>&nbsp;
+          <i class="fa fa-question-circle" aria-hidden="true"></i>
+        </label>
+        <select
+          class="form-control"
+          id="role"
+          name="role"
+          ng-model="formData.role"
+          required>
+          <option value="" disabled selected>{{'common.select' | translate}}</option>
+          <option ng-repeat="role in roleOptions" value="{{role.permission_id}}">{{role.permission_label}}</option>
+        </select>
+        <span class="help-block validation-block" ng-show='form.userCreateForm.role.$error.required && form.userCreateForm.submitted'>
+          {{'common.alerts.fieldRequired' | translate}}
+        </span>
+      </div>
+    </div>
+
+    <div class="form-group">
+      <label>
+        {{'users.isAmbariAdmin' | translate}}<span>&nbsp;*</span>
+        <i class="fa fa-question-circle" aria-hidden="true"></i>
+      </label>
+      <div>
+        <toggle-switch model="formData.isAdmin" on-label="{{'common.yes' | translate}}" off-label="{{'common.no' | translate}}" class="switch-primary" data-off-color="danger"></toggle-switch>
+      </div>
+    </div>
+
+    <div class="form-group">
+      <label>
+        {{'users.isActive' | translate}}<span>&nbsp;*</span>
+        <i class="fa fa-question-circle" aria-hidden="true"></i>
+      </label>
+      <div>
+        <toggle-switch model="formData.isActive" on-label="{{'users.active' | translate}}" off-label="{{'users.inactive' | translate}}" class="switch-primary" data-off-color="danger"></toggle-switch>
+      </div>
+    </div>
+
+  </div>
+  <div class="modal-footer">
+    <button class="btn btn-default" ng-click="cancel()">{{'common.controls.cancel' | translate}}</button>
+    <button class="btn btn-primary" ng-click="save()" type="submit">{{'common.controls.save' | translate}}</button>
+  </div>
+</form>

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/userEdit.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/userEdit.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/userEdit.html
new file mode 100644
index 0000000..0372a11
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/userEdit.html
@@ -0,0 +1,122 @@
+<!--
+* 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.
+-->
+    
+<div ng-show="user" class="user-edit-panel">
+  <div class="clearfix">
+    <ol class="breadcrumb pull-left">
+      <li><a href="#/userManagement">{{'common.users' | translate}}</a></li>
+      <li class="active"><span class="glyphicon glyphicon-flash" ng-show="user.admin"></span>{{user.user_name}}</li>
+    </ol>
+    <div class="pull-right top-margin-4">
+      <div ng-switch="isCurrentUser || user.user_type != 'LOCAL'">
+        <button class="btn deleteuser-btn disabled btn-default" ng-switch-when="true" tooltip="{{'common.cannotDelete' | translate: '{term: constants.user}'}}">{{'common.delete' | translate: '{term: constants.user}'}}</button>
+        <button class="btn deleteuser-btn btn-danger" ng-switch-when="false" ng-click="deleteUser()">{{'common.delete' | translate: '{term: constants.user}'}}</button>
+      </div>
+    </div>
+  </div>
+  <hr>
+  <form class="form-horizontal" role="form" >
+    <div class="form-group">
+      <label for="" class="col-sm-2 control-label">{{'common.type' | translate}}</label>
+      <div class="col-sm-10">
+        <label for="" class="control-label">{{user.userTypeName}}</label>
+      </div>
+    </div>
+    <div class="form-group">
+      <label for="" class="col-sm-2 control-label">{{'users.status' | translate}}</label>
+      <div class="col-sm-10">
+        <toggle-switch on-change="toggleUserActive()" disabled-tooltip="{{'users.alerts.cannotChange' | translate: '{term: constants.status}'}}" ng-disabled="isCurrentUser" model="user.active" on-label="{{'users.active' | translate}}" off-label="{{'users.inactive' | translate}}" class="switch-primary userstatus {{user ? '' : 'no-animation'}}" data-off-color="danger"></toggle-switch>
+      </div>
+    </div>
+    <div class="form-group">
+      <label for="" class="col-sm-2 control-label"><span class="glyphicon glyphicon-flash"></span> {{'users.ambariAdmin' | translate}}</label>
+      <div class="col-sm-10">
+        <toggle-switch on-change="toggleUserAdmin()" disabled-tooltip="{{'users.alerts.cannotChange' | translate: '{term: constants.admin}'}}" ng-disabled="isCurrentUser" model="user.admin" on-label="{{'common.yes' | translate}}" off-label="{{'common.no' | translate}}" class="switch-primary userstatus {{user ? '' : 'no-animation'}}" data-off-color="danger"></toggle-switch>
+      </div>
+    </div>
+    <div class="form-group">
+      <label for="password" class="col-sm-2 control-label">{{'users.password' | translate}}</label>
+      <div class="col-sm-10">
+        <div ng-switch="user.user_type != 'LOCAL'">
+          <button class="btn deleteuser-btn disabled btn-default" ng-switch-when="true" tooltip="{{'users.alerts.cannotChange' | translate: '{term: constants.password}'}}">{{'users.changePassword' | translate}}</button>
+          <a href ng-click="openChangePwdDialog()" ng-switch-when="false" class="btn btn-default changepassword">{{'users.changePassword' | translate}}</a>
+        </div>
+          
+      </div>
+    </div>
+    <div class="form-group">
+      <label for="groups" class="col-sm-2 control-label">{{getUserMembership(user.user_type)}}</label>
+      <div class="col-sm-10">
+        <editable-list items-source="editingGroupsList" resource-type="Group" editable="user.user_type == 'LOCAL'"></editable-list>
+      </div>
+        
+    </div>
+    <div class="form-group" >
+      <label for="" class="col-sm-2 control-label">{{'common.privileges' | translate}}</label>
+      <div class="col-sm-10">
+        <table class="table" ng-hide="hidePrivileges || user.admin">
+          <thead>
+            <tr>
+              <th>{{'common.cluster' | translate}}</th>
+              <th>{{'common.clusterRole' | translate}}</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr ng-repeat="(name, privilege) in privileges.clusters">
+              <td>
+                <span class="glyphicon glyphicon-cloud"></span> 
+                <a href="#/clusters/{{name}}/manageAccess">{{name}}</a>
+              </td>
+              <td>
+                <span tooltip="{{privilege.permission_label}}">{{privilege.permission_label}}</span>
+              </td>
+            </tr>
+            <tr>
+              <td ng-show="noClusterPriv">{{'common.alerts.noPrivileges' | translate: '{term: constants.cluster}'}}</td>
+            </tr>
+          </tbody>
+          <thead class="view-permission-header">
+            <tr>
+              <th>{{'common.view' | translate}}</th>
+              <th>{{'common.viewPermissions' | translate}}</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr ng-repeat="(name, privilege) in privileges.views">
+              <td>
+                <span class="glyphicon glyphicon-th"></span>
+                <a href="#/views/{{privilege.view_name}}/versions/{{privilege.version}}/instances/{{name}}/edit">{{name}}</a>
+              </td>
+              <td>
+                <span tooltip="{{item}}" ng-repeat="item in privilege.privileges track by $index">{{item | translate}}{{$last ? '' : ', '}}</span>
+              </td>
+              <td>
+                <i class="fa fa-trash-o" aria-hidden="true" ng-click="removePrivilege(name, privilege);"></i>
+              </td>
+            </tr>
+            <tr>
+              <td ng-show="noViewPriv">{{'common.alerts.noPrivileges' | translate: '{term: constants.view}'}}</td>
+            </tr>
+          </tbody>
+        </table>
+        <div class="alert alert-info" ng-show="!privileges && !user.admin">{{'common.alerts.noPrivilegesDescription' | translate: '{term: constants.user}'}}</div>
+        <div class="alert alert-info" ng-show="user.admin">{{'users.userIsAdmin' | translate}}</div>
+      </div>
+    </div>
+  </form>
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/usersList.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/usersList.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/usersList.html
new file mode 100644
index 0000000..cc4789b
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/usersList.html
@@ -0,0 +1,119 @@
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+-->
+
+<div class="users-pane">
+  <div class="clearfix panel">
+    <button class="btn btn-default createuser-btn pull-right" ng-click="createUser();">
+      {{'users.create' | translate}}
+    </button>
+  </div>
+  <table class="table table-striped table-hover">
+    <thead>
+      <tr class="fix-bottom">
+        <th>
+          <span>{{'users.username' | translate}}</span>
+        </th>
+        <th>
+          <span>{{'clusters.role' | translate}}</span>
+        </th>
+        <th>
+          <span>{{'users.status' | translate}}</span>
+        </th>
+        <th>
+          <span>{{'common.type' | translate}}</span>
+        </th>
+        <th>
+          <span>{{'common.group' | translate}}</span>
+        </th>
+        <th class="entity-actions">
+          <span>{{'common.actions' | translate}}</span>
+        </th>
+      </tr>
+      <tr class="fix-top">
+        <th>
+          <div class="search-container">
+            <input type="text" class="form-control namefilter" placeholder="{{'common.any' | translate}}" ng-model="filters.name" ng-change="resetPagination()">
+            <button type="button" class="close clearfilter" ng-show="filters.name" ng-click="filters.name=''; resetPagination()">
+              <span aria-hidden="true">&times;</span><span class="sr-only">{{'common.controls.close' | translate}}</span>
+            </button>
+          </div>
+        </th>
+        <th></th>
+        <th>
+          <select class="form-control statusfilter"
+                  ng-model="filters.status"
+                  ng-options="item.label for item in activeFilterOptions"
+                  ng-change="resetPagination()">
+          </select>
+        </th>
+        <th>
+          <select class="form-control typefilter"
+                  ng-model="filters.type"
+                  ng-options="item.label for item in typeFilterOptions"
+                  ng-change="resetPagination()">
+          </select>
+        </th>
+        <th></th>
+        <th></th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr ng-repeat="user in users">
+        <td>
+          <span>{{user.Users.user_name}}</span>
+        </td>
+        <td>
+          <span>{{user.Users.role}}</span>
+        </td>
+        <td>
+          <span>
+            {{(user.Users.active ? 'users.active' : 'users.inactive') | translate}}
+          </span>
+        </td>
+        <td><span>{{user.Users.userTypeName}}</span></td>
+        <td><span>{{user.Users.groups.length ? user.Users.groups.join(' ') : '-'}}</span></td>
+        <td class="entity-actions">
+          <a href="#/users/{{user.Users.encodedName}}/edit">
+            <i class="fa fa-pencil"></i>
+          </a>
+          <a href ng-click="deleteUser(user.Users)">
+            <i class="fa fa-trash-o"></i>
+          </a>
+        </td>
+      </tr>
+    </tbody>
+  </table>
+  <div ng-if="isLoading" class="spinner-container">
+    <i class="fa fa-2x fa-spinner fa-spin" aria-hidden="true"></i>
+  </div>
+  <div class="alert empty-table-alert col-sm-12" ng-show="!users.length && !isLoading">
+    {{'common.alerts.nothingToDisplay' | translate: '{term: constants.users}'}}
+  </div>
+  <div class="col-sm-12 table-bar" ng-show="totalUsers > minRowsToShowPagination">
+    <div class="pull-left filtered-info">
+      <span>{{'common.filterInfo' | translate: '{showed: tableInfo.showed, total: tableInfo.total, term: constants.users}'}}</span>
+      <span ng-show="isNotEmptyFilter">- <a href ng-click="clearFilters()">{{'common.controls.clearFilters' | translate}}</a></span>
+    </div>
+    <div class="pull-right left-margin">
+      <pagination class="paginator" total-items="totalUsers" max-size="maxVisiblePages" items-per-page="usersPerPage" ng-model="currentPage" ng-change="pageChanged()"></pagination>
+    </div>
+    <div class="pull-right">
+      <select class="form-control" ng-model="usersPerPage" ng-change="usersPerPageChanges()" ng-options="currOption for currOption in [10, 25, 50, 100]"></select>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/users/create.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/users/create.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/users/create.html
deleted file mode 100644
index 80a3b04..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/views/users/create.html
+++ /dev/null
@@ -1,82 +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.
--->
-<ol class="breadcrumb">
-  <li><a href="#/users">{{'common.users' | translate}}</a></li>
-  <li class="active">{{'users.create' | translate}}</li>
-</ol>
-<hr>
-<form class="form-horizontal create-user-form" role="form" novalidate name="form" autocomplete="off">
-  <div class="form-group" ng-class="{'has-error' : form.user_name.$error.required && form.submitted}">
-    <label for="username" class="col-sm-2 control-label">{{'users.username' | translate}}</label>
-    <div class="col-sm-10"
-         ng-class="{'has-error': form.user_name.$error.pattern}">
-      <input
-        autofocus
-        type="text"
-        id="username"
-        class="form-control username-input"
-        name="user_name"
-        placeholder="{{'users.userName' | translate}}"
-        ng-model="user.user_name"
-        ng-required="true"
-        ng-pattern="/^[^<>&`|\\]+$/"
-        ng-maxlength="80"
-        tooltip="{{'users.userNameTip' | translate}}"
-        autocomplete="off"
-        tooltip-trigger="focus">
-      <div class="alert alert-danger top-margin" ng-show="form.user_name.$error.required && form.submitted">{{'common.alerts.fieldIsRequired' | translate}}</div>
-    </div>
-  </div>
-  <div class="form-group">
-    <label for="" class="col-sm-2 control-label">{{'common.type' | translate}}</label>
-    <div class="col-sm-10">
-      <label for="" class="control-label">{{'common.local' | translate}}</label>
-    </div>
-  </div>
-  <div class="form-group">
-    <label for="" class="col-sm-2 control-label">{{'users.status' | translate}}</label>
-    <div class="col-sm-10">
-      <toggle-switch model="user.active" on-label="{{'users.active' | translate}}" off-label="{{'users.inactive' | translate}}" class="switch-primary userstatus" data-off-color="danger"></toggle-switch>
-    </div>
-  </div>
-  <div class="form-group">
-    <label for="" class="col-sm-2 control-label"><span class="glyphicon glyphicon-flash"></span>{{'users.ambariAdmin' | translate}}</label>
-    <div class="col-sm-10">
-      <toggle-switch ng-disabled="isCurrentUser" model="user.admin" on-label="{{'common.yes' | translate}}" off-label="{{'common.no' | translate}}" class="switch-primary userstatus" data-off-color="danger"></toggle-switch>
-    </div>
-    </div>
-  <div class="form-group" ng-class="{'has-error' : (form.password.$error.required && form.submitted) || form.confirmPassword.$error.passwordVerify}">
-    <label for="password" class="col-sm-2 control-label">{{'users.password' | translate}}</label>
-    <div class="col-sm-10">
-      <input type="password" class="form-control bottom-margin userpassword" name="password" placeholder="{{'users.password' | translate}}" required ng-model="user.password" autocomplete="off">
-      <input type="password" class="form-control bottom-margin userpasswordconfirm" name="confirmPassword" placeholder="{{'users.passwordConfirmation' | translate}}" required ng-model="user.passwordConfirmation"
-        password-verify="user.password" autocomplete="off">
-
-      <div class="alert alert-danger" ng-show='form.confirmPassword.$error.passwordVerify'>{{'users.alerts.wrongPassword' | translate}}</div>
-      <div class="alert alert-danger" ng-show='form.password.$error.required && form.submitted'>{{'users.alerts.passwordRequired' | translate}}</div>
-      
-    </div>
-  </div>
-  <div class="form-group">
-    <div class="col-sm-offset-2 col-sm-10">
-      <button class="btn btn-primary pull-right left-margin saveuser" ng-click="createUser()">{{'common.controls.save' | translate}}</button>
-      <a class="btn btn-default pull-right cancel" href ng-click="cancel()">{{'common.controls.cancel' | translate}}</a>
-    </div>
-  </div>
-      
-</form>

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/users/list.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/users/list.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/users/list.html
deleted file mode 100644
index 12227c3..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/views/users/list.html
+++ /dev/null
@@ -1,97 +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.
--->
-
-<div class="users-pane">
-  <div class="clearfix">
-    <ol class="breadcrumb pull-left">
-      <li class="active">{{'common.users' | translate}}</li>
-    </ol>
-    <div class="pull-right top-margin-4">
-      <link-to route="users.create" class="btn btn-default createuser-btn">
-        </span> {{'users.create' | translate}}
-      </link-to>
-    </div>
-  </div>
-  <table class="table table-striped table-hover">
-    <thead>
-      <tr>
-        <th width="30">
-          <span class="bottom-margin admin-filter glyphicon glyphicon-flash" 
-            ng-class="{'no-filter' : !adminFilter}" 
-            ng-click="toggleAdminFilter()"
-            tooltip="{{(adminFilter ? 'users.showAll' : 'users.showAdmin') | translate}}"
-          ></span>
-        </th>
-        <th>
-          <div class="search-container">
-            <label for="">{{'users.username' | translate}}</label>
-            <input type="text" class="form-control namefilter" placeholder="{{'common.any' | translate}}" ng-model="currentNameFilter" ng-change="resetPagination()">
-            <button type="button" class="close clearfilter" ng-show="currentNameFilter" ng-click="currentNameFilter=''; resetPagination()"><span aria-hidden="true">&times;</span><span class="sr-only">{{'common.controls.close' | translate}}</span></button>
-          </div>
-        </th>
-        <th>
-          <label for="">{{'common.type' | translate}}</label>
-          <select class="form-control typefilter"
-            ng-model="currentTypeFilter"
-            ng-options="item.label for item in typeFilterOptions"
-            ng-change="resetPagination()">
-          </select>
-
-        </th>
-        <th>
-          <label for="">{{'users.status' | translate}}</label>
-          <select class="form-control statusfilter" 
-            ng-model="currentActiveFilter"
-            ng-options="item.label for item in activeFilterOptions"
-            ng-change="resetPagination()">
-          </select>
-        </th>
-      </tr>
-    </thead>
-    <tbody>
-      <tr ng-repeat="user in users">
-        <td>
-          <span class="glyphicon" tooltip="{{user.Users.admin ? constants.admin : ''}}" ng-class="{'glyphicon-flash' : user.Users.admin}"></span>
-        </td>
-        <td>
-           <a href="#/users/{{user.Users.encoded_name}}">{{user.Users.user_name}}</a>
-        </td>
-        <td>{{user.Users.userTypeName}}</td>
-        <td><span ng-class="user.Users.active ? 'text-success' : 'text-danger'">{{(user.Users.active ? 'users.active' : 'users.inactive') | translate}}</span></td>
-      </tr>
-    </tbody>
-  </table>
-  <div ng-if="isLoading" class="spinner-container">
-    <i class="fa fa-2x fa-spinner fa-spin" aria-hidden="true"></i>
-  </div>
-  <div class="alert empty-table-alert col-sm-12" ng-show="!users.length && !isLoading">
-    {{'common.alerts.nothingToDisplay' | translate: '{term: constants.users}'}}
-  </div>
-  <div class="col-sm-12 table-bar">
-    <div class="pull-left filtered-info">
-      <span>{{'common.filterInfo' | translate: '{showed: tableInfo.showed, total: tableInfo.total, term: constants.users}'}}</span>
-      <span ng-show="isNotEmptyFilter">- <a href ng-click="clearFilters()">{{'common.controls.clearFilters' | translate}}</a></span>
-    </div>
-    <div class="pull-right left-margin">
-      <pagination class="paginator" total-items="totalUsers" max-size="maxVisiblePages" items-per-page="usersPerPage" ng-model="currentPage" ng-change="pageChanged()"></pagination>
-    </div>
-    <div class="pull-right">
-      <select class="form-control" ng-model="usersPerPage" ng-change="usersPerPageChanges()" ng-options="currOption for currOption in [10, 25, 50, 100]"></select>
-    </div>
-  </div>
-</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/users/modals/changePassword.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/users/modals/changePassword.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/users/modals/changePassword.html
deleted file mode 100644
index f29d315..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/views/users/modals/changePassword.html
+++ /dev/null
@@ -1,46 +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.
--->
-<div class="modal-header">
-  <h3 class="modal-title">{{'users.changePasswordFor' | translate: '{userName: userName}'}}</h3>
-</div>
-<div class="modal-body">
-  <form class="form-horizontal" novalidate name="form.passwordChangeForm" role="form" >
-    <div class="form-group" ng-class="{'has-error' : (form.passwordChangeForm.currentPassword.$error.required && form.passwordChangeForm.submitted)}">
-      <label for="" class="col-sm-4 control-label" >{{'users.yourPassword' | translate}}</label>
-      <div class="col-sm-8">
-        <input type="password" name="currentPassword" class="form-control bottom-margin" placeholder="{{'users.yourPassword' | translate}}" required ng-model="passwordData.currentUserPassword" autocomplete="off">
-        <div class="alert alert-danger no-margin-bottom" ng-show='form.passwordChangeForm.password.$error.required && form.passwordChangeForm.submitted'>{{'users.alerts.passwordRequired' | translate}}</div>
-      </div>
-    </div>
-    <div class="form-group no-margin-bottom" ng-class="{'has-error' : (form.passwordChangeForm.password.$error.required && form.passwordChangeForm.submitted) || form.passwordChangeForm.confirmPassword.$error.passwordVerify}">
-      <label for="" class="col-sm-4 control-label">{{'users.newPassword' | translate}}:</label>
-      <div class="col-sm-8">
-        <input type="password" class="form-control bottom-margin" name="password" placeholder="{{'users.newPassword' | translate}}" required ng-model="passwordData.password" autocomplete="off">
-        <input type="password" class="form-control bottom-margin" name="confirmPassword" placeholder="{{'users.newPasswordConfirmation' | translate}}" required ng-model="passwordData.passwordConfirmation"
-          password-verify="passwordData.password" autocomplete="off">
-        <div class="alert alert-danger no-margin-bottom" ng-show='form.passwordChangeForm.confirmPassword.$error.passwordVerify'>{{'users.alerts.wrongPassword' | translate}}</div>
-        <div class="alert alert-danger no-margin-bottom" ng-show='form.passwordChangeForm.password.$error.required && form.passwordChangeForm.submitted'>{{'users.alerts.passwordRequired' | translate}}</div>
-      </div>
-
-    </div>
-  </form>
-</div>
-<div class="modal-footer">
-  <button class="btn btn-default" ng-click="cancel()">{{'common.controls.cancel' | translate}}</button>
-  <button class="btn btn-primary" ng-click="ok()">{{'common.controls.ok' | translate}}</button>
-</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/users/show.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/users/show.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/users/show.html
deleted file mode 100644
index f965c5d..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/views/users/show.html
+++ /dev/null
@@ -1,122 +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.
--->
-    
-<div ng-show="user" class="user-edit-panel">
-  <div class="clearfix">
-    <ol class="breadcrumb pull-left">
-      <li><a href="#/users">{{'common.users' | translate}}</a></li>
-      <li class="active"><span class="glyphicon glyphicon-flash" ng-show="user.admin"></span>{{user.user_name}}</li>
-    </ol>
-    <div class="pull-right top-margin-4">
-      <div ng-switch="isCurrentUser || user.user_type != 'LOCAL'">
-        <button class="btn deleteuser-btn disabled btn-default" ng-switch-when="true" tooltip="{{'common.cannotDelete' | translate: '{term: constants.user}'}}">{{'common.delete' | translate: '{term: constants.user}'}}</button>
-        <button class="btn deleteuser-btn btn-danger" ng-switch-when="false" ng-click="deleteUser()">{{'common.delete' | translate: '{term: constants.user}'}}</button>
-      </div>
-    </div>
-  </div>
-  <hr>
-  <form class="form-horizontal" role="form" >
-    <div class="form-group">
-      <label for="" class="col-sm-2 control-label">{{'common.type' | translate}}</label>
-      <div class="col-sm-10">
-        <label for="" class="control-label">{{user.userTypeName}}</label>
-      </div>
-    </div>
-    <div class="form-group">
-      <label for="" class="col-sm-2 control-label">{{'users.status' | translate}}</label>
-      <div class="col-sm-10">
-        <toggle-switch on-change="toggleUserActive()" disabled-tooltip="{{'users.alerts.cannotChange' | translate: '{term: constants.status}'}}" ng-disabled="isCurrentUser" model="user.active" on-label="{{'users.active' | translate}}" off-label="{{'users.inactive' | translate}}" class="switch-primary userstatus {{user ? '' : 'no-animation'}}" data-off-color="danger"></toggle-switch>
-      </div>
-    </div>
-    <div class="form-group">
-      <label for="" class="col-sm-2 control-label"><span class="glyphicon glyphicon-flash"></span> {{'users.ambariAdmin' | translate}}</label>
-      <div class="col-sm-10">
-        <toggle-switch on-change="toggleUserAdmin()" disabled-tooltip="{{'users.alerts.cannotChange' | translate: '{term: constants.admin}'}}" ng-disabled="isCurrentUser" model="user.admin" on-label="{{'common.yes' | translate}}" off-label="{{'common.no' | translate}}" class="switch-primary userstatus {{user ? '' : 'no-animation'}}" data-off-color="danger"></toggle-switch>
-      </div>
-    </div>
-    <div class="form-group">
-      <label for="password" class="col-sm-2 control-label">{{'users.password' | translate}}</label>
-      <div class="col-sm-10">
-        <div ng-switch="user.user_type != 'LOCAL'">
-          <button class="btn deleteuser-btn disabled btn-default" ng-switch-when="true" tooltip="{{'users.alerts.cannotChange' | translate: '{term: constants.password}'}}">{{'users.changePassword' | translate}}</button>
-          <a href ng-click="openChangePwdDialog()" ng-switch-when="false" class="btn btn-default changepassword">{{'users.changePassword' | translate}}</a>
-        </div>
-          
-      </div>
-    </div>
-    <div class="form-group">
-      <label for="groups" class="col-sm-2 control-label">{{getUserMembership(user.user_type)}}</label>
-      <div class="col-sm-10">
-        <editable-list items-source="editingGroupsList" resource-type="Group" editable="user.user_type == 'LOCAL'"></editable-list>
-      </div>
-        
-    </div>
-    <div class="form-group" >
-      <label for="" class="col-sm-2 control-label">{{'common.privileges' | translate}}</label>
-      <div class="col-sm-10">
-        <table class="table" ng-hide="hidePrivileges || user.admin">
-          <thead>
-            <tr>
-              <th>{{'common.cluster' | translate}}</th>
-              <th>{{'common.clusterRole' | translate}}</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr ng-repeat="(name, privilege) in privileges.clusters">
-              <td>
-                <span class="glyphicon glyphicon-cloud"></span> 
-                <a href="#/clusters/{{name}}/manageAccess">{{name}}</a>
-              </td>
-              <td>
-                <span tooltip="{{privilege.permission_label}}">{{privilege.permission_label}}</span>
-              </td>
-            </tr>
-            <tr>
-              <td ng-show="noClusterPriv">{{'common.alerts.noPrivileges' | translate: '{term: constants.cluster}'}}</td>
-            </tr>
-          </tbody>
-          <thead class="view-permission-header">
-            <tr>
-              <th>{{'common.view' | translate}}</th>
-              <th>{{'common.viewPermissions' | translate}}</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr ng-repeat="(name, privilege) in privileges.views">
-              <td>
-                <span class="glyphicon glyphicon-th"></span>
-                <a href="#/views/{{privilege.view_name}}/versions/{{privilege.version}}/instances/{{name}}/edit">{{name}}</a>
-              </td>
-              <td>
-                <span tooltip="{{item}}" ng-repeat="item in privilege.privileges track by $index">{{item | translate}}{{$last ? '' : ', '}}</span>
-              </td>
-              <td>
-                <i class="fa fa-trash-o" aria-hidden="true" ng-click="removePrivilege(name, privilege);"></i>
-              </td>
-            </tr>
-            <tr>
-              <td ng-show="noViewPriv">{{'common.alerts.noPrivileges' | translate: '{term: constants.view}'}}</td>
-            </tr>
-          </tbody>
-        </table>
-        <div class="alert alert-info" ng-show="!privileges && !user.admin">{{'common.alerts.noPrivilegesDescription' | translate: '{term: constants.user}'}}</div>
-        <div class="alert alert-info" ng-show="user.admin">{{'users.userIsAdmin' | translate}}</div>
-      </div>
-    </div>
-  </form>
-</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/clusters/UserAccessListCtrl_test.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/clusters/UserAccessListCtrl_test.js b/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/clusters/UserAccessListCtrl_test.js
deleted file mode 100644
index 14c0975..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/clusters/UserAccessListCtrl_test.js
+++ /dev/null
@@ -1,820 +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.
- */
-
-describe('#Cluster', function () {
-
-  describe('UserAccessListCtrl', function() {
-
-    var scope, ctrl, $t, $httpBackend, Cluster, deferred, Alert, mock;
-
-    beforeEach(module('ambariAdminConsole', function () {}));
-
-    beforeEach(inject(function($rootScope, $controller, _$translate_, _$httpBackend_, _Cluster_, _$q_, _Alert_) {
-      scope = $rootScope.$new();
-      $t = _$translate_.instant;
-      $httpBackend = _$httpBackend_;
-      Cluster = _Cluster_;
-      Alert = _Alert_;
-      deferred = {
-        createPrivileges: _$q_.defer(),
-        getPrivilegesForResource: _$q_.defer(),
-        getPrivilegesWithFilters: _$q_.defer(),
-        deletePrivileges: _$q_.defer(),
-        deleteMultiplePrivileges: _$q_.defer()
-      };
-      ctrl = $controller('UserAccessListCtrl', {
-        $scope: scope
-      });
-      mock = {
-        Cluster: Cluster,
-        Alert: Alert,
-        scope: scope
-      };
-      spyOn(Cluster, 'createPrivileges').andReturn(deferred.createPrivileges.promise);
-      spyOn(Cluster, 'deletePrivileges').andReturn(deferred.deletePrivileges.promise);
-      spyOn(Cluster, 'getPrivilegesForResource').andReturn(deferred.getPrivilegesForResource.promise);
-      spyOn(Cluster, 'getPrivilegesWithFilters').andReturn(deferred.getPrivilegesWithFilters.promise);
-      spyOn(Alert, 'success').andCallFake(angular.noop);
-      spyOn(Alert, 'error').andCallFake(angular.noop);
-      spyOn(scope, 'loadRoles').andCallFake(angular.noop);
-
-      $httpBackend.expectGET(/\/api\/v1\/permissions/).respond(200, {
-        items: []
-      });
-      $httpBackend.expectGET(/\/api\/v1\/users?.*/).respond(200, {
-        items:[]
-      });
-      $httpBackend.flush();
-    }));
-
-    describe('#clearFilters()', function () {
-
-      it('should clear filters and reset pagination', function () {
-        scope.currentPage = 2;
-        scope.currentNameFilter = 'a';
-        scope.roleFilterOptions = [
-          {
-            label: $t('common.all'),
-            value: ''
-          },
-          {
-            label: $t('users.roles.clusterUser'),
-            value: 'CLUSTER.USER'
-          }
-        ];
-        scope.currentRoleFilter = scope.roleFilterOptions[1];
-        scope.clearFilters();
-        expect(scope.currentNameFilter).toEqual('');
-        expect(scope.currentRoleFilter).toEqual({
-          label: $t('common.all'),
-          value: ''
-        });
-        expect(scope.currentPage).toEqual(1);
-      });
-
-    });
-
-    describe('#isNotEmptyFilter', function () {
-
-      var cases = [
-        {
-          currentNameFilter: '',
-          currentRoleFilter: null,
-          isNotEmptyFilter: false,
-          title: 'no filters'
-        },
-        {
-          currentNameFilter: '',
-          currentRoleFilter: {
-            value: ''
-          },
-          isNotEmptyFilter: false,
-          title: 'empty filters'
-        },
-        {
-          currentNameFilter: 'a',
-          currentRoleFilter: {
-            value: ''
-          },
-          isNotEmptyFilter: true,
-          title: 'name filter'
-        },
-        {
-          currentNameFilter: '0',
-          currentRoleFilter: {
-            value: ''
-          },
-          isNotEmptyFilter: true,
-          title: 'name filter with "0" as string'
-        },
-        {
-          currentNameFilter: '',
-          currentRoleFilter: {
-            value: 'CLUSTER.USER'
-          },
-          isNotEmptyFilter: true,
-          title: 'role filter'
-        },
-        {
-          currentNameFilter: 'a',
-          currentRoleFilter: {
-            value: 'GROUP'
-          },
-          isNotEmptyFilter: true,
-          title: 'all filters'
-        },
-        {
-          currentNameFilter: '0',
-          currentRoleFilter: {
-            value: 'GROUP'
-          },
-          isNotEmptyFilter: true,
-          title: 'all filters with "0" as string'
-        }
-      ];
-
-      cases.forEach(function (item) {
-        it(item.title, function () {
-          scope.currentNameFilter = item.currentNameFilter;
-          scope.currentRoleFilter = item.currentRoleFilter;
-          scope.$digest();
-          expect(scope.isNotEmptyFilter).toEqual(item.isNotEmptyFilter);
-        });
-      });
-
-    });
-
-    describe('#save() for Users', function(){
-      var user = {};
-
-      beforeEach(function() {
-        items1 = {
-            "href" : "http://abc.com:8080/api/v1/users/user1",
-            "Users" : { "user_name" : "user1" },
-            "privileges" : [
-              {
-                "href" : "http://abc.com:8080/api/v1/users/user1/privileges/222",
-                "PrivilegeInfo" : {
-                  "cluster_name" : "myCluster",
-                  "permission_label" : "Service Administrator",
-                  "permission_name" : "SERVICE.ADMINISTRATOR",
-                  "principal_name" : "mygroup2",
-                  "principal_type" : "GROUP",
-                  "privilege_id" : 222,
-                  "type" : "CLUSTER",
-                  "user_name" : "user1"
-                }
-              }, {
-                "href" : "http://abc.com:8080/api/v1/users/user1/privileges/111",
-                "PrivilegeInfo" : {
-                  "cluster_name" : "myCluster",
-                  "permission_label" : "Service Administrator",
-                  "permission_name" : "SERVICE.ADMINISTRATOR",
-                  "principal_name" : "mygroup",
-                  "principal_type" : "GROUP",
-                  "privilege_id" : 111,
-                  "type" : "CLUSTER",
-                  "user_name" : "user1"
-                }
-              }, {
-                "href" : "http://abc.com:8080/api/v1/users/user1/privileges/11",
-                "PrivilegeInfo" : {
-                  "cluster_name" : "myCluster",
-                  "permission_label" : "Cluster Administrator",
-                  "permission_name" : "CLUSTER.ADMINISTRATOR",
-                  "principal_name" : "user1",
-                  "principal_type" : "USER",
-                  "privilege_id" : 11,
-                  "type" : "CLUSTER",
-                  "user_name" : "user1"
-                }
-              }
-            ]
-          };
-
-        items2 =
-          {
-            "href" : "http://abc.com:8080/api/v1/users/user2",
-            "Users" : { "user_name" : "user2" },
-            "privileges" : [
-              {
-                "href" : "http://abc.com:8080/api/v1/users/user2/privileges/111",
-                "PrivilegeInfo" : {
-                  "cluster_name" : "myCluster",
-                  "permission_label" : "Service Administrator",
-                  "permission_name" : "SERVICE.ADMINISTRATOR",
-                  "principal_name" : "mygroup",
-                  "principal_type" : "GROUP",
-                  "privilege_id" : 111,
-                  "type" : "CLUSTER",
-                  "user_name" : "user2"
-                }
-              }, {
-                "href" : "http://abc.com:8080/api/v1/users/user2/privileges/22",
-                "PrivilegeInfo" : {
-                  "cluster_name" : "myCluster",
-                  "permission_label" : "Service Administrator",
-                  "permission_name" : "SERVICE.ADMINISTRATOR",
-                  "principal_name" : "user2",
-                  "principal_type" : "USER",
-                  "privilege_id" : 22,
-                  "type" : "CLUSTER",
-                  "user_name" : "user2"
-                }
-              }
-            ]
-          };
-
-        all_items = { "items": [items1, items2]};
-
-        scope.loadUsers();
-
-        spyOn(Cluster, 'deleteMultiplePrivileges').andCallFake(function(clusterId, privilege_ids) {
-          privilege_ids.forEach(function(privilege_id) {
-            items1.privileges.forEach(function(p, index) {
-              if (p.PrivilegeInfo.privilege_id === privilege_id) {
-                //Remove from array
-                items1.privileges.splice(index, 1);
-              }
-            });
-          });
-        });
-        spyOn(scope, 'addPrivilege').andCallFake(function(user) {
-          var p = {};
-          p.PrivilegeInfo = {};
-          p.PrivilegeInfo.privilege_id = user.privilege_id + 1;
-          p.PrivilegeInfo.permission_name = user.permission_name;
-          p.PrivilegeInfo.principal_type = 'USER';
-
-          items1.privileges.push(p);
-          scope.loadUsers();
-        });
-
-        deferred.getPrivilegesWithFilters.resolve(all_items);
-        deferred.getPrivilegesForResource.promise.then(function(data) {
-          var arrayOfPrivileges = data.items[0].privileges;
-          var privilegesOfTypeUser = [];
-          var privilegesOfTypeGroup = [];
-          for (var i = 0; i < arrayOfPrivileges.length; i++) {
-            if(arrayOfPrivileges[i].PrivilegeInfo.principal_type === "GROUP"){
-              privilegesOfTypeGroup.push(arrayOfPrivileges[i]);
-            } else {
-              privilegesOfTypeUser.push(arrayOfPrivileges[i].PrivilegeInfo);
-            }
-          }
-
-          var effectivePrivilege = scope.pickEffectivePrivilege(arrayOfPrivileges);
-          var effectivePrivilegeFromGroups = scope.pickEffectivePrivilege(privilegesOfTypeGroup);
-          user.principal_type = 'USER';
-          user.original_perm = effectivePrivilege.permission_name;
-          user.editable = (Cluster.ineditableRoles.indexOf(effectivePrivilege.permission_name) === -1);
-
-          //add a new privilege of type USER only if it is also the effective privilege considering the user's Group privileges
-          var curIndex = scope.getRoleRank(user.permission_name);
-          var prevIndex = -1;
-          if (privilegesOfTypeGroup.length !== 0) {
-            prevIndex = scope.getRoleRank(effectivePrivilegeFromGroups.permission_name);
-          }
-          if ((curIndex === 6) || (curIndex <= prevIndex)) {
-            var privilege_ids = [];
-            privilegesOfTypeUser.forEach(function(privilegeOfTypeUser) {
-              privilege_ids.push(privilegeOfTypeUser.privilege_id);
-            });
-
-            //delete all privileges of type USER, if they exist
-            //then add the privilege for the user, after which the user displays the effective privilege
-            if(privilege_ids.length !== 0) {
-              Cluster.deleteMultiplePrivileges(
-                123,
-                privilege_ids
-              );
-            }
-            scope.addPrivilege(user);
-          } else {
-            Alert.error($t('common.alerts.cannotSavePermissions'), "User's effective privilege through its Group(s) is higher than your selected privilege.");
-            scope.loadUsers();
-          }
-        });
-
-        scope.$apply();
-      });
-
-      it('Should save the Privilege equal to the user\'s group privileges. Should also remove any individual user privileges', function() {
-        //using 'user1' for updating the new privilege
-        user.principal_name = scope.users[0].principal_name;
-        user.principal_type = scope.users[0].principal_type;
-        user.privilege_id = scope.users[0].privilege_id;
-        user.permission_name = "SERVICE.ADMINISTRATOR";
-
-        deferred.getPrivilegesForResource.resolve({ "items" : [items1] });
-
-        scope.$apply();
-
-        expect(scope.users[0].permission_name).toEqual(user.permission_name);
-        expect(scope.users[0].privilege_id).toEqual(user.privilege_id + 1);
-
-        var oldPrivilege = {
-          "href" : "http://abc.com:8080/api/v1/users/user1/privileges/11",
-          "PrivilegeInfo" : {
-            "cluster_name" : "myCluster",
-            "permission_label" : "Cluster Administrator",
-            "permission_name" : "CLUSTER.ADMINISTRATOR",
-            "principal_name" : "user1",
-            "principal_type" : "USER",
-            "privilege_id" : 11,
-            "type" : "CLUSTER",
-            "user_name" : "user1"
-          }
-        };
-        var newPrivilege = {
-          "PrivilegeInfo" : {
-            "permission_name" : user.permission_name,
-            "privilege_id" : user.privilege_id+1,
-            "principal_type" : "USER",
-            "principal_name" : "user1",
-            "encoded_name" : "user1",
-            "original_perm" : user.permission_name,
-            "url" : "users/user1",
-            "editable" : true
-          }
-        };
-
-        //test if the individual user privilege CLUSTER.ADMINISTRATOR is removed from 'items1' by deletePrivilege()
-        expect(items1.privileges).toNotContain(oldPrivilege);
-
-        //test if the new privilege got added to 'items1' by addPrivilege()
-        expect(items1.privileges).toContain(newPrivilege);
-      });
-
-      it('Should save the Privilege greater than the user\'s group privileges. Should also remove any individual user privileges', function() {
-        //using 'user1' for updating the new privilege
-        user.principal_name = scope.users[0].principal_name;
-        user.principal_type = scope.users[0].principal_type;
-        user.privilege_id = scope.users[0].privilege_id;
-        user.permission_name = "CLUSTER.OPERATOR";
-
-        deferred.getPrivilegesForResource.resolve({ "items" : [items1] });
-        scope.$apply();
-
-        expect(scope.users[0].permission_name).toEqual(user.permission_name);
-        expect(scope.users[0].privilege_id).toEqual(user.privilege_id + 1);
-
-        var oldPrivilege = {
-          "href" : "http://abc.com:8080/api/v1/users/user1/privileges/11",
-          "PrivilegeInfo" : {
-            "cluster_name" : "myCluster",
-            "permission_label" : "Cluster Administrator",
-            "permission_name" : "CLUSTER.ADMINISTRATOR",
-            "principal_name" : "user1",
-            "principal_type" : "USER",
-            "privilege_id" : 11,
-            "type" : "CLUSTER",
-            "user_name" : "user1"
-          }
-        };
-        var newPrivilege = {
-          "PrivilegeInfo" : {
-            "permission_name" : user.permission_name,
-            "principal_name" : "user1",
-            "principal_type" : "USER",
-            "privilege_id" : user.privilege_id + 1,
-            "encoded_name" : "user1",
-            "original_perm" : user.permission_name,
-            "url" : "users/user1",
-            "editable" : true
-          }
-        };
-
-        //test if the individual user privilege CLUSTER.ADMINISTRATOR is removed from 'items1' by deletePrivilege()
-        expect(items1.privileges).toNotContain(oldPrivilege);
-
-        //test if the new privilege got added to 'items1' by addPrivilege()
-        expect(items1.privileges).toContain(newPrivilege);
-      });
-
-      it('Should NOT save the Privilege smaller than the user\'s group privileges. Should keep the user\'s original privileges intact', function() {
-        //using 'user1' for updating the new privilege
-        user.principal_name = scope.users[0].principal_name;
-        user.principal_type = scope.users[0].principal_type;
-        user.privilege_id = scope.users[0].privilege_id;
-        user.permission_name = "CLUSTER.USER";
-
-        deferred.getPrivilegesForResource.resolve({ "items" : [items1] });
-        scope.$apply();
-
-        expect(scope.users[0].permission_name).toEqual(user.original_perm);
-        expect(scope.users[0].privilege_id).toEqual(user.privilege_id);
-
-        var oldPrivilege = {
-          "href" : "http://abc.com:8080/api/v1/users/user1/privileges/11",
-          "PrivilegeInfo" : {
-            "cluster_name" : "myCluster",
-            "permission_label" : "Cluster Administrator",
-            "permission_name" : "CLUSTER.ADMINISTRATOR",
-            "principal_name" : "user1",
-            "principal_type" : "USER",
-            "privilege_id" : 11,
-            "type" : "CLUSTER",
-            "user_name" : "user1",
-            "encoded_name" : "user1",
-            "original_perm" : "CLUSTER.ADMINISTRATOR",
-            "url" : "users/user1",
-            "editable" : true
-          }
-        };
-        var newPrivilege = {
-          "PrivilegeInfo" : {
-            "permission_name" : user.permission_name,
-            "principal_name" : "user1",
-            "principal_type" : "USER",
-            "privilege_id" : user.privilege_id+1,
-            "encoded_name" : "user1",
-            "original_perm" : user.permission_name,
-            "url" : "users/user1",
-            "editable" : true
-          }
-        };
-
-        //test if the individual user privilege CLUSTER.ADMINISTRATOR is NOT removed from 'items1'
-        expect(items1.privileges).toContain(oldPrivilege);
-
-        //test if the new privilege is NOT added to 'items1'
-        expect(items1.privileges).toNotContain(newPrivilege);
-      });
-
-    });
-
-    describe('#save() for Groups', function() {
-      var user = {};
-
-      beforeEach(function() {
-        items1 = {
-          "href" : "http://abc.com:8080/api/v1/groups/mygroup",
-          "Groups" : { "group_name" : "mygroup" },
-          "privileges" : [
-            {
-              "href" : "http://abc.com:8080/api/v1/groups/mygroup/privileges/3359",
-              "PrivilegeInfo" : {
-                "cluster_name" : "myCluster",
-                "group_name" : "mygroup",
-                "permission_label" : "Service Administrator",
-                "permission_name" : "SERVICE.ADMINISTRATOR",
-                "principal_name" : "mygroup",
-                "principal_type" : "GROUP",
-                "privilege_id" : 3359,
-                "type" : "CLUSTER"
-              }
-            }
-          ]
-        };
-
-        items2 = {
-          "href" : "http://abc.com:8080/api/v1/groups/mygroup2",
-          "Groups" : { "group_name" : "mygroup2" },
-          "privileges" : [
-            {
-              "href" : "http://abc.com:8080/api/v1/groups/mygroup2/privileges/3356",
-              "PrivilegeInfo" : {
-                "cluster_name" : "myCluster",
-                "group_name" : "mygroup2",
-                "permission_label" : "Service Administrator",
-                "permission_name" : "SERVICE.ADMINISTRATOR",
-                "principal_name" : "mygroup2",
-                "principal_type" : "GROUP",
-                "privilege_id" : 3356,
-                "type" : "CLUSTER"
-              }
-            }
-          ]
-        };
-
-        all_items = { "items": [items1, items2]};
-
-        scope.loadUsers();
-
-        spyOn(Cluster, 'deleteMultiplePrivileges').andCallFake(function(clusterId, privilege_ids) {
-          privilege_ids.forEach(function(privilege_id) {
-            items1.privileges.forEach(function(p, index) {
-              if (p.PrivilegeInfo.privilege_id === privilege_id) {
-                //Remove from array
-                items1.privileges.splice(index, 1);
-              }
-            });
-          });
-        });
-        spyOn(scope, 'addPrivilege').andCallFake(function(user) {
-          var p = {};
-          p.PrivilegeInfo = {};
-          p.PrivilegeInfo.privilege_id = user.privilege_id + 1;
-          p.PrivilegeInfo.permission_name = user.permission_name;
-          p.PrivilegeInfo.principal_type = 'GROUP';
-
-          items1.privileges.push(p);
-          scope.loadUsers();
-        });
-
-        deferred.getPrivilegesWithFilters.resolve(all_items);
-        deferred.getPrivilegesForResource.promise.then(function(data) {
-          var arrayOfPrivileges = data.items[0].privileges;
-          var privilegesOfTypeGroup = [];
-          var privilege = scope.pickEffectivePrivilege(arrayOfPrivileges);
-          user.principal_type = 'GROUP';
-          user.original_perm = privilege.permission_name;
-          user.editable = (Cluster.ineditableRoles.indexOf(privilege.permission_name) === -1);
-
-          arrayOfPrivileges.forEach(function(privilegeOfTypeGroup){
-            if (privilegeOfTypeGroup.PrivilegeInfo.principal_type === "GROUP") {
-              privilegesOfTypeGroup.push(privilegeOfTypeGroup.PrivilegeInfo);
-            }
-          });
-
-          var privilege_ids = [];
-          privilegesOfTypeGroup.forEach(function(privilegeOfTypeGroup) {
-            privilege_ids.push(privilegeOfTypeGroup.privilege_id);
-          });
-
-          //delete all privileges of type GROUP, if they exist
-          //then add the privilege for the group, after which the group displays the effective privilege
-          if(privilege_ids.length !== 0) {
-            Cluster.deleteMultiplePrivileges(
-                123,
-                privilege_ids
-            );
-          }
-          scope.addPrivilege(user);
-        });
-
-        scope.$apply();
-      });
-
-      it('Should save the Privilege equal to the group\'s effective privilege. Should remove any other privileges of the group',function(){
-        //using 'mygroup' for updating the new privilege
-        user.principal_name = scope.users[0].principal_name;
-        user.principal_type = scope.users[0].principal_type;
-        user.privilege_id = scope.users[0].privilege_id;
-        user.permission_name = "SERVICE.ADMINISTRATOR";
-
-        deferred.getPrivilegesForResource.resolve({ "items" : [items1] });
-
-        scope.$apply();
-
-        expect(scope.users[0].permission_name).toEqual(user.permission_name);
-        expect(scope.users[0].privilege_id).toEqual(user.privilege_id + 1);
-
-        var oldPrivilege = {
-          "PrivilegeInfo" : {
-            "cluster_name" : "myCluster",
-            "group_name" : "mygroup",
-            "permission_label" : "Service Administrator",
-            "permission_name" : "SERVICE.ADMINISTRATOR",
-            "principal_name" : "mygroup",
-            "principal_type" : "GROUP",
-            "privilege_id" : 3359,
-            "type" : "CLUSTER"
-          }
-        };
-        var newPrivilege = {
-          "PrivilegeInfo" : {
-            "privilege_id" : user.privilege_id + 1,
-            "permission_name" : user.permission_name,
-            "principal_type" : "GROUP",
-            "principal_name" : "mygroup",
-            "encoded_name" : "mygroup",
-            "original_perm" : user.permission_name,
-            "url" : "groups/mygroup/edit",
-            "editable" : true
-          }
-        };
-
-        //test if the older privilege is no longer present in 'items1'
-        expect(items1.privileges).toNotContain(oldPrivilege);
-
-        //test if the new privilege is added to 'items1'
-        expect(items1.privileges).toContain(newPrivilege);
-      });
-
-      it('Should save the Privilege greater than the group\'s effective privilege. Should remove any other privileges of the group',function(){
-        //using 'mygroup' for updating the new privilege
-        user.principal_name = scope.users[0].principal_name;
-        user.principal_type = scope.users[0].principal_type;
-        user.privilege_id = scope.users[0].privilege_id;
-        user.permission_name = "CLUSTER.ADMINISTRATOR";
-
-        deferred.getPrivilegesForResource.resolve({ "items" : [items1] });
-
-        scope.$apply();
-
-        expect(scope.users[0].permission_name).toEqual(user.permission_name);
-        expect(scope.users[0].privilege_id).toEqual(user.privilege_id + 1);
-
-        var oldPrivilege = {
-          "PrivilegeInfo" : {
-            "cluster_name" : "myCluster",
-            "group_name" : "mygroup",
-            "permission_label" : "Service Administrator",
-            "permission_name" : "SERVICE.ADMINISTRATOR",
-            "principal_name" : "mygroup",
-            "principal_type" : "GROUP",
-            "privilege_id" : 3359,
-            "type" : "CLUSTER"
-          }
-        };
-        var newPrivilege = {
-          "PrivilegeInfo" : {
-            "privilege_id" : user.privilege_id + 1,
-            "permission_name" : user.permission_name,
-            "principal_type" : "GROUP",
-            "principal_name" : "mygroup",
-            "encoded_name" : "mygroup",
-            "original_perm" : user.permission_name,
-            "url" : "groups/mygroup/edit",
-            "editable" : true
-          }
-        };
-
-        //test if the older privilege is no longer present in 'items1'
-        expect(items1.privileges).toNotContain(oldPrivilege);
-
-        //test if the new privilege is added to 'items1'
-        expect(items1.privileges).toContain(newPrivilege);
-      });
-
-      it('Should save the Privilege lesser than the group\'s effective privilege. Should remove any other privileges of the group', function() {
-        //using 'mygroup' for updating the new privilege
-        user.principal_name = scope.users[0].principal_name;
-        user.principal_type = scope.users[0].principal_type;
-        user.privilege_id = scope.users[0].privilege_id;
-        user.permission_name = "CLUSTER.USER";
-
-        deferred.getPrivilegesForResource.resolve({ "items" : [items1] });
-
-        scope.$apply();
-
-        expect(scope.users[0].permission_name).toEqual(user.permission_name);
-        expect(scope.users[0].privilege_id).toEqual(user.privilege_id + 1);
-
-        var oldPrivilege = {
-          "PrivilegeInfo" : {
-            "cluster_name" : "myCluster",
-            "group_name" : "mygroup",
-            "permission_label" : "Service Administrator",
-            "permission_name" : "SERVICE.ADMINISTRATOR",
-            "principal_name" : "mygroup",
-            "principal_type" : "GROUP",
-            "privilege_id" : 3359,
-            "type" : "CLUSTER"
-          }
-        };
-        var newPrivilege = {
-          "PrivilegeInfo" : {
-            "privilege_id" : user.privilege_id + 1,
-            "permission_name" : user.permission_name,
-            "principal_type" : "GROUP",
-            "principal_name" : "mygroup",
-            "encoded_name" : "mygroup",
-            "original_perm" : user.permission_name,
-            "url" : "groups/mygroup/edit",
-            "editable" : true
-          }
-        };
-
-        //test if the older privilege is no longer present in 'items1'
-        expect(items1.privileges).toNotContain(oldPrivilege);
-
-        //test if the new privilege is added to 'items1'
-        expect(items1.privileges).toContain(newPrivilege);
-      });
-
-    });
-
-    describe('#pickEffectivePrivilege()', function() {
-      var cases = [{
-          "test" : [{
-            "href" : "http://abc.com:8080/api/v1/groups/mygroup1",
-            "PrivilegeInfo" : {
-              "instance_name" : "jobs_view",
-              "permission_label" : "View User",
-              "permission_name" : "VIEW.USER",
-              "principal_name" : "mygroup1",
-              "principal_type" : "GROUP",
-              "privilege_id" : 111,
-              "type" : "VIEW",
-              "user_name" : "mygroup1",
-              "view_name" : "JOBS"
-            }
-          }],
-          "result":{
-            "permission_name": "CLUSTER.NONE"
-          }
-        }, {
-          "test": [{
-            "href" : "http://abc.com:8080/api/v1/groups/mygroup2",
-            "PrivilegeInfo" : {
-              "cluster_name":"mycluster",
-              "permission_label" : "Cluster User",
-              "permission_name" : "CLUSTER.USER",
-              "principal_name" : "mygroup2",
-              "principal_type" : "GROUP",
-              "privilege_id" : 222,
-              "type" : "CLUSTER",
-              "user_name":"mygroup2"
-            }
-          }],
-          "result":{
-            "permission_name": "CLUSTER.USER"
-          }
-        }, {
-          "test":[{
-            "href" : "http://abc.com:8080/api/v1/groups/mygroup3",
-            "PrivilegeInfo" : {
-              "cluster_name":"mycluster",
-              "permission_label" : "Cluster User",
-              "permission_name" : "CLUSTER.USER",
-              "principal_name" : "mygroup3",
-              "principal_type" : "GROUP",
-              "privilege_id" : 333,
-              "type" : "CLUSTER",
-              "user_name":"mygroup3"
-            }
-          }, {
-            "href" : "http://abc.com:8080/api/v1/groups/mygroup3",
-            "PrivilegeInfo" : {
-              "instance_name": "jobs_view",
-              "permission_label" : "View User",
-              "permission_name" : "VIEW.USER",
-              "principal_name" : "mygroup3",
-              "principal_type" : "GROUP",
-              "privilege_id" : 3333,
-              "type" : "VIEW",
-              "user_name":"mygroup3",
-              "view_name":"JOBS"
-            }
-          }],
-          "result":{
-            "permission_name": "CLUSTER.USER"
-          }
-        }, {
-          "test": [{
-            "href" : "http://abc.com:8080/api/v1/users/myuser1/privileges/11",
-            "PrivilegeInfo" : {
-              "instance_name": "jobs_view",
-              "permission_label" : "View User",
-              "permission_name" : "VIEW.USER",
-              "principal_name" : "myuser1",
-              "principal_type" : "USER",
-              "privilege_id" : 11,
-              "type" : "VIEW",
-              "user_name":"myuser1",
-              "view_name":"JOBS"
-            }
-          }],
-          "result":{
-            "permission_name": "CLUSTER.NONE"
-          }
-        }, {
-          "test":[{
-            "href" : "http://abc.com:8080/api/v1/users/myuser2/privileges/22",
-            "PrivilegeInfo" : {
-              "cluster_name":"mycluster",
-              "permission_label" : "Cluster Administrator",
-              "permission_name" : "CLUSTER.ADMINISTRATOR",
-              "principal_name" : "myuser2",
-              "principal_type" : "USER",
-              "privilege_id" : 22,
-              "type" : "CLUSTER",
-              "user_name":"myuser2"
-            }
-          }],
-          "result":{
-            "permission_name": "CLUSTER.ADMINISTRATOR"
-          }
-        }
-      ];
-
-      it('User/Group with only View User permission must show \'None\' as the Cluster Permission, otherwise show the effective privilege', function(){
-        var effectivePrivilege;
-        cases.forEach(function (item){
-          effectivePrivilege = scope.pickEffectivePrivilege(item.test);
-          scope.$apply();
-          expect(effectivePrivilege.permission_name).toEqual(item.result.permission_name);
-        });
-      });
-    });
-
-  });
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/groups/GroupsListCtrl_test.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/groups/GroupsListCtrl_test.js b/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/groups/GroupsListCtrl_test.js
deleted file mode 100644
index 8ed228b..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/groups/GroupsListCtrl_test.js
+++ /dev/null
@@ -1,129 +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.
- */
-
-describe('#Cluster', function () {
-
-  describe('GroupsListCtrl', function() {
-
-    var scope, ctrl, $t, $httpBackend;
-
-    beforeEach(module('ambariAdminConsole', function () {}));
-
-    beforeEach(inject(function($rootScope, $controller, _$translate_, _$httpBackend_) {
-      scope = $rootScope.$new();
-      $t = _$translate_.instant;
-      $httpBackend = _$httpBackend_;
-      ctrl = $controller('GroupsListCtrl', {
-        $scope: scope
-      });
-    }));
-
-    describe('#clearFilters()', function () {
-
-      it('should clear filters and reset pagination', function () {
-        scope.currentPage = 2;
-        scope.currentNameFilter = 'a';
-        scope.currentTypeFilter = {
-          label: $t('common.local'),
-          value: false
-        };
-        scope.clearFilters();
-        expect(scope.currentNameFilter).toEqual('');
-        expect(scope.currentTypeFilter).toEqual({
-          label: $t('common.all'),
-          value: '*'
-        });
-        expect(scope.currentPage).toEqual(1);
-      });
-
-    });
-
-    describe('#isNotEmptyFilter', function () {
-
-      var cases = [
-        {
-          currentNameFilter: '',
-          currentTypeFilter: null,
-          isNotEmptyFilter: false,
-          title: 'no filters'
-        },
-        {
-          currentNameFilter: '',
-          currentTypeFilter: {
-            value: '*'
-          },
-          isNotEmptyFilter: false,
-          title: 'empty filters'
-        },
-        {
-          currentNameFilter: 'a',
-          currentTypeFilter: {
-            value: '*'
-          },
-          isNotEmptyFilter: true,
-          title: 'name filter'
-        },
-        {
-          currentNameFilter: '0',
-          currentTypeFilter: {
-            value: '*'
-          },
-          isNotEmptyFilter: true,
-          title: 'name filter with "0" as string'
-        },
-        {
-          currentNameFilter: '',
-          currentTypeFilter: {
-            value: false
-          },
-          isNotEmptyFilter: true,
-          title: 'type filter'
-        },
-        {
-          currentNameFilter: 'a',
-          currentTypeFilter: {
-            value: false
-          },
-          isNotEmptyFilter: true,
-          title: 'both filters'
-        },
-        {
-          currentNameFilter: '0',
-          currentTypeFilter: {
-            value: false
-          },
-          isNotEmptyFilter: true,
-          title: 'both filters with "0" as string'
-        }
-      ];
-
-      cases.forEach(function (item) {
-        it(item.title, function () {
-          $httpBackend.expectGET(/\/api\/v1\/groups/).respond(200);
-          scope.currentNameFilter = item.currentNameFilter;
-          scope.currentTypeFilter = item.currentTypeFilter;
-          scope.$digest();
-          expect(scope.isNotEmptyFilter).toEqual(item.isNotEmptyFilter);
-        });
-      });
-
-    });
-
-  });
-
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/userManagement/GroupsListCtrl_test.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/userManagement/GroupsListCtrl_test.js b/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/userManagement/GroupsListCtrl_test.js
new file mode 100644
index 0000000..8d04757
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/userManagement/GroupsListCtrl_test.js
@@ -0,0 +1,129 @@
+/**
+ * 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.
+ */
+
+describe('#Cluster', function () {
+
+  describe('GroupsListCtrl', function() {
+
+    var scope, ctrl, $t, $httpBackend;
+
+    beforeEach(module('ambariAdminConsole', function () {}));
+
+    beforeEach(inject(function($rootScope, $controller, _$translate_, _$httpBackend_) {
+      scope = $rootScope.$new();
+      $t = _$translate_.instant;
+      $httpBackend = _$httpBackend_;
+      ctrl = $controller('GroupsListCtrl', {
+        $scope: scope
+      });
+    }));
+
+    describe('#clearFilters()', function () {
+
+      it('should clear filters and reset pagination', function () {
+        scope.currentPage = 2;
+        scope.filter.name = 'a';
+        scope.filter.type = {
+          label: $t('common.local'),
+          value: false
+        };
+        scope.clearFilters();
+        expect(scope.filter.name).toEqual('');
+        expect(scope.filter.type).toEqual({
+          label: $t('common.all'),
+          value: '*'
+        });
+        expect(scope.currentPage).toEqual(1);
+      });
+
+    });
+
+    describe('#isNotEmptyFilter', function () {
+
+      var cases = [
+        {
+          currentNameFilter: '',
+          currentTypeFilter: null,
+          isNotEmptyFilter: false,
+          title: 'no filters'
+        },
+        {
+          currentNameFilter: '',
+          currentTypeFilter: {
+            value: '*'
+          },
+          isNotEmptyFilter: false,
+          title: 'empty filters'
+        },
+        {
+          currentNameFilter: 'a',
+          currentTypeFilter: {
+            value: '*'
+          },
+          isNotEmptyFilter: true,
+          title: 'name filter'
+        },
+        {
+          currentNameFilter: '0',
+          currentTypeFilter: {
+            value: '*'
+          },
+          isNotEmptyFilter: true,
+          title: 'name filter with "0" as string'
+        },
+        {
+          currentNameFilter: '',
+          currentTypeFilter: {
+            value: false
+          },
+          isNotEmptyFilter: true,
+          title: 'type filter'
+        },
+        {
+          currentNameFilter: 'a',
+          currentTypeFilter: {
+            value: false
+          },
+          isNotEmptyFilter: true,
+          title: 'both filters'
+        },
+        {
+          currentNameFilter: '0',
+          currentTypeFilter: {
+            value: false
+          },
+          isNotEmptyFilter: true,
+          title: 'both filters with "0" as string'
+        }
+      ];
+
+      cases.forEach(function (item) {
+        it(item.title, function () {
+          $httpBackend.expectGET(/\/api\/v1\/groups/).respond(200);
+          scope.filter.name = item.currentNameFilter;
+          scope.filter.type = item.currentTypeFilter;
+          scope.$digest();
+          expect(scope.isNotEmptyFilter).toEqual(item.isNotEmptyFilter);
+        });
+      });
+
+    });
+
+  });
+
+});


[3/4] ambari git commit: AMBARI-22508 Ambari 3.0: Implement new design for Admin View: User Management. (atkach)

Posted by at...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UsersListCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UsersListCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UsersListCtrl.js
new file mode 100644
index 0000000..abe1780
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UsersListCtrl.js
@@ -0,0 +1,178 @@
+/**
+ * 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('ambariAdminConsole')
+.controller('UsersListCtrl',
+['$scope', 'User', '$modal', '$rootScope', 'UserConstants', '$translate', 'Cluster', 'View', 'ConfirmationModal', 'Settings',
+function($scope, User, $modal, $rootScope, UserConstants, $translate, Cluster, View, ConfirmationModal, Settings) {
+  var $t = $translate.instant;
+  $scope.constants = {
+    admin: $t('users.ambariAdmin'),
+    users: $t('common.users').toLowerCase()
+  };
+  $scope.minRowsToShowPagination = Settings.minRowsToShowPagination;
+  $scope.isLoading = false;
+  $scope.users = [];
+  $scope.usersPerPage = 10;
+  $scope.currentPage = 1;
+  $scope.totalUsers = 0;
+  $scope.filters = {
+    name: '',
+    status: null,
+    type: null
+  };
+  $scope.maxVisiblePages = 20;
+  $scope.tableInfo = {
+    total: 0,
+    showed: 0
+  };
+  $scope.isNotEmptyFilter = true;
+
+  $scope.pageChanged = function() {
+    $scope.loadUsers();
+  };
+  $scope.usersPerPageChanges = function() {
+    $scope.resetPagination();
+  };
+
+  $scope.loadUsers = function(){
+    $scope.isLoading = true;
+    User.list({
+      currentPage: $scope.currentPage,
+      usersPerPage: $scope.usersPerPage,
+      searchString: $scope.filters.name,
+      user_type: $scope.filters.type.value,
+      active: $scope.filters.status.value
+    }).then(function(data) {
+      $scope.isLoading = false;
+      $scope.totalUsers = data.data.itemTotal;
+      $scope.users = data.data.items.map(User.makeUser);
+      $scope.tableInfo.showed = data.data.items.length;
+      $scope.tableInfo.total = data.data.itemTotal;
+    });
+  };
+
+  $scope.resetPagination = function() {
+    $scope.currentPage = 1;
+    $scope.loadUsers();
+  };
+
+  $scope.activeFilterOptions = [
+    {label: $t('common.all'), value: '*'},
+    {label: $t('users.active'), value: true},
+    {label: $t('users.inactive'), value:false}
+  ];
+  $scope.filters.status = $scope.activeFilterOptions[0];
+
+  $scope.typeFilterOptions = [{ label: $t('common.all'), value: '*'}]
+    .concat(Object.keys(UserConstants.TYPES).map(function(key) {
+      return {
+        label: $t(UserConstants.TYPES[key].LABEL_KEY),
+        value: UserConstants.TYPES[key].VALUE
+      };
+    }));
+
+  $scope.filters.type = $scope.typeFilterOptions[0];
+
+  $scope.clearFilters = function () {
+    $scope.filters.name = '';
+    $scope.filters.type = $scope.typeFilterOptions[0];
+    $scope.filters.status = $scope.activeFilterOptions[0];
+    $scope.resetPagination();
+  };
+
+  $scope.loadUsers();
+
+  $scope.$watch(
+    function (scope) {
+      return Boolean(scope.filters.name || (scope.filters.status && scope.filters.status.value !== '*')
+        || (scope.filters.type && scope.filters.type.value !== '*'));
+    },
+    function (newValue, oldValue, scope) {
+      scope.isNotEmptyFilter = newValue;
+    }
+  );
+
+  $rootScope.$watch(function(scope) {
+    return scope.LDAPSynced;
+  }, function(LDAPSynced) {
+    if(LDAPSynced === true){
+      $rootScope.LDAPSynced = false;
+      $scope.loadUsers();
+    }
+  });
+
+  $scope.createUser = function () {
+    var modalInstance = $modal.open({
+      templateUrl: 'views/userManagement/modals/userCreate.html',
+      controller: 'UserCreateCtrl',
+      backdrop: 'static'
+    });
+
+    modalInstance.result.finally($scope.loadUsers);
+  };
+
+  $scope.deleteUser = function(user) {
+    ConfirmationModal.show(
+      $t('common.delete', {
+        term: $t('common.user')
+      }),
+      $t('common.deleteConfirmation', {
+        instanceType: $t('common.user').toLowerCase(),
+        instanceName: '"' + user.user_name + '"'
+      })
+    ).then(function() {
+      Cluster.getPrivilegesForResource({
+        nameFilter : user.user_name,
+        typeFilter : {value: 'USER'}
+      }).then(function(data) {
+        var clusterPrivilegesIds = [];
+        var viewsPrivileges = [];
+        if (data.items && data.items.length) {
+          angular.forEach(data.items[0].privileges, function(privilege) {
+            if (privilege.PrivilegeInfo.principal_type === 'USER') {
+              if (privilege.PrivilegeInfo.type === 'VIEW') {
+                viewsPrivileges.push({
+                  id: privilege.PrivilegeInfo.privilege_id,
+                  view_name: privilege.PrivilegeInfo.view_name,
+                  version: privilege.PrivilegeInfo.version,
+                  instance_name: privilege.PrivilegeInfo.instance_name
+                });
+              } else {
+                clusterPrivilegesIds.push(privilege.PrivilegeInfo.privilege_id);
+              }
+            }
+          });
+        }
+        User.delete(user.user_name).then(function() {
+          if (clusterPrivilegesIds.length) {
+            Cluster.getAllClusters().then(function (clusters) {
+              var clusterName = clusters[0].Clusters.cluster_name;
+              Cluster.deleteMultiplePrivileges(clusterName, clusterPrivilegesIds);
+            });
+          }
+          angular.forEach(viewsPrivileges, function(privilege) {
+            View.deletePrivilege(privilege);
+          });
+          $scope.loadUsers();
+        });
+      });
+    });
+  };
+}]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersCreateCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersCreateCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersCreateCtrl.js
deleted file mode 100644
index bcb7bfc..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersCreateCtrl.js
+++ /dev/null
@@ -1,75 +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.
- */
-'use strict';
-
-angular.module('ambariAdminConsole')
-.controller('UsersCreateCtrl',['$scope', '$routeParams', 'User', '$location', 'Alert', 'UnsavedDialog', '$translate', function($scope, $routeParams, User, $location, Alert, UnsavedDialog, $translate) {
-  var $t = $translate.instant;
-  $scope.user = {
-    active: true
-  };
-  var targetUrl = '/users';
-
-  $scope.createUser = function() {
-    $scope.form.submitted = true;
-    if ($scope.form.$valid){
-      User.create({
-        'Users/user_name': $scope.user.user_name,
-        'Users/password': $scope.user.password,
-        'Users/active': !!$scope.user.active,
-        'Users/admin': !!$scope.user.admin
-      }).then(function() {
-        Alert.success($t('users.alerts.userCreated', {
-          userName: $scope.user.user_name,
-          encUserName: encodeURIComponent($scope.user.user_name)
-        }));
-        $scope.form.$setPristine();
-        $location.path(targetUrl);
-      }).catch(function(data) {
-        Alert.error($t('users.alerts.userCreationError'), data.data.message);
-      });
-    }
-  };
-
-  $scope.cancel = function() {
-    $scope.form.$setPristine();
-    $location.path('/users');
-  };
-
-  $scope.$on('$locationChangeStart', function(event, __targetUrl) {
-        
-    if( $scope.form.$dirty ){
-      UnsavedDialog().then(function(action) {
-        targetUrl = __targetUrl.split('#').pop();
-        switch(action){
-          case 'save':
-            $scope.createUser();
-            break;
-          case 'discard':
-            $scope.form.$setPristine();
-            $location.path(targetUrl);
-            break;
-          case 'cancel':
-          targetUrl = '/users';
-            break;
-        }
-      });
-      event.preventDefault();
-    }
-  });
-}]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersListCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersListCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersListCtrl.js
deleted file mode 100644
index 8146163..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersListCtrl.js
+++ /dev/null
@@ -1,122 +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.
- */
-'use strict';
-
-angular.module('ambariAdminConsole')
-  .controller('UsersListCtrl',['$scope', 'User', '$modal', '$rootScope', 'UserConstants', '$translate', 'Settings', function($scope, User, $modal, $rootScope, UserConstants, $translate, Settings) {
-  var $t = $translate.instant;
-  $scope.constants = {
-    admin: $t('users.ambariAdmin'),
-    users: $t('common.users').toLowerCase()
-  };
-  $scope.isLoading = false;
-  $scope.users = [];
-  $scope.usersPerPage = 10;
-  $scope.currentPage = 1;
-  $scope.totalUsers = 1;
-  $scope.currentNameFilter = '';
-  $scope.maxVisiblePages=20;
-  $scope.tableInfo = {
-    total: 0,
-    showed: 0
-  };
-  $scope.isNotEmptyFilter = true;
-
-  $scope.pageChanged = function() {
-    $scope.loadUsers();
-  };
-  $scope.usersPerPageChanges = function() {
-    $scope.resetPagination();
-  };
-
-  $scope.loadUsers = function(){
-    $scope.isLoading = true;
-    User.list({
-      currentPage: $scope.currentPage,
-      usersPerPage: $scope.usersPerPage,
-      searchString: $scope.currentNameFilter,
-      user_type: $scope.currentTypeFilter.value,
-      active: $scope.currentActiveFilter.value,
-      admin: $scope.adminFilter
-    }).then(function(data) {
-      $scope.isLoading = false;
-      $scope.totalUsers = data.data.itemTotal;
-      $scope.users = data.data.items.map(User.makeUser);
-      $scope.tableInfo.showed = data.data.items.length;
-      $scope.tableInfo.total = data.data.itemTotal;
-    });
-  };
-
-  $scope.resetPagination = function() {
-    $scope.currentPage = 1;
-    $scope.loadUsers();
-  };
-
-  $scope.activeFilterOptions = [
-    {label: $t('common.all'), value: '*'},
-    {label: $t('users.active'), value: true},
-    {label: $t('users.inactive'), value:false}
-  ];
-  $scope.currentActiveFilter = $scope.activeFilterOptions[0];
-
-  $scope.typeFilterOptions = [{ label: $t('common.all'), value: '*'}]
-    .concat(Object.keys(UserConstants.TYPES).map(function(key) {
-      return {
-        label: $t(UserConstants.TYPES[key].LABEL_KEY),
-        value: UserConstants.TYPES[key].VALUE
-      };
-    }));
-
-  $scope.currentTypeFilter = $scope.typeFilterOptions[0];
-
-  $scope.adminFilter = false;
-  $scope.toggleAdminFilter = function() {
-    $scope.adminFilter = !$scope.adminFilter;
-    $scope.resetPagination();
-    $scope.loadUsers();
-  };
-
-  $scope.clearFilters = function () {
-    $scope.currentNameFilter = '';
-    $scope.currentTypeFilter = $scope.typeFilterOptions[0];
-    $scope.currentActiveFilter = $scope.activeFilterOptions[0];
-    $scope.adminFilter = false;
-    $scope.resetPagination();
-  };
-
-  $scope.loadUsers();
-
-  $scope.$watch(
-    function (scope) {
-      return Boolean(scope.currentNameFilter || (scope.currentActiveFilter && scope.currentActiveFilter.value !== '*')
-        || (scope.currentTypeFilter && scope.currentTypeFilter.value !== '*') || $scope.adminFilter);
-    },
-    function (newValue, oldValue, scope) {
-      scope.isNotEmptyFilter = newValue;
-    }
-  );
-
-  $rootScope.$watch(function(scope) {
-    return scope.LDAPSynced;
-  }, function(LDAPSynced) {
-    if(LDAPSynced === true){
-      $rootScope.LDAPSynced = false;
-      $scope.loadUsers();
-    }
-  });
-}]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersShowCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersShowCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersShowCtrl.js
deleted file mode 100644
index 200872e..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersShowCtrl.js
+++ /dev/null
@@ -1,290 +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.
- */
-'use strict';
-
-angular.module('ambariAdminConsole')
-.controller('UsersShowCtrl', ['$scope', '$routeParams', 'Cluster', 'User', 'View', '$modal', '$location', 'ConfirmationModal', 'Alert', 'Auth', 'getDifference', 'Group', '$q', 'UserConstants', '$translate', function($scope, $routeParams, Cluster, User, View, $modal, $location, ConfirmationModal, Alert, Auth, getDifference, Group, $q, UserConstants, $translate) {
-
-  var $t = $translate.instant;
-
-  $scope.constants = {
-    user: $t('common.user'),
-    status: $t('users.status'),
-    admin: $t('users.admin'),
-    password: $t('users.password'),
-    view: $t('common.view').toLowerCase(),
-    cluster: $t('common.cluster').toLowerCase()
-  };
-
-  function loadUserInfo(){
-    User.get($routeParams.id).then(function(data) {
-      $scope.user = User.makeUser(data).Users;
-      $scope.isCurrentUser = $scope.user.user_name === Auth.getCurrentUser();
-      $scope.editingGroupsList = angular.copy($scope.user.groups);
-    });
-  }
-
-  loadUserInfo();
-  $scope.user;
-  $scope.isCurrentUser = true;
-  $scope.dataLoaded = false;
-
-  $scope.isGroupEditing = false;
-  $scope.enableGroupEditing = function() {
-    $scope.isGroupEditing = true;
-    $scope.editingGroupsList = angular.copy($scope.user.groups);
-  };
-
-  $scope.$watch(function() {
-    return $scope.editingGroupsList;
-  }, function(newValue) {
-    if(newValue){
-      if( !angular.equals(newValue, $scope.user.groups) ){
-        $scope.updateGroups();
-      }
-    }
-  }, true);
-
-  $scope.updateGroups = function() {
-    var groups = $scope.editingGroupsList.toString().split(',').filter(function(item) {return item.trim();}).map(function(item) {return item.trim()});
-    var diff = getDifference($scope.user.groups, groups);
-    var promises = [];
-    // Remove user from groups
-    angular.forEach(diff.del, function(groupName) {
-      promises.push(Group.removeMemberFromGroup(groupName, $scope.user.user_name).catch(function(data) {
-        Alert.error($t('users.alerts.removeUserError'), data.data.message);
-      }));
-    });
-    // Add user to groups
-    angular.forEach(diff.add, function(groupName) {
-      promises.push(Group.addMemberToGroup(groupName, $scope.user.user_name).catch(function(data) {
-        Alert.error($t('users.alerts.cannotAddUser'), data.data.message);
-      }));
-    });
-    $q.all(promises).then(function() {
-      loadUserInfo();
-    });
-    $scope.isGroupEditing = false;
-  };
-
-  $scope.getUserMembership = function(userType) {
-    if(userType) {
-	return $t(UserConstants.TYPES[userType].LABEL_KEY) + " " + $t('users.groupMembership');
-    }
-  };
-
-  $scope.cancelUpdate = function() {
-    $scope.isGroupEditing = false;
-    $scope.editingGroupsList = '';
-  };
-
-  $scope.openChangePwdDialog = function() {
-    var modalInstance = $modal.open({
-      templateUrl: 'views/users/modals/changePassword.html',
-      resolve: {
-        userName: function() {
-          return $scope.user.user_name;
-        }
-      },
-      controller: ['$scope', 'userName', function($scope, userName) {
-        $scope.passwordData = {
-          password: '',
-          currentUserPassword: ''
-        };
-
-        $scope.form = {};
-        $scope.userName = userName;
-
-        $scope.ok = function() {
-          $scope.form.passwordChangeForm.submitted = true;
-          if($scope.form.passwordChangeForm.$valid){
-
-            modalInstance.close({
-              password: $scope.passwordData.password, 
-              currentUserPassword: $scope.passwordData.currentUserPassword
-            });
-          }
-        };
-        $scope.cancel = function() {
-          modalInstance.dismiss('cancel');
-        };
-      }]
-    });
-
-    modalInstance.result.then(function(data) {
-      User.setPassword($scope.user, data.password, data.currentUserPassword).then(function() {
-        Alert.success($t('users.alerts.passwordChanged'));
-      }).catch(function(data) {
-        Alert.error($t('users.alerts.cannotChangePassword'), data.data.message);
-      });
-    }); 
-  };
-
-  $scope.toggleUserActive = function() {
-    if(!$scope.isCurrentUser){
-      var newStatusKey = $scope.user.active ? 'inactive' : 'active',
-        newStatus = $t('users.' + newStatusKey).toLowerCase();
-      ConfirmationModal.show(
-        $t('users.changeStatusConfirmation.title'),
-        $t('users.changeStatusConfirmation.message', {
-          userName: $scope.user.user_name,
-          status: newStatus
-        })
-      ).then(function() {
-        User.setActive($scope.user.user_name, $scope.user.active)
-          .catch(function(data) {
-            Alert.error($t('common.alerts.cannotUpdateStatus'), data.data.message);
-            $scope.user.active = !$scope.user.active;
-          });
-      })
-      .catch(function() {
-        $scope.user.active = !$scope.user.active;
-      });
-    }
-  };    
-  $scope.toggleUserAdmin = function() {
-    if(!$scope.isCurrentUser){
-      var action = $scope.user.admin ?
-        $t('users.changePrivilegeConfirmation.revoke') : $t('users.changePrivilegeConfirmation.grant');
-      ConfirmationModal.show(
-        $t('users.changePrivilegeConfirmation.title'),
-        $t('users.changePrivilegeConfirmation.message', {
-          action: action,
-          userName: $scope.user.user_name
-        })
-      ).then(function() {
-        User.setAdmin($scope.user.user_name, $scope.user.admin)
-        .then(function() {
-          loadPrivileges();
-        })
-        .catch(function (data) {
-          Alert.error($t('common.alerts.cannotUpdateAdminStatus'), data.data.message);
-          $scope.user.admin = !$scope.user.admin;
-        });
-      })
-      .catch(function() {
-        $scope.user.admin = !$scope.user.admin;
-      });
-
-    }
-  };
-
-  $scope.removePrivilege = function(name, privilege) {
-    var privilegeObject = {
-        id: privilege.privilege_id,
-        view_name: privilege.view_name,
-        version: privilege.version,
-        instance_name: name
-    };
-    View.deletePrivilege(privilegeObject).then(function() {
-      loadPrivileges();
-    });
-  };
-
-  $scope.deleteUser = function() {
-    ConfirmationModal.show(
-      $t('common.delete', {
-        term: $t('common.user')
-      }),
-      $t('common.deleteConfirmation', {
-        instanceType: $t('common.user').toLowerCase(),
-        instanceName: '"' + $scope.user.user_name + '"'
-      })
-    ).then(function() {
-      Cluster.getPrivilegesForResource({
-        nameFilter : $scope.user.user_name,
-        typeFilter : {value: 'USER'}
-      }).then(function(data) {
-        var clusterPrivilegesIds = [];
-        var viewsPrivileges = [];
-        if (data.items && data.items.length) {
-          angular.forEach(data.items[0].privileges, function(privilege) {
-            if (privilege.PrivilegeInfo.principal_type === 'USER') {
-              if (privilege.PrivilegeInfo.type === 'VIEW') {
-                viewsPrivileges.push({
-                  id: privilege.PrivilegeInfo.privilege_id,
-                  view_name: privilege.PrivilegeInfo.view_name,
-                  version: privilege.PrivilegeInfo.version,
-                  instance_name: privilege.PrivilegeInfo.instance_name
-                });
-              } else {
-                clusterPrivilegesIds.push(privilege.PrivilegeInfo.privilege_id);
-              }
-            }
-          });
-        }
-        User.delete($scope.user.user_name).then(function() {
-          $location.path('/users');
-          if (clusterPrivilegesIds.length) {
-            Cluster.getAllClusters().then(function (clusters) {
-              var clusterName = clusters[0].Clusters.cluster_name;
-              Cluster.deleteMultiplePrivileges(clusterName, clusterPrivilegesIds);
-            });
-          }
-          angular.forEach(viewsPrivileges, function(privilege) {
-            View.deletePrivilege(privilege);
-          });
-        });
-      });
-    });
-  };
-
-  // Load privileges
-  function loadPrivileges(){
-    User.getPrivileges($routeParams.id).then(function(data) {
-      var privileges = {
-        clusters: {},
-        views: {}
-      };
-      angular.forEach(data.data.items, function(privilege) {
-        privilege = privilege.PrivilegeInfo;
-        if(privilege.type === 'CLUSTER'){
-          // This is cluster
-          if (privileges.clusters[privilege.cluster_name]) {
-            var preIndex = Cluster.orderedRoles.indexOf(privileges.clusters[privilege.cluster_name].permission_name);
-            var curIndex = Cluster.orderedRoles.indexOf(privilege.permission_name);
-            // replace when cur is a more powerful role
-            if (curIndex < preIndex) {
-              privileges.clusters[privilege.cluster_name] = privilege;
-            }
-          } else {
-            privileges.clusters[privilege.cluster_name] = privilege;
-          }
-        } else if ( privilege.type === 'VIEW'){
-          privileges.views[privilege.instance_name] = privileges.views[privilege.instance_name] || { privileges:[]};
-          privileges.views[privilege.instance_name].version = privilege.version;
-          privileges.views[privilege.instance_name].view_name = privilege.view_name;
-          privileges.views[privilege.instance_name].privilege_id = privilege.privilege_id;
-          if (privileges.views[privilege.instance_name].privileges.indexOf(privilege.permission_label) == -1) {
-            privileges.views[privilege.instance_name].privileges.push(privilege.permission_label);
-          }
-        }
-      });
-
-      $scope.privileges = data.data.items.length ? privileges : null;
-      $scope.noClusterPriv = $.isEmptyObject(privileges.clusters);
-      $scope.noViewPriv = $.isEmptyObject(privileges.views);
-      $scope.hidePrivileges = $scope.noClusterPriv && $scope.noViewPriv;
-      $scope.dataLoaded = true;
-
-    }).catch(function(data) {
-      Alert.error($t('common.alerts.cannotLoadPrivileges'), data.data.message);
-    });
-  }
-  loadPrivileges();
-}]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js
index 1967dfa..de3968d 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js
@@ -101,6 +101,7 @@ angular.module('ambariAdminConsole')
     'common.admin': 'Admin',
     'common.actions': 'Actions',
     'common.error': 'Error',
+    'common.select': 'Select',
 
     'common.clusterNameChangeConfirmation.title': 'Confirm Cluster Name Change',
     'common.clusterNameChangeConfirmation.message': 'Are you sure you want to change the cluster name to {{clusterName}}?',
@@ -276,6 +277,8 @@ angular.module('ambariAdminConsole')
 
     'groups.createLocal': 'Add Groups',
     'groups.name': 'Group name',
+    'groups.role': 'Add roles to this group',
+    'groups.addUsers': 'Add users to this group',
     'groups.members': 'Members',
     'groups.membersPlural': '{{n}} member{{n == 1 ? "" : "s"}}',
 
@@ -285,7 +288,7 @@ angular.module('ambariAdminConsole')
     'groups.alerts.getGroupsListError': 'Get groups list error',
 
     'users.username': 'Username',
-    'users.userName': 'User name',
+    'users.user.name': 'User name',
     'users.admin': 'Admin',
     'users.ambariAdmin': 'Ambari Admin',
     'users.ambariClusterURL': 'Ambari Cluster URL',
@@ -300,7 +303,11 @@ angular.module('ambariAdminConsole')
     'users.inactive': 'Inactive',
     'users.status': 'Status',
     'users.password': 'Password',
+    'users.role': 'Add roles for this user (Cluster Operator/Service Admin)',
+    'users.confirmPassword': 'Confirm Password',
     'users.passwordConfirmation': 'Password сonfirmation',
+    'users.isAmbariAdmin': 'Is this user an Ambari Admin?',
+    'users.isActive': 'Deactivate this user?',
     'users.userIsAdmin': 'This user is an Ambari Admin and has all privileges.',
     'users.showAll': 'Show all users',
     'users.showAdmin': 'Show only admin users',

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/routes.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/routes.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/routes.js
index dabc57a..c8d0e96 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/routes.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/routes.js
@@ -38,51 +38,25 @@ angular.module('ambariAdminConsole')
       controller: 'LoginActivitiesMainCtrl'
     }
   },
-  users: {
-    list: {
-      url: '/users',
-      templateUrl: 'views/users/list.html',
-      controller: 'UsersListCtrl',
+  userManagement: {
+    main: {
+      url: '/userManagement',
+      templateUrl: 'views/userManagement/main.html',
+      controller: 'UserManagementCtrl',
       label: 'Users'
     },
-    edit: {
+    editUser: {
       url: '/users/:id/edit',
-      templateUrl: 'views/users/create.html',
-      controller: 'UsersCreateCtrl',
+      templateUrl: 'views/userManagement/userEdit.html',
+      controller: 'UserEditCtrl',
       label: 'Users'
     },
-    create: {
-      url: '/users/new',
-      templateUrl: 'views/users/create.html',
-      controller: 'UsersCreateCtrl',
-      label: 'Users'
-    },
-    show: {
-      url: '/users/:id*',
-      templateUrl: 'views/users/show.html',
-      controller: 'UsersShowCtrl',
-      label: 'Users'
-    }
-  },
-  groups: {
-    list: {
-      url: '/groups',
-      templateUrl: 'views/groups/list.html',
-      controller: 'GroupsListCtrl',
-      label: 'Groups'
-    },
-    edit: {
+    editGroup: {
       url: '/groups/:id/edit',
-      templateUrl: 'views/groups/edit.html',
-      controller: 'GroupsEditCtrl',
+      templateUrl: 'views/userManagement/groupEdit.html',
+      controller: 'GroupEditCtrl',
       label: 'Groups'
     },
-    create: {
-      url: '/groups/new',
-      templateUrl: 'views/groups/create.html',
-      controller: 'GroupsCreateCtrl',
-      label: 'Groups'
-    }
   },
   views: {
     list: {
@@ -157,20 +131,9 @@ angular.module('ambariAdminConsole')
     }
   },
   clusters: {
-    manageAccess: {
-      url: '/clusters/:id/manageAccess',
-      templateUrl: 'views/clusters/manageAccess.html',
-      controller: 'ClustersManageAccessCtrl',
-      label: 'Roles'
-    },
-    userAccessList: {
-      url: '/clusters/:id/userAccessList',
-      templateUrl: 'views/clusters/userAccessList.html',
-      controller: 'UserAccessListCtrl'
-    },
     clusterInformation: {
       url: '/clusterInformation',
-      templateUrl: 'views/clusterInformation.html',
+      templateUrl: 'views/clusters/clusterInformation.html',
       controller: 'ClusterInformationCtrl',
       label: 'Cluster Information'
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js
index ac50653..47015d1 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js
@@ -94,9 +94,10 @@ angular.module('ambariAdminConsole')
      * @returns {Object}
      */
     makeUser: function(user) {
-      user.Users.encoded_name = encodeURIComponent(user.Users.user_name);
+      user.Users.encodedName = encodeURIComponent(user.Users.user_name);
       user.Users.userTypeName = $t(UserConstants.TYPES[user.Users.user_type].LABEL_KEY);
-      user.Users.ldap_user = user.Users.user_type === UserConstants.TYPES.LDAP.VALUE;
+      user.Users.ldapUser = user.Users.user_type === UserConstants.TYPES.LDAP.VALUE;
+      user.Users.role = user.privileges.length ? user.privileges[0].PrivilegeInfo.privilege_id : null;
 
       return user;
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css b/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css
index 94bdf11..91b2fb1 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css
@@ -247,6 +247,9 @@ a.gotoinstance{
 .hide-soft{
   display: none;
 }
+.nowrap {
+  white-space: nowrap;
+}
 .visible{
   display: block;
 }
@@ -386,16 +389,6 @@ a.gotoinstance{
 .search-container input {
   font-weight: normal;
 }
-.groups-pane .search-container .close{
-  top: 32px;
-}
-
-.groups-pane table thead th{
-  border-top: 0;
-}
-.groups-pane table thead tr:first-child th{
-  border: 0;
-}
 
 ul.nav li > a{
   cursor: pointer;
@@ -464,16 +457,6 @@ table.no-border tr td{
   margin-top: 0;
 }
 
-.groups-pane table ul{
-  list-style-type: none;
-  margin: 0;
-  padding: 0;
-}
-.groups-pane table ul li {
-  margin: 0;
-  padding: 0;
-}
-
 .property-form label{
   word-wrap: break-word;
   text-overflow: ellipsis;
@@ -539,6 +522,7 @@ button.btn.btn-xs{
   font-size: 12px;
   line-height: 1.5;
   border-radius: 3px;
+  height: 24px;
 }
 
 a.btn-primary, a.btn-primary:focus {
@@ -617,13 +601,13 @@ a.alert-link, a.alert-link:hover, a.alert-link:visited{
   box-sizing: border-box;
 }
 
-.fix-bottom{
+.fix-bottom th {
   border-bottom: none !important;;
   border-top: none !important;
   border-width: 0;
 }
 
-.fix-top{
+.fix-top th {
   border-top: none !important;
   border-width: 0;
 }
@@ -1334,3 +1318,21 @@ body {
 .navigation-bar-fit-height {
   z-index: 1001;
 }
+
+.entity-actions a {
+  color: inherit;
+  font-size: 16px;
+  cursor: pointer;
+  padding: 0 5px;
+}
+
+td.entity-actions,
+th.entity-actions {
+  width: 10%;
+}
+
+.entity-actions a:hover,
+.entity-actions a:visited:hover,
+.entity-actions a:focus:hover {
+  text-decoration: none;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/styles/user-management.css
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/styles/user-management.css b/ambari-admin/src/main/resources/ui/admin-web/app/styles/user-management.css
new file mode 100644
index 0000000..77c94ac
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/styles/user-management.css
@@ -0,0 +1,30 @@
+/**
+ * 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.
+ */
+
+#user-management .table th {
+  vertical-align: baseline;
+}
+
+#user-management .nav.nav-tabs {
+  margin-bottom: 0;
+}
+
+#user-management .users-pane,
+#user-management .groups-pane {
+  margin-top: -35px;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/styles/views.css
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/styles/views.css b/ambari-admin/src/main/resources/ui/admin-web/app/styles/views.css
index 58583de..9bb84df 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/styles/views.css
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/styles/views.css
@@ -16,24 +16,6 @@
  * limitations under the License.
  */
 
-.view-instance-actions a {
-  color: inherit;
-  font-size: 16px;
-  cursor: pointer;
-  padding: 0 5px;
-}
-
-td.view-instance-actions,
-th.view-instance-actions {
-  width: 10%;
-}
-
-.view-instance-actions a:hover,
-.view-instance-actions a:visited:hover,
-.view-instance-actions a:focus:hover {
-  text-decoration: none;
-}
-
 #create-instance-form i {
   cursor: pointer;
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/viewsList.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/viewsList.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/viewsList.html
index 2ff0fc4..04901f1 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/viewsList.html
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/viewsList.html
@@ -29,24 +29,24 @@
     <table class="table table-striped table-hover">
         <thead>
         <tr class="fix-bottom">
-            <th class="fix-bottom col-md-2">
+            <th class="col-md-2">
                 <span>{{'common.name' | translate}}</span>
             </th>
-            <th class="fix-bottom col-md-3">
+            <th class="col-md-3">
                 <span>{{'urls.url' | translate}}</span>
             </th>
-            <th class="fix-bottom col-md-2">
+            <th class="col-md-2">
                 <span>{{'views.table.viewType' | translate}}</span>
             </th>
-            <th class="fix-bottom col-md-2">
+            <th class="col-md-2">
                 <span>{{'urls.viewInstance' | translate}}</span>
             </th>
-            <th class="fix-bottom col-md-2 view-instance-actions">
+            <th class="col-md-2 entity-actions">
                 <span>{{'common.actions' | translate}}</span>
             </th>
         </tr>
-        <tr>
-            <th class="fix-top">
+        <tr class="fix-top">
+            <th>
                 <div class="search-container">
                     <input type="text" class="form-control" placeholder="{{'common.any' | translate}}"
                            ng-model="instanceNameFilter" ng-change="filterInstances()">
@@ -57,7 +57,7 @@
                     </button>
                 </div>
             </th>
-            <th class="fix-top">
+            <th>
                 <div class="search-container">
                     <input type="text" class="form-control" placeholder="{{'common.any' | translate}}"
                            ng-model="instanceUrlFilter" ng-change="filterInstances()">
@@ -68,17 +68,15 @@
                     </button>
                 </div>
             </th>
-            <th class="fix-top">
+            <th>
                 <select class="form-control typefilter v-small-input"
                         ng-model="instanceTypeFilter"
                         ng-options="item.label for item in typeFilterOptions"
                         ng-change="filterInstances()">
                 </select>
             </th>
-            <th class="fix-top">
-            </th>
-            <th class="fix-top">
-            </th>
+            <th></th>
+            <th></th>
         </tr>
         </thead>
 
@@ -98,7 +96,7 @@
             <td>
                 <span>{{instance.instance_name}}</span>
             </td>
-            <td class="view-instance-actions" ng-switch="instance.versionObj.status">
+            <td class="entity-actions" ng-switch="instance.versionObj.status">
                 <span ng-switch-when="PENDING">
                     <i class="viewstatus pending"></i>
                     {{'views.pending' | translate}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/clusterInformation.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/clusterInformation.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/clusterInformation.html
deleted file mode 100644
index ead73c3..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/views/clusterInformation.html
+++ /dev/null
@@ -1,87 +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.
--->
-
-<div id="cluster-information">
-  <div ng-show="cluster.Clusters.provisioning_state !== 'INSTALLED'">
-    <div class="welcome-header">
-      <h1>{{'main.title' | translate}}</h1>
-      <span>{{'main.noClusterDescription' | translate}}</span>
-    </div>
-    <div class="create-cluster-section">
-      <h2>{{'main.createCluster.title' | translate}}</h2>
-      <div>
-        <span>
-          {{'main.createCluster.description' | translate}}
-        </span>
-      </div>
-      <div><i class="fa fa-cloud" aria-hidden="true"></i></div>
-      <div>
-        <a href="{{fromSiteRoot('/#/installer/step0')}}" class="btn btn-primary">
-          {{'main.createCluster.launchInstallWizard' | translate}}
-        </a>
-      </div>
-    </div>
-  </div>
-
-  <div ng-show="cluster.Clusters.provisioning_state === 'INSTALLED'">
-    <form class="row" name="editClusterNameForm" ng-submit="confirmClusterNameChange()">
-      <div class="form-group col-xs-4 cluster-name"
-           ng-class="{'has-error': editClusterNameForm.clusterName.$invalid}">
-        <label for="clusterName">{{'views.clusterName' | translate}}*</label>
-        <input type="text"
-               class="form-control"
-               id="clusterName"
-               name="clusterName"
-               ng-change="toggleSaveButton()"
-               ng-model="edit.clusterName"
-               required
-               autofocus
-               ng-pattern="/^\w*$/"
-               ng-maxlength="80"
-               tooltip="{{'common.renameClusterTip' | translate}}"
-               tooltip-trigger="focus"
-               tooltip-placement="bottom"
-               ng-class="{edited: isClusterNameEdited}">
-        <button
-          type="submit"
-          ng-class="{'disabled': editClusterNameForm.clusterName.$invalid}"
-          class="btn btn-default pull-right"
-          ng-show="isClusterNameEdited">
-          {{'common.controls.save' | translate}}
-        </button>
-      </div>
-    </form>
-    <div>
-      <div class="row dev-blueprint">
-        <div class="col-sm-11"><span>{{'clusters.devBlueprint' | translate}}</span></div>
-        <div class="col-sm-1">
-          <div class="btn btn-default pull-right" ng-click="downloadBlueprint()">{{"common.download" | translate}}
-          </div>
-        </div>
-      </div>
-      <textarea type="text"
-                rows="20"
-                class="form-control"
-                name="blueprint_text"
-                ng-model="blueprint"
-                ng-disabled="true"
-                ng-readonly="true">
-      </textarea>
-    </div>
-  </div>
-</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/clusters/clusterInformation.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/clusters/clusterInformation.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/clusters/clusterInformation.html
new file mode 100644
index 0000000..ead73c3
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/clusters/clusterInformation.html
@@ -0,0 +1,87 @@
+<!--
+* 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.
+-->
+
+<div id="cluster-information">
+  <div ng-show="cluster.Clusters.provisioning_state !== 'INSTALLED'">
+    <div class="welcome-header">
+      <h1>{{'main.title' | translate}}</h1>
+      <span>{{'main.noClusterDescription' | translate}}</span>
+    </div>
+    <div class="create-cluster-section">
+      <h2>{{'main.createCluster.title' | translate}}</h2>
+      <div>
+        <span>
+          {{'main.createCluster.description' | translate}}
+        </span>
+      </div>
+      <div><i class="fa fa-cloud" aria-hidden="true"></i></div>
+      <div>
+        <a href="{{fromSiteRoot('/#/installer/step0')}}" class="btn btn-primary">
+          {{'main.createCluster.launchInstallWizard' | translate}}
+        </a>
+      </div>
+    </div>
+  </div>
+
+  <div ng-show="cluster.Clusters.provisioning_state === 'INSTALLED'">
+    <form class="row" name="editClusterNameForm" ng-submit="confirmClusterNameChange()">
+      <div class="form-group col-xs-4 cluster-name"
+           ng-class="{'has-error': editClusterNameForm.clusterName.$invalid}">
+        <label for="clusterName">{{'views.clusterName' | translate}}*</label>
+        <input type="text"
+               class="form-control"
+               id="clusterName"
+               name="clusterName"
+               ng-change="toggleSaveButton()"
+               ng-model="edit.clusterName"
+               required
+               autofocus
+               ng-pattern="/^\w*$/"
+               ng-maxlength="80"
+               tooltip="{{'common.renameClusterTip' | translate}}"
+               tooltip-trigger="focus"
+               tooltip-placement="bottom"
+               ng-class="{edited: isClusterNameEdited}">
+        <button
+          type="submit"
+          ng-class="{'disabled': editClusterNameForm.clusterName.$invalid}"
+          class="btn btn-default pull-right"
+          ng-show="isClusterNameEdited">
+          {{'common.controls.save' | translate}}
+        </button>
+      </div>
+    </form>
+    <div>
+      <div class="row dev-blueprint">
+        <div class="col-sm-11"><span>{{'clusters.devBlueprint' | translate}}</span></div>
+        <div class="col-sm-1">
+          <div class="btn btn-default pull-right" ng-click="downloadBlueprint()">{{"common.download" | translate}}
+          </div>
+        </div>
+      </div>
+      <textarea type="text"
+                rows="20"
+                class="form-control"
+                name="blueprint_text"
+                ng-model="blueprint"
+                ng-disabled="true"
+                ng-readonly="true">
+      </textarea>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/clusters/manageAccess.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/clusters/manageAccess.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/clusters/manageAccess.html
deleted file mode 100644
index a399eff..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/views/clusters/manageAccess.html
+++ /dev/null
@@ -1,63 +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.
--->
-    
-<div class="cluster-manage-access-pane">
-  <div class="clearfix">
-    <ol class="breadcrumb pull-left">
-      <li class="active">{{clusterName}} {{'common.roles' | translate}}</li>
-    </ol>
-  </div>
-  <hr>
-  <div class="pull-right">
-    <div class="layout-switch-icon-wrapper">
-      <i class="glyphicon glyphicon-th-large layout-switch-icon"></i>
-      <p class="label-block">{{'common.blockViewLabel' | translate}}</p>
-    </div>
-    <div class="layout-switch-icon-wrapper disabled">
-      <i class="glyphicon glyphicon-list layout-switch-icon" ng-click="switchToList()" tooltip-html-unsafe="{{'clusters.switchToList' | translate}}"></i>
-      <p class="label-list">{{'common.listViewLabel' | translate}}</p>
-    </div>
-  </div>
-  <table class="table">
-    <thead>
-      <tr>
-        <th class="col-sm-2" width="20%">
-          <label>{{'common.roles' | translate}}</label>&nbsp;
-          <i class="glyphicon glyphicon-question-sign green-icon cursor-pointer" ng-click="showHelpPage()"></i>
-        </th>
-        <th class="col-sm-5" width="40%"><label>{{'clusters.assignRoles' | translate: '{term: getConstant("common.users")}'}}</label></th>
-        <th class="col-sm-5" width="40%"><label>{{'clusters.assignRoles' | translate: '{term: getConstant("common.groups")}'}}</label></th>
-      </tr>
-    </thead>
-    <tbody>
-      <tr ng-repeat="permission in permissions">
-        <td><label class="" tooltip="{{permission.PermissionInfo.permission_name}}">{{permission.PermissionInfo.permission_label}}</label></td>
-        <td>
-          <div class="" ng-switch="isEditMode">
-            <editable-list items-source="permissionsEdit[permission.PermissionInfo.permission_name].USER" resource-type="User" editable="true"></editable-list>
-          </div>
-        </td>
-        <td>
-          <div class="" ng-switch="isEditMode">
-            <editable-list items-source="permissionsEdit[permission.PermissionInfo.permission_name].GROUP" resource-type="Group" editable="true"></editable-list>
-          </div>
-        </td>
-      </tr>
-    </tbody>
-  </table>
-</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/clusters/userAccessList.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/clusters/userAccessList.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/clusters/userAccessList.html
deleted file mode 100644
index 8b29157..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/views/clusters/userAccessList.html
+++ /dev/null
@@ -1,102 +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.
--->
-
-<div class="users-pane">
-  <div class="clearfix">
-    <ol class="breadcrumb pull-left">
-      <li class="active">{{clusterId}} {{'common.roles' | translate}}</li>
-    </ol>
-  </div>
-  <hr>
-  <div class="pull-right">
-    <div class="layout-switch-icon-wrapper disabled">
-      <i class="glyphicon glyphicon-th-large layout-switch-icon" ng-click="switchToBlock()" tooltip-html-unsafe="{{'clusters.switchToBlock' | translate}}"></i>
-      <p class="label-block">{{'common.blockViewLabel' | translate}}</p>
-    </div>
-    <div class="layout-switch-icon-wrapper">
-      <i class="glyphicon glyphicon-list layout-switch-icon"></i>
-      <p class="label-list">{{'common.listViewLabel' | translate}}</p>
-    </div>
-  </div>
-  <ul class="nav nav-pills">
-    <li ng-class="{'active': isUserActive}"><a ng-click="switchToUser()">{{'common.users' | translate}}</a></li>
-    <li ng-class="{'active': !isUserActive}"><a ng-click="switchToGroup()">{{'common.groups' | translate}}</a></li>
-  </ul>
-  <br/>
-  <table class="table table-striped table-hover">
-    <thead>
-    <tr>
-      <th class="role-name-column">
-        <div class="search-container">
-          <label for="">{{'common.name' | translate}}</label>
-          <input type="text" class="form-control namefilter" placeholder="{{'common.any' | translate}}" ng-model="currentNameFilter" ng-change="resetPagination()">
-          <button type="button" class="close clearfilter" ng-show="currentNameFilter" ng-click="currentNameFilter=''; resetPagination()">
-            <span aria-hidden="true">&times;</span><span class="sr-only">{{'common.controls.close' | translate}}</span>
-          </button>
-        </div>
-      </th>
-      <th>
-        <label for="">{{'clusters.role' | translate}}</label>&nbsp;
-        <i class="glyphicon glyphicon-question-sign green-icon cursor-pointer" ng-click="showHelpPage()"></i>
-        <select class="form-control statusfilter"
-                ng-model="currentRoleFilter"
-                ng-options="item.label for item in roleFilterOptions"
-                ng-change="resetPagination()">
-        </select>
-      </th>
-    </tr>
-    </thead>
-    <tbody>
-    <tr ng-repeat="user in users">
-      <td>
-        <a href="#/{{user.url}}">{{user.principal_name}}</a>
-      </td>
-      <td>
-        <div ng-show="!user.editable">{{user.permission_label}}</div>
-        <select class="form-control role-select" ng-show="user.editable" ng-model="user.permission_name"
-                ng-options="role.permission_name as role.permission_label for role in roleValueOptions">
-        </select>
-        <span ng-show="user.principal_type != $parent.currentTypeFilter.value">&nbsp;{{'common.fromGroupMark' | translate}}</span>
-        <span ng-show="user.permission_name != user.original_perm">
-          <button class="btn btn-default btn-xs cancel" ng-click="cancel(user)">
-            <span class="glyphicon glyphicon-remove cancel"></span>
-          </button>
-          <button class="btn btn-primary btn-xs" ng-click="save(user)">
-            <span class="glyphicon glyphicon-ok"></span>
-          </button>
-        </span>
-      </td>
-    </tr>
-    </tbody>
-  </table>
-  <div class="alert alert-info col-sm-12" ng-show="!users.length">
-    {{'common.alerts.nothingToDisplay' | translate: '{term: (isUserActive ? constants.users : constants.groups)}'}}
-  </div>
-  <div class="col-sm-12 table-bar">
-    <div class="pull-left filtered-info">
-      <span>{{'common.filterInfo' | translate: '{showed: tableInfo.showed, total: tableInfo.total, term: (isUserActive ? constants.users : constants.groups)}'}}</span>
-      <span ng-show="isNotEmptyFilter">- <a href ng-click="clearFilters()">{{'common.controls.clearFilters' | translate}}</a></span>
-    </div>
-    <div class="pull-right left-margin">
-      <pagination class="paginator" total-items="totalUsers" max-size="maxVisiblePages" items-per-page="usersPerPage" ng-model="currentPage" ng-change="pageChanged()"></pagination>
-    </div>
-    <div class="pull-right">
-      <select class="form-control" ng-model="usersPerPage" ng-change="usersPerPageChanges()" ng-options="currOption for currOption in [10, 25, 50, 100]"></select>
-    </div>
-  </div>
-</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/groups/create.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/groups/create.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/groups/create.html
deleted file mode 100644
index 9a0e0fb..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/views/groups/create.html
+++ /dev/null
@@ -1,43 +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.
--->
-<ol class="breadcrumb">
-  <li><a href="#/groups">{{'common.groups' | translate}}</a></li>
-  <li class="active">{{'groups.createLocal' | translate}}</li>
-</ol>
-<hr>
-<form class="form-horizontal" role="form" novalidate name="form" autocomplete="off">
-  <div class="form-group" ng-class="{'has-error' : (form.group_name.$error.required || form.group_name.$error.pattern) && form.submitted}">
-    <label for="groupname" class="col-sm-2 control-label">{{'groups.name' | translate}}</label>
-    <div class="col-sm-10">
-      <input type="text" id="groupname" class="form-control groupname-input" name="group_name" placeholder="{{'groups.name' | translate}}" ng-model="group.group_name" required ng-pattern="/^([a-zA-Z0-9._\s]+)$/" autocomplete="off">
-      <div class="alert alert-danger top-margin" ng-show="form.group_name.$error.required && form.submitted">
-        {{'common.alerts.fieldIsRequired' | translate}}
-      </div>
-      <div class="alert alert-danger top-margin" ng-show="form.group_name.$error.pattern && form.submitted">
-        {{'common.alerts.onlySimpleChars' | translate}}
-      </div>
-    </div>
-  </div>
-  <div class="form-group">
-    <div class="col-sm-offset-2 col-sm-10">
-      <button class="btn btn-primary groupcreate-btn pull-right left-margin" ng-click="createGroup()">{{'common.controls.save' | translate}}</button>
-      <a href ng-click="cancel()" class="btn btn-default pull-right cancel-button">{{'common.controls.cancel' | translate}}</a>
-    </div>
-  </div>
-      
-</form>

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/groups/edit.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/groups/edit.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/groups/edit.html
deleted file mode 100644
index 1aafd03..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/views/groups/edit.html
+++ /dev/null
@@ -1,98 +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.
--->
-  
-<div class="clearfix">
-  <ol class="breadcrumb pull-left">
-    <li><a href="#/groups">{{'common.groups' | translate}}</a></li>
-    <li class="active">{{group.group_name}}</li>
-  </ol>
-  <div class="pull-right top-margin-4">
-  <div ng-switch="group.group_type != 'LOCAL'">
-      <button ng-switch-when="true" class="btn disabled deletegroup-btn deleteuser-btn" tooltip="{{'common.cannotDelete' | translate: '{term: constants.group}'}}">{{'common.delete' | translate: '{term: constants.group}'}}</button>
-      <button ng-switch-when="false" class="btn btn-danger deletegroup-btn" ng-click="deleteGroup(group)">{{'common.delete' | translate: '{term: constants.group}'}}</button>
-    </div>
-      
-  </div>
-</div>
-<hr>
-<form class="form-horizontal group-edit" role="form" novalidate name="form" >
-  <div class="form-group">
-    <label for="" class="col-sm-2 control-label">{{'common.type' | translate}}</label>
-    <div class="col-sm-10">
-      <label for="" class="control-label">{{group.groupTypeName | translate}}</label>
-    </div>
-  </div>
-  <div class="form-group">
-    <label for="members" class="col-sm-2 control-label">{{group.groupTypeName | translate}} {{'groups.members' | translate}}</label>
-    <div class="col-sm-10">
-      <editable-list items-source="group.editingUsers" resource-type="User" editable="group.group_type == 'LOCAL'"></editable-list>
-    </div>
-  </div>
-
-  <div class="form-group">
-      <label for="" class="col-sm-2 control-label">{{'common.privileges' | translate}}</label>
-      <div class="col-sm-10">
-        <table class="table" ng-hide="hidePrivileges">
-          <thead>
-            <tr>
-              <th>{{'common.cluster' | translate}}</th>
-              <th>{{'common.clusterRole' | translate}}</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr ng-repeat="(name, privilege) in privileges.clusters">
-              <td>
-                <span class="glyphicon glyphicon-cloud"></span> 
-                <a href="#/clusters/{{name}}/manageAccess">{{name}}</a>
-              </td>
-              <td>
-                <span tooltip="{{item}}" ng-repeat="item in privilege">{{item | translate}}{{$last ? '' : ', '}}</span>
-              </td>
-            </tr>
-            <tr>
-              <td ng-show="noClusterPriv">{{'common.alerts.noPrivileges' | translate: '{term: constants.cluster}'}}</td>
-            </tr>
-          </tbody>
-          <thead class="view-permission-header">
-            <tr>
-              <th>{{'common.view' | translate}}</th>
-              <th>{{'common.viewPermissions' | translate}}</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr ng-repeat="(name, privilege) in privileges.views">
-              <td>
-                <span class="glyphicon glyphicon-th"></span>
-                <a href="#/views/{{privilege.view_name}}/versions/{{privilege.version}}/instances/{{name}}/edit">{{name}}</a>
-              </td>
-              <td>
-                <span tooltip="{{item}}" ng-repeat="item in privilege.privileges">{{item | translate}}{{$last ? '' : ', '}}</span>
-              </td>
-              <td>
-                <i class="fa fa-trash-o" aria-hidden="true" ng-click="removePrivilege(name, privilege);"></i>
-              </td>
-            </tr>
-            <tr>
-              <td ng-show="noViewPriv">{{'common.alerts.noPrivileges' | translate: '{term: constants.view}'}}</td>
-            </tr>
-          </tbody>
-        </table>
-        <div class="alert alert-info hide-soft" ng-class="{'visible' : !privileges}">{{'common.alerts.noPrivilegesDescription' | translate: '{term: constants.group.toLowerCase()}'}}</div>
-      </div>
-    </div>
-</form>

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/groups/list.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/groups/list.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/groups/list.html
deleted file mode 100644
index b39f55d..0000000
--- a/ambari-admin/src/main/resources/ui/admin-web/app/views/groups/list.html
+++ /dev/null
@@ -1,80 +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.
--->
-<div class="groups-pane">
-  <div class="clearfix">
-    <ol class="breadcrumb pull-left">
-      <li class="active">{{'common.groups' | translate}}</li>
-    </ol>
-    <div class="pull-right top-margin-4">
-      <link-to route="groups.create" class="btn btn-default creategroup-btn">
-        {{'groups.createLocal' | translate}}
-      </link-to>
-    </div>
-  </div>
-  <table class="table table-striped table-hover col-sm-12">
-    <thead>
-      <tr>
-        <th class="col-sm-8">
-          <div class="search-container">
-            <label for="">{{'groups.name' | translate}}</label>
-            <input type="text" class="form-control namefilter" placeholder="{{'common.any' | translate}}" ng-model="currentNameFilter" ng-change="resetPagination()">
-            <button type="button" class="close" ng-show="currentNameFilter" ng-click="currentNameFilter=''; resetPagination()"><span aria-hidden="true">&times;</span><span class="sr-only">{{'common.controls.close' | translate}}</span></button>
-          </div>
-        </th>
-        <th class="col-sm-2">
-          <label for="">{{'common.type' | translate}}</label>
-          <select class="form-control typefilter"
-            ng-model="currentTypeFilter"
-            ng-options="item.label for item in typeFilterOptions"
-            ng-change="resetPagination();">
-          </select>
-        </th>
-        <th class="col-sm-2 vertical-top">
-          <label for="">{{'groups.members' | translate}}</label>
-        </th>
-      </tr>
-    </thead>
-    <tbody>
-      <tr ng-repeat="group in groups">
-        <td class="col-sm-8">
-          <link-to route="groups.edit" class="link-to-group" id="{{group.group_name}}">{{group.group_name}}</link-to>
-        </td>
-        <td class="col-sm-2">{{group.groupTypeName | translate}}</td>
-        <td class="col-sm-2">{{'groups.membersPlural' | translate: '{n: group.members && group.members.length || 0}'}}</td>
-      </tr>
-    </tbody>
-  </table>
-  <div ng-if="isLoading" class="spinner-container">
-    <i class="fa fa-2x fa-spinner fa-spin" aria-hidden="true"></i>
-  </div>
-  <div class="alert empty-table-alert col-sm-12" ng-show="!groups.length && !isLoading">
-    {{'common.alerts.nothingToDisplay' | translate: '{term: constants.groups}'}}
-  </div>
-  <div class="col-sm-12 table-bar">
-    <div class="pull-left filtered-info">
-      <span>{{'common.filterInfo' | translate: '{showed: tableInfo.showed, total: tableInfo.total, term: constants.groups}'}}</span>
-      <span ng-show="isNotEmptyFilter">- <a href ng-click="clearFilters()">{{'common.controls.clearFilters' | translate}}</a></span>
-    </div>
-    <div class="pull-right left-margin">
-      <pagination class="paginator" total-items="totalGroups" max-size="maxVisiblePages" items-per-page="groupsPerPage" ng-model="currentPage" ng-change="pageChanged()"></pagination>
-    </div>
-    <div class="pull-right">
-      <select class="form-control" ng-model="groupsPerPage" ng-change="groupsPerPageChanges()" ng-options="currOption for currOption in [10, 25, 50, 100]"></select>
-    </div>
-  </div>
-</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/sideNav.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/sideNav.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/sideNav.html
index 97dc5d3..98a95cd 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/views/sideNav.html
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/sideNav.html
@@ -59,25 +59,11 @@
           </li>
         </ul>
       </li>
-      <li class="mainmenu-li dropdown has-sub-menu">
-        <a title="{{'common.userManagement' | translate}}" rel="tooltip" data-placement="right" data-toggle="collapse-sub-menu">
-          <span class="toggle-icon glyphicon glyphicon-menu-down pull-right"></span>
+      <li class="mainmenu-li" ng-class="{active: isActive('userManagement.main')}">
+        <link-to route="userManagement.main" class="userslist-link" title="{{'common.users' | translate}}" rel="tooltip" data-placement="right">
           <i class="navigation-icon fa fa-users" aria-hidden="true"></i>
-          <span class="navigation-menu-item">{{'common.userManagement' | translate}}</span>
-        </a>
-        <ul class="sub-menu nav nav-pills nav-stacked">
-          <li class="submenu-li"
-              ng-class="{active: isActive('clusters.manageAccess') || isActive('clusters.userAccessList')}"
-              ng-show="cluster.Clusters.provisioning_state === 'INSTALLED'">
-            <a href="#/clusters/{{cluster.Clusters.cluster_name}}/manageAccess" class="roles">{{'common.roles' | translate}}</a>
-          </li>
-          <li class="submenu-li" ng-class="{active: isActive('users.list')}">
-            <link-to route="users.list" class="userslist-link">{{'common.users' | translate}}</link-to>
-          </li>
-          <li class="submenu-li" ng-class="{active: isActive('groups.list')}">
-            <link-to route="groups.list" class="groupslist-link">{{'common.groups' | translate}}</link-to>
-          </li>
-        </ul>
+          <span class="navigation-menu-item">{{'common.users' | translate}}</span>
+        </link-to>
       </li>
       <li class="mainmenu-li" ng-class="{active: isActive('views.list')}">
         <link-to route="views.list" class="viewslist-link" title="{{'common.views' | translate}}" rel="tooltip" data-placement="right">

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/groupEdit.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/groupEdit.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/groupEdit.html
new file mode 100644
index 0000000..90a1907
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/groupEdit.html
@@ -0,0 +1,99 @@
+<!--
+* 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.
+-->
+
+<div class="clearfix">
+  <div class="pull-right">
+    <div ng-switch="group.group_type != 'LOCAL'">
+      <button
+        ng-switch-when="true"
+        class="btn disabled deletegroup-btn"
+        tooltip="{{'common.cannotDelete' | translate: '{term: constants.group}'}}">
+        {{'common.delete' | translate: '{term: constants.group}'}}
+      </button>
+      <button ng-switch-when="false" class="btn btn-danger deletegroup-btn" ng-click="deleteGroup(group)">
+        {{'common.delete' | translate: '{term: constants.group}'}}
+      </button>
+    </div>
+  </div>
+</div>
+<form class="form-horizontal group-edit" role="form" novalidate name="form" >
+  <div class="form-group">
+    <label class="col-sm-2 control-label">{{'common.type' | translate}}</label>
+    <div class="col-sm-10">
+      <label class="control-label">{{group.groupTypeName | translate}}</label>
+    </div>
+  </div>
+  <div class="form-group">
+    <label class="col-sm-2 control-label">{{group.groupTypeName | translate}} {{'groups.members' | translate}}</label>
+    <div class="col-sm-10">
+      <editable-list items-source="group.editingUsers" resource-type="User" editable="group.group_type == 'LOCAL'"></editable-list>
+    </div>
+  </div>
+
+  <div class="form-group">
+      <label class="col-sm-2 control-label">{{'common.privileges' | translate}}</label>
+      <div class="col-sm-10">
+        <table class="table" ng-hide="hidePrivileges">
+          <thead>
+            <tr>
+              <th>{{'common.cluster' | translate}}</th>
+              <th>{{'common.clusterRole' | translate}}</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr ng-repeat="(name, privilege) in privileges.clusters">
+              <td>
+                <span class="glyphicon glyphicon-cloud"></span> 
+                <a href="#/clusters/{{name}}/manageAccess">{{name}}</a>
+              </td>
+              <td>
+                <span tooltip="{{item}}" ng-repeat="item in privilege">{{item | translate}}{{$last ? '' : ', '}}</span>
+              </td>
+            </tr>
+            <tr>
+              <td ng-show="noClusterPriv">{{'common.alerts.noPrivileges' | translate: '{term: constants.cluster}'}}</td>
+            </tr>
+          </tbody>
+          <thead class="view-permission-header">
+            <tr>
+              <th>{{'common.view' | translate}}</th>
+              <th>{{'common.viewPermissions' | translate}}</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr ng-repeat="(name, privilege) in privileges.views">
+              <td>
+                <span class="glyphicon glyphicon-th"></span>
+                <a href="#/views/{{privilege.view_name}}/versions/{{privilege.version}}/instances/{{name}}/edit">{{name}}</a>
+              </td>
+              <td>
+                <span tooltip="{{item}}" ng-repeat="item in privilege.privileges">{{item | translate}}{{$last ? '' : ', '}}</span>
+              </td>
+              <td>
+                <i class="fa fa-trash-o" aria-hidden="true" ng-click="removePrivilege(name, privilege);"></i>
+              </td>
+            </tr>
+            <tr>
+              <td ng-show="noViewPriv">{{'common.alerts.noPrivileges' | translate: '{term: constants.view}'}}</td>
+            </tr>
+          </tbody>
+        </table>
+        <div class="alert alert-info hide-soft" ng-class="{'visible' : !privileges}">{{'common.alerts.noPrivilegesDescription' | translate: '{term: constants.group.toLowerCase()}'}}</div>
+      </div>
+    </div>
+</form>

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/groupsList.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/groupsList.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/groupsList.html
new file mode 100644
index 0000000..d79d14e
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/groupsList.html
@@ -0,0 +1,94 @@
+<!--
+* 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.
+-->
+<div class="groups-pane">
+  <div class="clearfix panel">
+    <button class="btn btn-default creategroup-btn pull-right" ng-click="createGroup()">
+      {{'groups.createLocal' | translate}}
+    </button>
+  </div>
+  <table class="table table-striped table-hover col-sm-12">
+    <thead>
+      <tr>
+        <th class="col-sm-6">
+          <span>{{'groups.name' | translate}}</span>
+        </th>
+        <th class="col-sm-2">
+          <span>{{'common.type' | translate}}</span>
+        </th>
+        <th class="col-sm-2">
+          <span>{{'groups.members' | translate}}</span>
+        </th>
+        <th class="col-sm-2">
+          <span>{{'common.actions' | translate}}</span>
+        </th>
+      </tr>
+      <tr>
+        <th class="col-sm-6">
+          <div class="search-container">
+            <input type="text" class="form-control namefilter" placeholder="{{'common.any' | translate}}" ng-model="filter.name" ng-change="resetPagination()">
+            <button type="button" class="close" ng-show="filter.name" ng-click="filter.name=''; resetPagination()"><span aria-hidden="true">&times;</span><span class="sr-only">{{'common.controls.close' | translate}}</span></button>
+          </div>
+        </th>
+        <th class="col-sm-2">
+          <select class="form-control typefilter"
+                  ng-model="filter.type"
+                  ng-options="item.label for item in typeFilterOptions"
+                  ng-change="resetPagination();">
+          </select>
+        </th>
+        <th class="col-sm-2"></th>
+        <th class="col-sm-2"></th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr ng-repeat="group in groups">
+        <td class="col-sm-8">
+          <span>{{group.group_name}}</span>
+        </td>
+        <td class="col-sm-2">{{group.groupTypeName | translate}}</td>
+        <td class="col-sm-2">{{'groups.membersPlural' | translate: '{n: group.members && group.members.length || 0}'}}</td>
+        <td class="entity-actions">
+          <link-to route="userManagement.editGroup" class="link-to-group" id="{{group.group_name}}">
+            <i class="fa fa-pencil"></i>
+          </link-to>
+          <a href ng-click="deleteGroup(group)">
+            <i class="fa fa-trash-o"></i>
+          </a>
+        </td>
+      </tr>
+    </tbody>
+  </table>
+  <div ng-if="isLoading" class="spinner-container">
+    <i class="fa fa-2x fa-spinner fa-spin" aria-hidden="true"></i>
+  </div>
+  <div class="alert empty-table-alert col-sm-12" ng-show="!groups.length && !isLoading">
+    {{'common.alerts.nothingToDisplay' | translate: '{term: constants.groups}'}}
+  </div>
+  <div class="col-sm-12 table-bar" ng-show="totalGroups > minRowsToShowPagination">
+    <div class="pull-left filtered-info">
+      <span>{{'common.filterInfo' | translate: '{showed: tableInfo.showed, total: tableInfo.total, term: constants.groups}'}}</span>
+      <span ng-show="isNotEmptyFilter">- <a href ng-click="clearFilters()">{{'common.controls.clearFilters' | translate}}</a></span>
+    </div>
+    <div class="pull-right left-margin">
+      <pagination class="paginator" total-items="totalGroups" max-size="maxVisiblePages" items-per-page="groupsPerPage" ng-model="currentPage" ng-change="pageChanged()"></pagination>
+    </div>
+    <div class="pull-right">
+      <select class="form-control" ng-model="groupsPerPage" ng-change="groupsPerPageChanges()" ng-options="currOption for currOption in [10, 25, 50, 100]"></select>
+    </div>
+  </div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/main.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/main.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/main.html
new file mode 100644
index 0000000..079eefb
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/main.html
@@ -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.
+-->
+
+<div id="user-management">
+  <ul class="nav nav-tabs">
+    <li ng-class="{active: activeTab === 'USERS'}">
+      <a ng-click="activeTab = 'USERS'">{{'common.users' | translate}}</a>
+    </li>
+    <li ng-class="{active: activeTab === 'GROUPS'}">
+      <a ng-click="activeTab = 'GROUPS'">{{'common.groups' | translate}}</a>
+    </li>
+  </ul>
+  <div>
+    <div class="users" ng-if="activeTab === 'USERS'">
+      <div ng-include="'views/userManagement/usersList.html'" ng-controller="UsersListCtrl"></div>
+    </div>
+    <div class="groups" ng-if="activeTab === 'GROUPS'">
+      <div ng-include="'views/userManagement/groupsList.html'" ng-controller="GroupsListCtrl"></div>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/99b19e58/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/modals/changePassword.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/modals/changePassword.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/modals/changePassword.html
new file mode 100644
index 0000000..f29d315
--- /dev/null
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/modals/changePassword.html
@@ -0,0 +1,46 @@
+<!--
+* 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.
+-->
+<div class="modal-header">
+  <h3 class="modal-title">{{'users.changePasswordFor' | translate: '{userName: userName}'}}</h3>
+</div>
+<div class="modal-body">
+  <form class="form-horizontal" novalidate name="form.passwordChangeForm" role="form" >
+    <div class="form-group" ng-class="{'has-error' : (form.passwordChangeForm.currentPassword.$error.required && form.passwordChangeForm.submitted)}">
+      <label for="" class="col-sm-4 control-label" >{{'users.yourPassword' | translate}}</label>
+      <div class="col-sm-8">
+        <input type="password" name="currentPassword" class="form-control bottom-margin" placeholder="{{'users.yourPassword' | translate}}" required ng-model="passwordData.currentUserPassword" autocomplete="off">
+        <div class="alert alert-danger no-margin-bottom" ng-show='form.passwordChangeForm.password.$error.required && form.passwordChangeForm.submitted'>{{'users.alerts.passwordRequired' | translate}}</div>
+      </div>
+    </div>
+    <div class="form-group no-margin-bottom" ng-class="{'has-error' : (form.passwordChangeForm.password.$error.required && form.passwordChangeForm.submitted) || form.passwordChangeForm.confirmPassword.$error.passwordVerify}">
+      <label for="" class="col-sm-4 control-label">{{'users.newPassword' | translate}}:</label>
+      <div class="col-sm-8">
+        <input type="password" class="form-control bottom-margin" name="password" placeholder="{{'users.newPassword' | translate}}" required ng-model="passwordData.password" autocomplete="off">
+        <input type="password" class="form-control bottom-margin" name="confirmPassword" placeholder="{{'users.newPasswordConfirmation' | translate}}" required ng-model="passwordData.passwordConfirmation"
+          password-verify="passwordData.password" autocomplete="off">
+        <div class="alert alert-danger no-margin-bottom" ng-show='form.passwordChangeForm.confirmPassword.$error.passwordVerify'>{{'users.alerts.wrongPassword' | translate}}</div>
+        <div class="alert alert-danger no-margin-bottom" ng-show='form.passwordChangeForm.password.$error.required && form.passwordChangeForm.submitted'>{{'users.alerts.passwordRequired' | translate}}</div>
+      </div>
+
+    </div>
+  </form>
+</div>
+<div class="modal-footer">
+  <button class="btn btn-default" ng-click="cancel()">{{'common.controls.cancel' | translate}}</button>
+  <button class="btn btn-primary" ng-click="ok()">{{'common.controls.ok' | translate}}</button>
+</div>
\ No newline at end of file