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 <hiv...@gmail.com> Authored: Wed Feb 3 17:24:11 2016 +0200 Committer: Alex Antonenko <hiv...@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">