From c8e5809cc71999d2e9218451c9b10df598452889 Mon Sep 17 00:00:00 2001 From: Charlton Austin <charlton.austin@gmail.com> Date: Thu, 13 Oct 2016 09:04:59 -0400 Subject: [PATCH] Refactoring manage users to it's own directive. --- static/directives/manage-users-tab.html | 240 +++++++++++++++++++ static/js/directives/ui/manage-user-tab.js | 230 ++++++++++++++++++ static/js/pages/superuser.js | 211 +---------------- static/partials/super-user.html | 257 ++------------------- 4 files changed, 491 insertions(+), 447 deletions(-) create mode 100644 static/directives/manage-users-tab.html create mode 100644 static/js/directives/ui/manage-user-tab.js diff --git a/static/directives/manage-users-tab.html b/static/directives/manage-users-tab.html new file mode 100644 index 000000000..18d10ec84 --- /dev/null +++ b/static/directives/manage-users-tab.html @@ -0,0 +1,240 @@ +<div class="manage-users-tab-element"> + <div class="cor-loader" ng-show="!users"></div> + <div class="alert alert-error" ng-show="usersError"> + {{ usersError }} + </div> + <div ng-show="users"> + <div class="manager-header" header-title="Users"> + <button class="create-button btn btn-primary" ng-click="showCreateUser()" + quay-show="Config.AUTHENTICATION_TYPE == 'Database'"> + <i class="fa fa-plus" style="margin-right: 6px;"></i>Create User + </button> + <span class="co-alert co-alert-info" quay-show="Config.AUTHENTICATION_TYPE != 'Database'"> + Note: <span class="registry-name"></span> is configured to use external authentication, so users can only be created in that system + </span> + </div> + + <div class="filter-box" collection="users" filter-model="search" filter-name="Users"></div> + + <table class="cor-table"> + <thead> + <td style="width: 24px;"></td> + <td>Username</td> + <td>E-mail address</td> + <td style="width: 24px;"></td> + </thead> + + <tr ng-repeat="current_user in (users | filter:search | orderBy:'username')" + class="user-row" + ng-class="current_user.enabled ? 'enabled': 'disabled'"> + <td> + <span class="avatar" data="current_user.avatar" size="24"></span> + </td> + <td> + <span class="labels"> + <span class="label label-success" ng-if="user.username == current_user.username">You</span> + <span class="label label-primary" + ng-if="current_user.super_user">Superuser</span> + <span class="label label-default" + ng-if="!current_user.enabled">Disabled</span> + </span> + {{ current_user.username }} + </td> + <td> + <a href="mailto:{{ current_user.email }}">{{ current_user.email }}</a> + </td> + <td style="text-align: center;"> + <span class="cor-options-menu" + ng-if="user.username != current_user.username && !current_user.super_user"> + <span class="cor-option" option-click="setSuperuser(current_user, true)" + quay-show="!current_user.super_user"> + <i class="fa">Ω</i> + Make Superuser + </span> + <span class="cor-option" option-click="setSuperuser(current_user, false)" + quay-show="current_user.super_user"> + <i class="fa">ω</i> + Remove Superuser + </span> + + <span class="cor-option" option-click="showChangeEmail(current_user)" + quay-show="Config.AUTHENTICATION_TYPE == 'Database'"> + <i class="fa fa-envelope-o"></i> Change E-mail Address + </span> + <span class="cor-option" option-click="showChangePassword(current_user)" + quay-show="Config.AUTHENTICATION_TYPE == 'Database'"> + <i class="fa fa-key"></i> Change Password + </span> + <span class="cor-option" option-click="sendRecoveryEmail(current_user)" + quay-show="Features.MAILING && Config.AUTHENTICATION_TYPE == 'Database'"> + <i class="fa fa-envelope"></i> Send Recovery E-mail + </span> + <span class="cor-option" option-click="showDeleteUser(current_user)"> + <i class="fa fa-times"></i> Delete User + </span> + <span class="cor-option" option-click="askDisableUser(current_user)"> + <i class="fa" ng-class="current_user.enabled ? 'fa-circle-o' : 'fa-check-circle-o'"></i> <span + ng-if="current_user.enabled">Disable</span> <span ng-if="!current_user.enabled">Enable</span> User + </span> + <span class="cor-option" option-click="askTakeOwnership(current_user, false)" + ng-if="user.username != current_user.username && !current_user.super_user"> + <i class="fa fa-bolt"></i> Take Ownership + </span> + </span> + </td> + </tr> + </table> + </div> <!-- /show if users --> + + <!-- Take ownership dialog --> + <div class="cor-confirm-dialog take-ownership-dialog" + dialog-context="takeOwnershipInfo" + dialog-action="takeOwnership(info, callback)" + dialog-title="Take Ownership" + dialog-action-title="Take Ownership"> + Are you sure you want to take ownership of + <span ng-if="takeOwnershipInfo.is_org">organization <span class="avatar" data="takeOwnershipInfo.entity.avatar" + size="16"></span> {{ takeOwnershipInfo.entity.name }}?</span> + <span ng-if="!takeOwnershipInfo.is_org">user namespace <span class="avatar" data="takeOwnershipInfo.entity.avatar" + size="16"></span> {{ takeOwnershipInfo .entity.username }}?</span> + + <div class="co-alert co-alert-warning" ng-if="!takeOwnershipInfo.is_org"> + Note: This will convert the user namespace into an organization. <strong>The user will no longer be able to login + to + this account.</strong> + </div> + </div> + + <!-- Modal message dialog --> + <div class="co-dialog modal fade" id="confirmDeleteUserModal"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> + <h4 class="modal-title">Delete User?</h4> + </div> + <div class="modal-body"> + <div class="alert alert-danger"> + This operation <strong>cannot be undone</strong> and will <strong>delete any repositories owned by the + user</strong>. + </div> + Are you <strong>sure</strong> you want to delete user <strong>{{ userToDelete.username }}</strong>? + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-danger" ng-click="deleteUser(userToDelete)">Delete User</button> + <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> + </div> + </div><!-- /.modal-content --> + </div><!-- /.modal-dialog --> + </div><!-- /.modal --> + + <!-- Modal create user dialog --> + <div class="co-dialog modal fade" id="createUserModal"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> + <h4 class="modal-title">Create New User</h4> + </div> + <form name="createUserForm" ng-submit="createUser()"> + <div class="modal-body" ng-show="createdUser"> + <table class="table"> + <thead> + <th>Username</th> + <th>E-mail address</th> + <th>Temporary Password</th> + </thead> + + <tr class="user-row"> + <td>{{ createdUser.username }}</td> + <td>{{ createdUser.email }}</td> + <td>{{ createdUser.password }}</td> + </tr> + </table> + </div> + <div class="modal-body" ng-show="creatingUser"> + <div class="cor-loader"></div> + </div> + <div class="modal-body" ng-show="!creatingUser && !createdUser"> + <div class="form-group"> + <label>Username</label> + <input class="form-control" type="text" ng-model="newUser.username" ng-pattern="/^[a-z0-9_]{4,30}$/" + required> + </div> + + <div class="form-group"> + <label>Email address</label> + <input class="form-control" type="email" ng-model="newUser.email" required> + </div> + </div> + <div class="modal-footer" ng-show="createdUser"> + <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> + </div> + <div class="modal-footer" ng-show="!creatingUser && !createdUser"> + <button class="btn btn-primary" type="submit" ng-disabled="!createUserForm.$valid"> + Create User + </button> + <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> + </div> + </form> + </div><!-- /.modal-content --> + </div><!-- /.modal-dialog --> + </div><!-- /.modal --> + + + <!-- Modal change password dialog --> + <div class="co-dialog modal fade" id="changePasswordModal"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> + <h4 class="modal-title">Change User Password</h4> + </div> + <div class="modal-body"> + <div class="alert alert-warning"> + The user will no longer be able to access the registry with their current password + </div> + + <form class="form-change" id="changePasswordForm" name="changePasswordForm" data-trigger="manual"> + <input type="password" class="form-control" placeholder="User's new password" + ng-model="userToChange.password" + required ng-pattern="/^.{8,}$/"> + <input type="password" class="form-control" placeholder="Verify the new password" + ng-model="userToChange.repeatPassword" + match="userToChange.password" required ng-pattern="/^.{8,}$/"> + </form> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-primary" ng-click="changeUserPassword(userToChange)" + ng-disabled="changePasswordForm.$invalid">Change User Password + </button> + <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> + </div> + </div><!-- /.modal-content --> + </div><!-- /.modal-dialog --> + </div><!-- /.modal --> + + <!-- Modal change email dialog --> + <div class="co-dialog modal fade" id="changeEmailModal"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> + <h4 class="modal-title">Change User E-mail Address</h4> + </div> + <div class="modal-body"> + <form class="form-change" id="changeEmailForm" name="changeEmailForm" data-trigger="manual"> + <input type="email" class="form-control" placeholder="User's new email" ng-model="userToChange.newemail" + required> + </form> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-primary" ng-click="changeUserEmail(userToChange)" + ng-disabled="changeEmailForm.$invalid">Change User E-mail + </button> + <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> + </div> + </div><!-- /.modal-content --> + </div><!-- /.modal-dialog --> + </div><!-- /.modal --> +</div> diff --git a/static/js/directives/ui/manage-user-tab.js b/static/js/directives/ui/manage-user-tab.js new file mode 100644 index 000000000..6193925f9 --- /dev/null +++ b/static/js/directives/ui/manage-user-tab.js @@ -0,0 +1,230 @@ +/** + * An element which displays a panel for managing users. + */ +angular.module('quay').directive('manageUserTab', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: '/static/directives/manage-users-tab.html', + replace: false, + transclude: true, + restrict: 'C', + scope: { + 'isEnabled': '=isEnabled' + }, + controller: function ($scope, $timeout, $location, $element, ApiService, UserService) { + + $scope.newUser = {}; + $scope.createdUser = null; + $scope.takeOwnershipInfo = null; + + + $scope.showCreateUser = function () { + $scope.createdUser = null; + $('#createUserModal').modal('show'); + }; + + var loadUsersInternal = function () { + ApiService.listAllUsers().then(function (resp) { + $scope.users = resp['users']; + $scope.showInterface = true; + }, function (resp) { + $scope.users = []; + $scope.usersError = ApiService.getErrorMessage(resp); + }); + }; + + $scope.createUser = function () { + $scope.creatingUser = true; + $scope.createdUser = null; + + var errorHandler = ApiService.errorDisplay('Cannot create user', function () { + $scope.creatingUser = false; + $('#createUserModal').modal('hide'); + }); + + ApiService.createInstallUser($scope.newUser, null).then(function (resp) { + $scope.creatingUser = false; + $scope.newUser = {}; + $scope.createdUser = resp; + loadUsersInternal(); + }, errorHandler) + }; + + + $scope.setSuperuser = function (user, status) { + var setSuperuser = function () { + var params = { + 'username': user.username + }; + + var data = { + 'superuser': status + }; + + ApiService.changeInstallUser(data, params).then(function (resp) { + $scope.requiresRestart = true; + }, ApiService.errorDisplay('Could not change user')); + }; + + var msg = 'Note: This change, once applied, will require your installation ' + + 'to be restarted to take effect'; + + bootbox.confirm(msg, function (status) { + if (status) { + setSuperuser(); + } + }); + }; + + $scope.showChangeEmail = function (user) { + $scope.userToChange = user; + $('#changeEmailModal').modal({}); + }; + + $scope.changeUserEmail = function (user) { + $('#changeEmailModal').modal('hide'); + + var params = { + 'username': user.username + }; + + var data = { + 'email': user.newemail + }; + + ApiService.changeInstallUser(data, params).then(function (resp) { + loadUsersInternal(); + user.email = user.newemail; + delete user.newemail; + }, ApiService.errorDisplay('Could not change user')); + }; + + $scope.showChangePassword = function (user) { + $scope.userToChange = user; + $('#changePasswordModal').modal({}); + }; + + $scope.changeUserPassword = function (user) { + $('#changePasswordModal').modal('hide'); + + var params = { + 'username': user.username + }; + + var data = { + 'password': user.password + }; + + ApiService.changeInstallUser(data, params).then(function (resp) { + loadUsersInternal(); + }, ApiService.errorDisplay('Could not change user')); + }; + + $scope.sendRecoveryEmail = function (user) { + var params = { + 'username': user.username + }; + + ApiService.sendInstallUserRecoveryEmail(null, params).then(function (resp) { + bootbox.dialog({ + "message": "A recovery email has been sent to " + resp['email'], + "title": "Recovery email sent", + "buttons": { + "close": { + "label": "Close", + "className": "btn-primary" + } + } + }); + + }, ApiService.errorDisplay('Cannot send recovery email')) + }; + + $scope.showDeleteUser = function (user) { + if (user.username == UserService.currentUser().username) { + bootbox.dialog({ + "message": 'Cannot delete yourself!', + "title": "Cannot delete user", + "buttons": { + "close": { + "label": "Close", + "className": "btn-primary" + } + } + }); + return; + } + + $scope.userToDelete = user; + $('#confirmDeleteUserModal').modal({}); + }; + + $scope.deleteUser = function (user) { + $('#confirmDeleteUserModal').modal('hide'); + + var params = { + 'username': user.username + }; + + ApiService.deleteInstallUser(null, params).then(function (resp) { + loadUsersInternal(); + }, ApiService.errorDisplay('Cannot delete user')); + }; + + $scope.askDisableUser = function (user) { + var message = 'Are you sure you want to disable this user? ' + + 'They will be unable to login, pull or push.'; + + if (!user.enabled) { + message = 'Are you sure you want to reenable this user? ' + + 'They will be able to login, pull or push.' + } + + bootbox.confirm(message, function (resp) { + if (resp) { + var params = { + 'username': user.username + }; + + var data = { + 'enabled': !user.enabled + }; + + ApiService.changeInstallUser(data, params).then(function (resp) { + loadUsersInternal(); + }); + } + }); + }; + + $scope.askTakeOwnership = function (entity, is_org) { + $scope.takeOwnershipInfo = { + 'entity': entity, + 'is_org': is_org + }; + }; + + $scope.takeOwnership = function (info, callback) { + var errorDisplay = ApiService.errorDisplay('Could not take ownership of namespace', callback); + var params = { + 'namespace': info.entity.username || info.entity.name + }; + + ApiService.takeOwnership(null, params).then(function () { + callback(true); + $location.path('/organization/' + params.namespace); + }, errorDisplay) + }; + + $scope.$watch('isEnabled', function (value) { + if (value) { + if ($scope.users) { + return; + } + loadUsersInternal(); + } + }); + } + }; + return directiveDefinitionObject; +}); diff --git a/static/js/pages/superuser.js b/static/js/pages/superuser.js index 50636337b..91bee789a 100644 --- a/static/js/pages/superuser.js +++ b/static/js/pages/superuser.js @@ -10,7 +10,7 @@ }) }]); - function SuperuserCtrl($scope, $timeout, $location, ApiService, Features, UserService, ContainerService, AngularPollChannel, CoreDialog) { + function SuperuserCtrl($scope, ApiService, Features, UserService, ContainerService, AngularPollChannel, CoreDialog) { if (!Features.SUPER_USERS) { return; } @@ -21,8 +21,6 @@ $scope.configStatus = null; $scope.requiresRestart = null; $scope.logsCounter = 0; - $scope.newUser = {}; - $scope.createdUser = null; $scope.changeLog = null; $scope.debugServices = null; $scope.debugLogs = null; @@ -32,7 +30,7 @@ $scope.currentConfig = null; $scope.serviceKeysActive = false; $scope.globalMessagesActive = false; - $scope.takeOwnershipInfo = null; + $scope.manageUsersActive = false; $scope.loadMessageOfTheDay = function () { $scope.globalMessagesActive = true; @@ -42,11 +40,6 @@ $scope.requiresRestart = true; }; - $scope.showCreateUser = function() { - $scope.createdUser = null; - $('#createUserModal').modal('show'); - }; - $scope.loadServiceKeys = function() { $scope.serviceKeysActive = true; }; @@ -121,110 +114,7 @@ }; $scope.loadUsers = function() { - if ($scope.users) { - return; - } - - $scope.loadUsersInternal(); - }; - - $scope.loadUsersInternal = function() { - ApiService.listAllUsers().then(function(resp) { - $scope.users = resp['users']; - $scope.showInterface = true; - }, function(resp) { - $scope.users = []; - $scope.usersError = ApiService.getErrorMessage(resp); - }); - }; - - $scope.showChangePassword = function(user) { - $scope.userToChange = user; - $('#changePasswordModal').modal({}); - }; - - $scope.showChangeEmail = function(user) { - $scope.userToChange = user; - $('#changeEmailModal').modal({}); - }; - - $scope.createUser = function() { - $scope.creatingUser = true; - $scope.createdUser = null; - - var errorHandler = ApiService.errorDisplay('Cannot create user', function() { - $scope.creatingUser = false; - $('#createUserModal').modal('hide'); - }); - - ApiService.createInstallUser($scope.newUser, null).then(function(resp) { - $scope.creatingUser = false; - $scope.newUser = {}; - $scope.createdUser = resp; - $scope.loadUsersInternal(); - }, errorHandler) - }; - - $scope.setSuperuser = function(user, status) { - var setSuperuser = function() { - var params = { - 'username': user.username - }; - - var data = { - 'superuser': status - }; - - ApiService.changeInstallUser(data, params).then(function(resp) { - $scope.requiresRestart = true; - }, ApiService.errorDisplay('Could not change user')); - }; - - var msg = 'Note: This change, once applied, will require your installation ' + - 'to be restarted to take effect'; - - bootbox.confirm(msg, function(status) { - if (status) { - setSuperuser(); - } - }); - }; - - $scope.showDeleteUser = function(user) { - if (user.username == UserService.currentUser().username) { - bootbox.dialog({ - "message": 'Cannot delete yourself!', - "title": "Cannot delete user", - "buttons": { - "close": { - "label": "Close", - "className": "btn-primary" - } - } - }); - return; - } - - $scope.userToDelete = user; - $('#confirmDeleteUserModal').modal({}); - }; - - $scope.changeUserEmail = function(user) { - $('#changeEmailModal').modal('hide'); - - var params = { - 'username': user.username - }; - - var data = { - 'email': user.newemail - }; - - ApiService.changeInstallUser(data, params).then(function(resp) { - $scope.loadUsersInternal(); - user.email = user.newemail; - delete user.newemail; - }, ApiService.errorDisplay('Could not change user')); + $scope.manageUsersActive = true; }; $scope.askDeleteOrganization = function(org) { @@ -261,99 +151,6 @@ }); }; - $scope.askTakeOwnership = function(entity, is_org) { - $scope.takeOwnershipInfo = { - 'entity': entity, - 'is_org': is_org - }; - }; - - $scope.takeOwnership = function(info, callback) { - var errorDisplay = ApiService.errorDisplay('Could not take ownership of namespace', callback); - var params = { - 'namespace': info.entity.username || info.entity.name - }; - - ApiService.takeOwnership(null, params).then(function() { - callback(true); - $location.path('/organization/' + params.namespace); - }, errorDisplay) - }; - - $scope.askDisableUser = function(user) { - var message = 'Are you sure you want to disable this user? ' + - 'They will be unable to login, pull or push.' - - if (!user.enabled) { - message = 'Are you sure you want to reenable this user? ' + - 'They will be able to login, pull or push.' - } - - bootbox.confirm(message, function(resp) { - if (resp) { - var params = { - 'username': user.username - }; - - var data = { - 'enabled': !user.enabled - }; - - ApiService.changeInstallUser(data, params).then(function(resp) { - $scope.loadUsersInternal(); - }); - } - }); - }; - - $scope.changeUserPassword = function(user) { - $('#changePasswordModal').modal('hide'); - - var params = { - 'username': user.username - }; - - var data = { - 'password': user.password - }; - - ApiService.changeInstallUser(data, params).then(function(resp) { - $scope.loadUsersInternal(); - }, ApiService.errorDisplay('Could not change user')); - }; - - $scope.deleteUser = function(user) { - $('#confirmDeleteUserModal').modal('hide'); - - var params = { - 'username': user.username - }; - - ApiService.deleteInstallUser(null, params).then(function(resp) { - $scope.loadUsersInternal(); - }, ApiService.errorDisplay('Cannot delete user')); - }; - - $scope.sendRecoveryEmail = function(user) { - var params = { - 'username': user.username - }; - - ApiService.sendInstallUserRecoveryEmail(null, params).then(function(resp) { - bootbox.dialog({ - "message": "A recovery email has been sent to " + resp['email'], - "title": "Recovery email sent", - "buttons": { - "close": { - "label": "Close", - "className": "btn-primary" - } - } - }); - - }, ApiService.errorDisplay('Cannot send recovery email')) - }; - $scope.restartContainer = function() { $('#restartingContainerModal').modal({ keyboard: false, @@ -378,7 +175,7 @@ var message = "Installation of this product has not yet been completed." + "<br><br>Please read the " + "<a href='https://coreos.com/docs/enterprise-registry/initial-setup/'>" + - "Setup Guide</a>" + "Setup Guide</a>"; var title = "Installation Incomplete"; CoreDialog.fatal(title, message); diff --git a/static/partials/super-user.html b/static/partials/super-user.html index 3b8892468..4e9eaa943 100644 --- a/static/partials/super-user.html +++ b/static/partials/super-user.html @@ -34,11 +34,12 @@ <span class="cor-tab" tab-title="Usage Logs" tab-target="#logs" tab-init="loadUsageLogs()"> <i class="fa fa-bar-chart"></i> </span> - <span class="cor-tab" tab-title="Internal Logs and Debugging" tab-target="#debug" tab-init="loadDebugServices()"> + <span class="cor-tab" tab-title="Internal Logs and Debugging" tab-target="#debug" + tab-init="loadDebugServices()"> <i class="fa fa-bug"></i> </span> <span class="cor-tab hidden-xs" tab-title="Registry Settings" tab-target="#setup" - tab-init="loadConfig()"> + tab-init="loadConfig()"> <i class="fa fa-cog"></i> </span> <span class="cor-tab hidden-xs" tab-title="Globally visible user messages" tab-target="#message-of-the-day" @@ -76,18 +77,18 @@ ng-class="debugService == service ? 'active' : ''"> <a ng-click="viewSystemLogs(service)">{{ service }}</a> </li> - </ul> + </ul> - <div class="system-log-download-panel" ng-if="!debugService"> - Select a service above to view its local logs + <div class="system-log-download-panel" ng-if="!debugService"> + Select a service above to view its local logs - <div> - <a class="btn btn-primary" href="/systemlogsarchive?_csrf_token={{ csrf_token }}" ng-safenewtab> - <i class="fa fa-download fa-lg" style="margin-right: 4px;"></i> Download All Local Logs (.tar.gz) - </a> + <div> + <a class="btn btn-primary" href="/systemlogsarchive?_csrf_token={{ csrf_token }}" ng-safenewtab> + <i class="fa fa-download fa-lg" style="margin-right: 4px;"></i> Download All Local Logs (.tar.gz) + </a> + </div> </div> - </div> - <div class="cor-log-box" logs="debugLogs" ng-show="debugService"></div> + <div class="cor-log-box" logs="debugLogs" ng-show="debugService"></div> </div> </div> @@ -114,10 +115,10 @@ <table class="cor-table"> <thead> - <td style="width: 24px;"></td> - <td>Name</td> - <td>Admin E-mail</td> - <td style="width: 24px;"></td> + <td style="width: 24px;"></td> + <td>Name</td> + <td>Admin E-mail</td> + <td style="width: 24px;"></td> </thead> <tr ng-repeat="current_org in (organizations | filter:search | orderBy:'name')" @@ -151,187 +152,12 @@ <!-- Users tab --> <div id="users" class="tab-pane active"> - <div class="cor-loader" ng-show="!users"></div> - <div class="alert alert-error" ng-show="usersError"> - {{ usersError }} - </div> - <div ng-show="users"> - <div class="manager-header" header-title="Users"> - <button class="create-button btn btn-primary" ng-click="showCreateUser()" - quay-show="Config.AUTHENTICATION_TYPE == 'Database'"> - <i class="fa fa-plus" style="margin-right: 6px;"></i>Create User - </button> - <span class="co-alert co-alert-info" quay-show="Config.AUTHENTICATION_TYPE != 'Database'"> - Note: <span class="registry-name"></span> is configured to use external authentication, so users can only be created in that system - </span> - </div> - - <div class="filter-box" collection="users" filter-model="search" filter-name="Users"></div> - - <table class="cor-table"> - <thead> - <td style="width: 24px;"></td> - <td>Username</td> - <td>E-mail address</td> - <td style="width: 24px;"></td> - </thead> - - <tr ng-repeat="current_user in (users | filter:search | orderBy:'username')" - class="user-row" - ng-class="current_user.enabled ? 'enabled': 'disabled'"> - <td> - <span class="avatar" data="current_user.avatar" size="24"></span> - </td> - <td> - <span class="labels"> - <span class="label label-success" ng-if="user.username == current_user.username">You</span> - <span class="label label-primary" - ng-if="current_user.super_user">Superuser</span> - <span class="label label-default" - ng-if="!current_user.enabled">Disabled</span> - </span> - {{ current_user.username }} - </td> - <td> - <a href="mailto:{{ current_user.email }}">{{ current_user.email }}</a> - </td> - <td style="text-align: center;"> - <span class="cor-options-menu" - ng-if="user.username != current_user.username && !current_user.super_user"> - <span class="cor-option" option-click="setSuperuser(current_user, true)" - quay-show="!current_user.super_user"> - <i class="fa">Ω</i> - Make Superuser - </span> - <span class="cor-option" option-click="setSuperuser(current_user, false)" - quay-show="current_user.super_user"> - <i class="fa">ω</i> - Remove Superuser - </span> - - <span class="cor-option" option-click="showChangeEmail(current_user)" - quay-show="Config.AUTHENTICATION_TYPE == 'Database'"> - <i class="fa fa-envelope-o"></i> Change E-mail Address - </span> - <span class="cor-option" option-click="showChangePassword(current_user)" - quay-show="Config.AUTHENTICATION_TYPE == 'Database'"> - <i class="fa fa-key"></i> Change Password - </span> - <span class="cor-option" option-click="sendRecoveryEmail(current_user)" - quay-show="Features.MAILING && Config.AUTHENTICATION_TYPE == 'Database'"> - <i class="fa fa-envelope"></i> Send Recovery E-mail - </span> - <span class="cor-option" option-click="showDeleteUser(current_user)"> - <i class="fa fa-times"></i> Delete User - </span> - <span class="cor-option" option-click="askDisableUser(current_user)"> - <i class="fa" ng-class="current_user.enabled ? 'fa-circle-o' : 'fa-check-circle-o'"></i> <span ng-if="current_user.enabled">Disable</span> <span ng-if="!current_user.enabled">Enable</span> User - </span> - <span class="cor-option" option-click="askTakeOwnership(current_user, false)" - ng-if="user.username != current_user.username && !current_user.super_user"> - <i class="fa fa-bolt"></i> Take Ownership - </span> - </span> - </td> - </tr> - </table> - </div> <!-- /show if users --> + <div class="manage-user-tab" is-enabled="manageUsersActive"></div> </div> <!-- users-tab --> </div> <!-- /cor-tab-content --> </div> <!-- /cor-tab-panel --> - <!-- Take ownership dialog --> - <div class="cor-confirm-dialog take-ownership-dialog" - dialog-context="takeOwnershipInfo" - dialog-action="takeOwnership(info, callback)" - dialog-title="Take Ownership" - dialog-action-title="Take Ownership"> - Are you sure you want to take ownership of - <span ng-if="takeOwnershipInfo.is_org">organization <span class="avatar" data="takeOwnershipInfo.entity.avatar" size="16"></span> {{ takeOwnershipInfo.entity.name }}?</span> - <span ng-if="!takeOwnershipInfo.is_org">user namespace <span class="avatar" data="takeOwnershipInfo.entity.avatar" size="16"></span> {{ takeOwnershipInfo .entity.username }}?</span> - - <div class="co-alert co-alert-warning" ng-if="!takeOwnershipInfo.is_org"> - Note: This will convert the user namespace into an organization. <strong>The user will no longer be able to login to this account.</strong> - </div> - </div> - - <!-- Modal message dialog --> - <div class="co-dialog modal fade" id="confirmDeleteUserModal"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> - <h4 class="modal-title">Delete User?</h4> - </div> - <div class="modal-body"> - <div class="alert alert-danger"> - This operation <strong>cannot be undone</strong> and will <strong>delete any repositories owned by the user</strong>. - </div> - Are you <strong>sure</strong> you want to delete user <strong>{{ userToDelete.username }}</strong>? - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-danger" ng-click="deleteUser(userToDelete)">Delete User</button> - <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> - </div> - </div><!-- /.modal-content --> - </div><!-- /.modal-dialog --> - </div><!-- /.modal --> - - - <!-- Modal create user dialog --> - <div class="co-dialog modal fade" id="createUserModal"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> - <h4 class="modal-title">Create New User</h4> - </div> - <form name="createUserForm" ng-submit="createUser()"> - <div class="modal-body" ng-show="createdUser"> - <table class="table"> - <thead> - <th>Username</th> - <th>E-mail address</th> - <th>Temporary Password</th> - </thead> - - <tr class="user-row"> - <td>{{ createdUser.username }}</td> - <td>{{ createdUser.email }}</td> - <td>{{ createdUser.password }}</td> - </tr> - </table> - </div> - <div class="modal-body" ng-show="creatingUser"> - <div class="cor-loader"></div> - </div> - <div class="modal-body" ng-show="!creatingUser && !createdUser"> - <div class="form-group"> - <label>Username</label> - <input class="form-control" type="text" ng-model="newUser.username" ng-pattern="/^[a-z0-9_]{4,30}$/" required> - </div> - - <div class="form-group"> - <label>Email address</label> - <input class="form-control" type="email" ng-model="newUser.email" required> - </div> - </div> - <div class="modal-footer" ng-show="createdUser"> - <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> - </div> - <div class="modal-footer" ng-show="!creatingUser && !createdUser"> - <button class="btn btn-primary" type="submit" ng-disabled="!createUserForm.$valid"> - Create User - </button> - <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> - </div> - </form> - </div><!-- /.modal-content --> - </div><!-- /.modal-dialog --> - </div><!-- /.modal --> - - <!-- Modal message dialog --> <div class="co-dialog modal fade" id="restartingContainerModal"> <div class="modal-dialog"> @@ -353,55 +179,6 @@ </div><!-- /.modal-dialog --> </div><!-- /.modal --> - <!-- Modal message dialog --> - <div class="co-dialog modal fade" id="changePasswordModal"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> - <h4 class="modal-title">Change User Password</h4> - </div> - <div class="modal-body"> - <div class="alert alert-warning"> - The user will no longer be able to access the registry with their current password - </div> - - <form class="form-change" id="changePasswordForm" name="changePasswordForm" data-trigger="manual"> - <input type="password" class="form-control" placeholder="User's new password" ng-model="userToChange.password" required ng-pattern="/^.{8,}$/"> - <input type="password" class="form-control" placeholder="Verify the new password" ng-model="userToChange.repeatPassword" - match="userToChange.password" required ng-pattern="/^.{8,}$/"> - </form> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-primary" ng-click="changeUserPassword(userToChange)" - ng-disabled="changePasswordForm.$invalid">Change User Password</button> - <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> - </div> - </div><!-- /.modal-content --> - </div><!-- /.modal-dialog --> - </div><!-- /.modal --> - - <!-- Modal message dialog --> - <div class="co-dialog modal fade" id="changeEmailModal"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> - <h4 class="modal-title">Change User E-mail Address</h4> - </div> - <div class="modal-body"> - <form class="form-change" id="changeEmailForm" name="changeEmailForm" data-trigger="manual"> - <input type="email" class="form-control" placeholder="User's new email" ng-model="userToChange.newemail" required> - </form> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-primary" ng-click="changeUserEmail(userToChange)" - ng-disabled="changeEmailForm.$invalid">Change User E-mail</button> - <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> - </div> - </div><!-- /.modal-content --> - </div><!-- /.modal-dialog --> - </div><!-- /.modal --> </div> <!-- /page-content --> </div>