You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by nc...@apache.org on 2016/02/03 20:14:26 UTC

[12/16] ambari git commit: AMBARI-14900. Ambari Admin: Create LDAP Setup page (alexantonenko)

AMBARI-14900. Ambari Admin: Create LDAP Setup page (alexantonenko)


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

Branch: refs/heads/branch-dev-patch-upgrade
Commit: 46f6030b06dc9a1e1adfa18cd5df4d9ad7456ebc
Parents: 079a5b3
Author: Alex Antonenko <hi...@gmail.com>
Authored: Wed Feb 3 17:24:11 2016 +0200
Committer: Alex Antonenko <hi...@gmail.com>
Committed: Wed Feb 3 18:03:40 2016 +0200

----------------------------------------------------------------------
 .../authentication/AuthenticationMainCtrl.js    | 147 ++++++++++-
 .../ui/admin-web/app/scripts/i18n.config.js     |  76 ++++++
 .../resources/ui/admin-web/app/styles/main.css  |  16 +-
 .../app/views/authentication/main.html          | 250 ++++++++++++++++++-
 .../ui/admin-web/app/views/users/create.html    |   2 +-
 5 files changed, 483 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/46f6030b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/authentication/AuthenticationMainCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/authentication/AuthenticationMainCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/authentication/AuthenticationMainCtrl.js
index a746aa3..c7b7026 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/authentication/AuthenticationMainCtrl.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/authentication/AuthenticationMainCtrl.js
@@ -18,5 +18,150 @@
 'use strict';
 
 angular.module('ambariAdminConsole')
-  .controller('AuthenticationMainCtrl',['$scope', function($scope) {
+  .controller('AuthenticationMainCtrl', ['$scope', '$translate', 'Alert', function ($scope, $translate, $Alert) {
+    $scope.t = $translate.instant;
+
+    $scope.isLDAPEnabled = false;
+    $scope.connectivity = {
+      trustStore: 'default',
+      trustStoreOptions: ['default', 'custom'],
+      trustStoreType: 'jks',
+      trustStoreTypeOptions: ['jks', 'jceks', 'pkcs12']
+    };
+    $scope.attributes = {
+      detection: 'auto'
+    };
+
+    $scope.isConnectivityFormInvalid = true;
+    $scope.isAutoDetectFormInvalid = true;
+    $scope.isAttributesFormInvalid = true;
+    $scope.isTestAttributesFormInvalid = false;
+
+    $scope.isRequestRunning = false;
+
+    $scope.isConnectionTestRunning = false;
+    $scope.isConnectionTestComplete = false;
+    $scope.hasConnectionTestPassed = false;
+
+    $scope.isAttributeDetectionRunning = false;
+    $scope.isAttributeDetectionComplete = false;
+    $scope.isAttributeDetectionSuccessful = false;
+
+    $scope.isTestAttributesRunning = false;
+    $scope.isTestAttributesComplete = false;
+    $scope.isTestAttributesSuccessful = false;
+
+    $scope.isSaving = false;
+    $scope.isSavingComplete = false;
+    $scope.isSavingSuccessful = false;
+
+    $scope.isTestAttributesFormShown = false;
+
+    $scope.toggleAuthentication = function () {
+      $scope.isConnectionTestRunning = false;
+      $scope.isConnectionTestComplete = false;
+      $scope.hasConnectionTestPassed = false;
+    };
+
+    $scope.testConnection = function () {
+      $scope.isConnectionTestRunning = true;
+      $scope.isConnectionTestComplete = false;
+      $scope.isAttributeDetectionRunning = false;
+      $scope.isAttributeDetectionComplete = false;
+      $scope.isAttributeDetectionSuccessful = false;
+
+      // TODO replace mock with test connection request when API is available
+      setTimeout(function (prevValue) {
+        $scope.isConnectionTestRunning = false;
+        $scope.isConnectionTestComplete = true;
+        $scope.hasConnectionTestPassed = !prevValue;
+      }, 1000, $scope.hasConnectionTestPassed);
+      $scope.hasConnectionTestPassed = false;
+    };
+
+    $scope.detectAttributes = function () {
+      $scope.isAttributeDetectionRunning = true;
+      $scope.isAttributeDetectionComplete = false;
+
+      // TODO replace mock with attributes detection request when API is available
+      setTimeout(function (prevValue) {
+        $scope.isAttributeDetectionRunning = false;
+        $scope.isAttributeDetectionComplete = true;
+        $scope.isAttributeDetectionSuccessful = !prevValue;
+        if ($scope.isAttributeDetectionSuccessful) {
+          var form = $scope.attributes;
+          form.userObjClass = 'person';
+          form.userNameAttr = 'sAMAccountName';
+          form.groupObjClass = 'group';
+          form.groupNameAttr = 'cn';
+          form.groupMemberAttr = 'member';
+          form.distinguishedNameAttr = 'distinguishedName';
+        }
+      }, 1000, $scope.isAttributeDetectionSuccessful);
+
+      $scope.isAttributeDetectionSuccessful = false;
+    };
+
+    $scope.showTestAttributesForm = function () {
+      $scope.isTestAttributesFormShown = true;
+    };
+
+    $scope.testAttributes = function () {
+      $scope.isTestAttributesRunning = true;
+      $scope.isTestAttributesComplete = false;
+
+      // TODO replace mock with test attributes request when API is available
+      setTimeout(function (prevValue) {
+        $scope.isTestAttributesRunning = false;
+        $scope.isTestAttributesComplete = true;
+        $scope.isTestAttributesSuccessful = !prevValue;
+        if ($scope.isTestAttributesSuccessful) {
+          $scope.attributes.availableGroups = ['HadoopOps', 'HadoopOpsDFW', 'AmbariAdmins', 'ExchangeAdmins', 'AmbariUsers', 'ExchangeUsers'];
+        }
+      }, 1000, $scope.isTestAttributesSuccessful);
+      $scope.isTestAttributesSuccessful = false;
+    };
+
+    $scope.save = function () {
+      $scope.isSaving = true;
+      $scope.isSavingComplete = false;
+      // TODO replace mock with save request when API is available
+      setTimeout(function (prevValue) {
+        $scope.isSaving = false;
+        $scope.isSavingComplete = true;
+        $scope.isSavingSuccessful = !prevValue;
+        if ($scope.isSavingSuccessful) {
+          $Alert.success('Settings saved');
+        } else {
+          $Alert.error('Saving failed', '500 Error');
+        }
+      }, 1000, $scope.isSavingSuccessful);
+      $scope.isSavingSuccessful = false;
+    };
+
+    $scope.$watch('connectivity', function (form, oldForm, scope) {
+      scope.isConnectivityFormInvalid = !(form.host && form.port
+        && (form.trustStore === 'default' || form.trustStorePath && form.trustStorePassword)
+        && form.dn && form.bindPassword);
+    }, true);
+
+    $scope.$watch('attributes', function (form, oldForm, scope) {
+      scope.isAutoDetectFormInvalid = !(form.userSearch && form.groupSearch);
+      scope.isAttributesFormInvalid = !(form.userObjClass && form.userNameAttr && form.groupObjClass
+        && form.groupNameAttr && form.groupMemberAttr && form.distinguishedNameAttr
+        && (form.detection === 'auto' || form.userSearchManual && form.groupSearchManual));
+      scope.isTestAttributesFormInvalid = !(form.username && form.password);
+    }, true);
+
+    $scope.$watch('attributes.detection', function (newValue, oldValue, scope) {
+      scope.isTestAttributesFormShown = false;
+      scope.isAttributeDetectionComplete = false;
+      scope.isAttributeDetectionSuccessful = false;
+    });
+
+    $scope.$watch(function (scope) {
+      return scope.isConnectionTestRunning || scope.isAttributeDetectionRunning || scope.isTestAttributesRunning || scope.isSaving;
+    }, function (newValue, oldValue, scope) {
+      scope.isRequestRunning = newValue;
+    });
 }]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/46f6030b/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 c1b9d88..086bc13 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
@@ -320,6 +320,82 @@ angular.module('ambariAdminConsole')
         'versionUpdateError': 'Version update error',
         'versionDeleteError': 'Version delete error'
       }
+    },
+
+    'authentication': {
+      'description': 'Ambari supports authenticating against local Ambari users created and stored in the Ambari Database, or authenticating against a LDAP server:',
+      'ldap': 'LDAP Authentication',
+      'on': 'On',
+      'off': 'Off',
+
+      'connectivity': {
+        'title': 'LDAP Connectivity Configuration',
+        'host': 'LDAP Server Host',
+        'port': 'LDAP Server Port',
+        'ssl': 'Use SSL?',
+        'trustStore': {
+          'label': 'Trust Store',
+          'options': {
+            'default': 'JDK Default',
+            'custom': 'Custom'
+          }
+        },
+        'trustStorePath': 'Trust Store Path',
+        'trustStoreType': {
+          'label': 'Trust Store Type',
+          'options': {
+            'jks': 'JKS',
+            'jceks': 'JCEKS',
+            'pkcs12': 'PKCS12'
+          }
+        },
+        'trustStorePassword': 'Trust Store Password',
+        'dn': 'Bind DN',
+        'bindPassword': 'Bind Password',
+
+        'controls': {
+          'testConnection': 'Test Connection'
+        }
+      },
+
+      'attributes': {
+        'title': 'LDAP Attribute Configuration',
+        'detection': {
+          'label': 'Identifying the proper attributes to be used when authenticating and looking up users and groups can be specified manually, or automatically detected. Please choose:',
+          'options': {
+            'manual': 'Define Attributes Manually',
+            'auto': 'Auto-Detect Attributes'
+          }
+        },
+        'userSearch': 'User Search Base',
+        'groupSearch': 'Group Search Base',
+        'detected': 'The following attributes were detected, please review and Test Attributes to ensure their accuracy.',
+        'userObjClass': 'User Object Class',
+        'userNameAttr': 'User Name Attribute',
+        'groupObjClass': 'Group Object Class',
+        'groupNameAttr': 'Group Name Attribute',
+        'groupMemberAttr': 'Group Member Attribute',
+        'distinguishedNameAttr': 'Distinguished Name Attribute',
+        'test': {
+          'description': 'To quickly test the chosen attributes click the button below. During this process you can specify a test user name and password and Ambari will attempt to authenticate and retrieve group membership information',
+          'username': 'Test Username',
+          'password': 'Test Password'
+        },
+        'groupsList': 'List of Groups',
+
+        'controls': {
+          'autoDetect': 'Perform Auto-Detection',
+          'testAttrs': 'Test Attributes'
+        },
+
+        'alerts': {
+          'successfulAuth': 'Successful Authentication'
+        }
+      },
+
+      'controls': {
+        'test': 'Test'
+      }
     }
   });
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/46f6030b/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 7a91296..cc57fa3 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
@@ -394,7 +394,7 @@ a.gotoinstance{
   font-size: 16px;
 }
 
-.user-edit-panel .ats-switch span.switch-right , .create-user-form .ats-switch span.switch-right{
+.user-edit-panel .ats-switch span.switch-right , .create-user-form .ats-switch span.switch-right, .enable-ldap .ats-switch span.switch-right {
   background-color: #da4f49;
   color: white;
 }
@@ -1147,14 +1147,14 @@ button.btn.btn-xs{
 .ambariAlert.error {
   border-left: 3px solid #ef2427;
 }
-.ambariAlert.error .icon-box {
+.ambariAlert.error .icon-box, .test-ldap-icon.fa-times-circle {
   color: #ef2427;
 }
 
 .ambariAlert.success {
   border-left: 3px solid #82c534;
 }
-.ambariAlert.success .icon-box {
+.ambariAlert.success .icon-box, .test-ldap-icon.fa-check-circle {
   color: #82c534;
 }
 
@@ -1373,6 +1373,14 @@ accordion .panel-group .panel{
 }
 
 thead.view-permission-header > tr > th {
-  border-top: 0px;
+  border-top: 0;
   padding-top: 40px;
 }
+
+.enable-ldap input[type="checkbox"] {
+  margin-top: 10px;
+}
+
+.test-ldap-icon.ng-hide-add-active, .test-ldap-icon.ng-hide-remove {
+  display: inline-block!important;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/46f6030b/ambari-admin/src/main/resources/ui/admin-web/app/views/authentication/main.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/authentication/main.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/authentication/main.html
index 7743d1e..8fa1429 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/views/authentication/main.html
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/authentication/main.html
@@ -16,11 +16,257 @@
 * limitations under the License.
 -->
 
-<div class="users-pane">
+<div class="users-pane enable-ldap">
+
   <div class="clearfix">
     <ol class="breadcrumb pull-left">
       <li class="active">{{'common.authentication' | translate}}</li>
     </ol>
   </div>
   <hr>
-</div>
+
+  <div class="form-horizontal">
+    <div class="form-group col-sm-12">{{'authentication.description' | translate}}</div>
+    <div class="form-group">
+      <label class="control-label col-sm-4">{{'authentication.ldap' | translate}}</label>
+      <span class="col-sm-8">
+        <toggle-switch model="isLDAPEnabled" ng-disabled="isRequestRunning" on-label="{{'authentication.on' | translate}}" off-label="{{'authentication.off' | translate}}" class="switch-primary" data-off-color="danger" on-change="toggleAuthentication()"></toggle-switch>
+      </span>
+    </div>
+  </div>
+
+  <div ng-show="isLDAPEnabled">
+
+    <div class="clearfix">
+      <ol class="breadcrumb pull-left">
+        <li class="active">{{'authentication.connectivity.title' | translate}}</li>
+      </ol>
+    </div>
+    <hr>
+
+    <form class="form-horizontal" ng-submit="testConnection()">
+      <div class="form-group">
+        <label for="host" class="control-label col-sm-4">{{'authentication.connectivity.host' | translate}}</label>
+        <div class="col-sm-8">
+          <input type="text" class="form-control" id="host" ng-model="connectivity.host">
+        </div>
+      </div>
+      <div class="form-group">
+        <label for="port" class="control-label col-sm-4">{{'authentication.connectivity.port' | translate}}</label>
+        <div class="col-sm-8">
+          <input type="text" class="form-control" id="port" ng-model="connectivity.port">
+        </div>
+      </div>
+      <div class="form-group">
+        <label for="ssl" class="control-label col-sm-4">{{'authentication.connectivity.ssl' | translate}}</label>
+        <div class="col-sm-8">
+          <input type="checkbox" id="ssl" ng-model="connectivity.ssl">
+        </div>
+      </div>
+      <div class="form-group">
+        <label for="trust-store" class="control-label col-sm-4">{{'authentication.connectivity.trustStore.label' | translate}}</label>
+        <div class="col-sm-3">
+          <select class="form-control" id="trust-store" ng-model="connectivity.trustStore" ng-options="t('authentication.connectivity.trustStore.options.' + item) for item in connectivity.trustStoreOptions"></select>
+        </div>
+      </div>
+      <div ng-show="connectivity.trustStore === 'custom'">
+        <div class="form-group">
+          <label for="trust-store-path" class="control-label col-sm-4">{{'authentication.connectivity.trustStorePath' | translate}}</label>
+          <div class="col-sm-8">
+            <input type="text" class="form-control" id="trust-store-path" ng-model="connectivity.trustStorePath">
+          </div>
+        </div>
+        <div class="form-group">
+          <label for="trust-store-type" class="control-label col-sm-4">{{'authentication.connectivity.trustStoreType.label' | translate}}</label>
+          <div class="col-sm-3">
+            <select class="form-control" id="trust-store-type" ng-model="connectivity.trustStoreType" ng-options="t('authentication.connectivity.trustStoreType.options.' + item) for item in connectivity.trustStoreTypeOptions"></select>
+          </div>
+        </div>
+        <div class="form-group">
+          <label for="trust-store-password" class="control-label col-sm-4">{{'authentication.connectivity.trustStorePassword' | translate}}</label>
+          <div class="col-sm-8">
+            <input type="password" class="form-control" id="trust-store-password" ng-model="connectivity.trustStorePassword">
+          </div>
+        </div>
+      </div>
+      <div class="form-group">
+        <label for="dn" class="control-label col-sm-4">{{'authentication.connectivity.dn' | translate}}</label>
+        <div class="col-sm-8">
+          <input type="text" class="form-control" id="dn" ng-model="connectivity.dn">
+        </div>
+      </div>
+      <div class="form-group">
+        <label for="bind-password" class="control-label col-sm-4">{{'authentication.connectivity.bindPassword' | translate}}</label>
+        <div class="col-sm-8">
+          <input type="password" class="form-control" id="bind-password" ng-model="connectivity.bindPassword">
+        </div>
+      </div>
+      <div class="form-group">
+        <div class="col-sm-offset-4 col-sm-8">
+          <button type="submit" class="btn btn-primary" ng-disabled="isConnectivityFormInvalid || isRequestRunning">{{'authentication.connectivity.controls.testConnection' | translate}}</button>
+          <i class="test-ldap-icon fa ng-class: {'fa-spin fa-spinner': isConnectionTestRunning, 'fa-check-circle': hasConnectionTestPassed, 'fa-times-circle': isConnectionTestComplete && !hasConnectionTestPassed}" ng-show="isConnectionTestRunning || isConnectionTestComplete"></i>
+        </div>
+      </div>
+    </form>
+
+    <div ng-show="hasConnectionTestPassed">
+
+      <div class="clearfix">
+        <ol class="breadcrumb pull-left">
+          <li class="active">{{'authentication.attributes.title' | translate}}</li>
+        </ol>
+      </div>
+      <hr>
+
+      <form class="form-horizontal" ng-submit="detectAttributes()">
+        <div class="form-group col-sm-12">{{'authentication.attributes.detection.label' | translate}}</div>
+        <div class="form-group">
+          <label for="manual-detection" class="col-sm-12">
+            <input type="radio" id="manual-detection" name="detection" ng-model="attributes.detection" ng-disabled="isAttributeDetectionRunning" value="manual">
+            {{'authentication.attributes.detection.options.manual' | translate}}
+          </label>
+          <label for="auto-detection" class="col-sm-12">
+            <input type="radio" id="auto-detection" name="detection" ng-model="attributes.detection" ng-disabled="isAttributeDetectionRunning" value="auto">
+            {{'authentication.attributes.detection.options.auto' | translate}}
+          </label>
+        </div>
+
+        <div ng-show="attributes.detection === 'auto'">
+
+          <div class="form-group">
+            <label for="user-search" class="control-label col-sm-4">{{'authentication.attributes.userSearch' | translate}}</label>
+            <div class="col-sm-8">
+              <input type="text" class="form-control" id="user-search" ng-model="attributes.userSearch">
+            </div>
+          </div>
+          <div class="form-group">
+            <label for="group-search" class="control-label col-sm-4">{{'authentication.attributes.groupSearch' | translate}}</label>
+            <div class="col-sm-8">
+              <input type="text" class="form-control" id="group-search" ng-model="attributes.groupSearch">
+            </div>
+          </div>
+
+          <div class="form-group">
+            <div class="col-sm-offset-4 col-sm-8">
+              <button class="btn btn-primary" ng-disabled="isAutoDetectFormInvalid || isRequestRunning">{{'authentication.attributes.controls.autoDetect' | translate}}</button>
+              <i class="test-ldap-icon fa ng-class: {'fa-spin fa-spinner': isAttributeDetectionRunning, 'fa-check-circle': isAttributeDetectionSuccessful, 'fa-times-circle': isAttributeDetectionComplete && !isAttributeDetectionSuccessful}" ng-show="isAttributeDetectionRunning || isAttributeDetectionComplete"></i>
+            </div>
+          </div>
+          <div class="form-group col-sm-12" ng-show="isAttributeDetectionComplete && isAttributeDetectionSuccessful">{{'authentication.attributes.detected' | translate}}</div>
+
+        </div>
+
+      </form>
+
+      <div ng-show="attributes.detection === 'manual' || isAttributeDetectionComplete && isAttributeDetectionSuccessful">
+
+        <form id="attributes" class="form-horizontal" ng-submit="save()">
+
+          <div class="form-group">
+            <label for="user-obj-class" class="control-label col-sm-4">{{'authentication.attributes.userObjClass' | translate}}</label>
+            <div class="col-sm-8">
+              <input type="text" class="form-control" id="user-obj-class" ng-model="attributes.userObjClass">
+            </div>
+          </div>
+          <div class="form-group">
+            <label for="user-name-attr" class="control-label col-sm-4">{{'authentication.attributes.userNameAttr' | translate}}</label>
+            <div class="col-sm-8">
+              <input type="text" class="form-control" id="user-name-attr" ng-model="attributes.userNameAttr">
+            </div>
+          </div>
+          <div class="form-group">
+            <label for="group-obj-class" class="control-label col-sm-4">{{'authentication.attributes.groupObjClass' | translate}}</label>
+            <div class="col-sm-8">
+              <input type="text" class="form-control" id="group-obj-class" ng-model="attributes.groupObjClass">
+            </div>
+          </div>
+          <div class="form-group">
+            <label for="group-name-attr" class="control-label col-sm-4">{{'authentication.attributes.groupNameAttr' | translate}}</label>
+            <div class="col-sm-8">
+              <input type="text" class="form-control" id="group-name-attr" ng-model="attributes.groupNameAttr">
+            </div>
+          </div>
+          <div class="form-group">
+            <label for="group-member-attr" class="control-label col-sm-4">{{'authentication.attributes.groupMemberAttr' | translate}}</label>
+            <div class="col-sm-8">
+              <input type="text" class="form-control" id="group-member-attr" ng-model="attributes.groupMemberAttr">
+            </div>
+          </div>
+          <div class="form-group">
+            <label for="distinguished-name-attr" class="control-label col-sm-4">{{'authentication.attributes.distinguishedNameAttr' | translate}}</label>
+            <div class="col-sm-8">
+              <input type="text" class="form-control" id="distinguished-name-attr" ng-model="attributes.distinguishedNameAttr">
+            </div>
+          </div>
+          <div ng-show="attributes.detection === 'manual'">
+            <div class="form-group">
+              <label for="user-search-manual" class="control-label col-sm-4">{{'authentication.attributes.userSearch' | translate}}</label>
+              <div class="col-sm-8">
+                <input type="text" class="form-control" id="user-search-manual" ng-model="attributes.userSearchManual">
+              </div>
+            </div>
+            <div class="form-group">
+              <label for="group-search-manual" class="control-label col-sm-4">{{'authentication.attributes.groupSearch' | translate}}</label>
+              <div class="col-sm-8">
+                <input type="text" class="form-control" id="group-search-manual" ng-model="attributes.groupSearchManual">
+              </div>
+            </div>
+          </div>
+          <div class="form-group col-sm-12">{{'authentication.attributes.test.description' | translate}}</div>
+          <div class="form-group">
+            <div class="col-sm-offset-4 col-sm-8">
+              <button type="submit" class="btn btn-primary" ng-click="showTestAttributesForm()" ng-disabled="isAttributesFormInvalid || isTestAttributesFormShown || isRequestRunning">{{'authentication.attributes.controls.testAttrs' | translate}}</button>
+            </div>
+          </div>
+        </form>
+
+        <form class="form-horizontal" ng-show="isTestAttributesFormShown" ng-submit="testAttributes()">
+          <div class="form-group">
+            <label for="username" class="control-label col-sm-4">{{'authentication.attributes.test.username' | translate}}</label>
+            <div class="col-sm-8">
+              <input type="text" class="form-control" id="username" ng-model="attributes.username">
+            </div>
+          </div>
+          <div class="form-group">
+            <label for="password" class="control-label col-sm-4">{{'authentication.attributes.test.password' | translate}}</label>
+            <div class="col-sm-8">
+              <input type="password" class="form-control" id="password" ng-model="attributes.password">
+            </div>
+          </div>
+          <div class="form-group">
+            <div class="col-sm-offset-4 col-sm-8">
+              <button type="submit" class="btn btn-primary" ng-disabled="isTestAttributesFormInvalid || isRequestRunning">{{'authentication.controls.test' | translate}}</button>
+              <i class="test-ldap-icon fa ng-class: {'fa-spin fa-spinner': isTestAttributesRunning, 'fa-times-circle': isTestAttributesComplete && !isTestAttributesSuccessful}" ng-show="isTestAttributesRunning || isTestAttributesComplete"></i>
+            </div>
+          </div>
+        </form>
+
+        <div class="form-horizontal" ng-show="isTestAttributesSuccessful">
+          <div class="form-group">
+            <span class="control-label col-sm-4">
+              {{'authentication.attributes.alerts.successfulAuth' | translate}}
+            </span>
+            <div class="col-sm-1">
+              <i class="control-label test-ldap-icon fa fa-check-circle"></i>
+            </div>
+          </div>
+          <div class="form-group">
+            <label for="groups" class="control-label col-sm-4">{{'authentication.attributes.groupsList' | translate}}</label>
+            <div class="col-sm-6">
+              <select multiple class="form-control" id="groups" form="attributes" ng-model="attributes.groups" ng-options="item for item in attributes.availableGroups"></select>
+            </div>
+          </div>
+        </div>
+
+        <div class="text-center form-group">
+          <button type="submit" form="attributes" class="btn btn-primary" ng-disabled="isAttributesFormInvalid || isRequestRunning">{{'common.controls.save' | translate}}</button>
+          <i class="test-ldap-icon fa fa-spin fa-spinner" ng-show="isSaving"></i>
+        </div>
+
+      </div>
+
+    </div>
+
+  </div>
+
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/46f6030b/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
index c0967fb..bc86819 100644
--- 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
@@ -25,7 +25,7 @@
     <label for="username" class="col-sm-2 control-label">{{'users.username' | translate}}</label>
     <div class="col-sm-10">
       <input type="text" id="username" class="form-control username-input" name="user_name" placeholder="{{'users.userName' | translate}}" ng-model="user.user_name" required autocomplete="off">
-      <div class="alert alert-danger top-margin" ng-show="form.user_name.$error.required && form.submitted">{{'common.alerts.fieldIsRequired' | translate}</div>
+      <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">