You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ma...@apache.org on 2021/10/06 13:41:29 UTC
[airavata-django-portal] 05/13: AIRAVATA-3319 Add admin UI for
updating user's username
This is an automated email from the ASF dual-hosted git repository.
machristie pushed a commit to branch AIRAVATA-3319-handle-missing-name-and-email-attributes-from-cilo
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git
commit 4ce2dd26cbe093b611f4749add918d04e05984f2
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Tue Jul 6 12:55:54 2021 -0400
AIRAVATA-3319 Add admin UI for updating user's username
---
django_airavata/apps/admin/package.json | 1 +
.../src/components/users/ChangeUsernamePanel.vue | 92 ++++++++++++++++++++++
.../IdentityServiceUserManagementContainer.vue | 9 +++
.../src/components/users/UserDetailsContainer.vue | 7 ++
django_airavata/apps/admin/yarn.lock | 5 ++
.../django_airavata_api/js/service_config.js | 8 ++
.../common/js/components/ConfirmationButton.vue | 52 ++++++++++++
django_airavata/static/common/js/index.js | 2 +
8 files changed, 176 insertions(+)
diff --git a/django_airavata/apps/admin/package.json b/django_airavata/apps/admin/package.json
index 670f5f5..94fde2f 100644
--- a/django_airavata/apps/admin/package.json
+++ b/django_airavata/apps/admin/package.json
@@ -26,6 +26,7 @@
"vue-resource": "^1.3.4",
"vue-router": "^2.7.0",
"vuedraggable": "^2.16.0",
+ "vuelidate": "^0.7.6",
"vuex": "^2.4.0",
"weekstart": "^1.0.0"
},
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/ChangeUsernamePanel.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/ChangeUsernamePanel.vue
new file mode 100644
index 0000000..42bc64b
--- /dev/null
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/ChangeUsernamePanel.vue
@@ -0,0 +1,92 @@
+<template>
+ <b-card header="Change Username">
+ <p class="card-text">
+ This will change the user's username in the identity service. NOTE: if
+ this user already has an Airavata User Profile, giving the user a new
+ username will result in the user getting a new Airavata User Profile and
+ losing the old one. Also, after updating the username the user will need
+ to log out and log back in.
+ </p>
+ <b-form-group label="New Username" label-for="new-username">
+ <b-input-group>
+ <b-form-input
+ id="new-username"
+ v-model="$v.newUsername.$model"
+ :state="validateState($v.newUsername)"
+ />
+ <b-input-group-append>
+ <b-button @click="newUsername = email">Copy Email Address</b-button>
+ </b-input-group-append>
+ </b-input-group>
+ <b-form-invalid-feedback
+ :state="validateState($v.newUsername)"
+ v-if="!$v.newUsername.emailOrMatchesRegex"
+ >
+ Username can only contain lowercase letters, numbers, underscores and
+ hyphens OR it can be the same as the email address.
+ </b-form-invalid-feedback>
+ </b-form-group>
+ <confirmation-button
+ variant="primary"
+ @confirmed="updateUsername"
+ :disabled="$v.$invalid"
+ dialog-title="Please confirm username change"
+ >
+ Please confirm that you want to change the user's username to
+ <strong>{{ newUsername }}</strong
+ >. NOTE: if this user already has an Airavata User Profile, giving the
+ user a new username will result in
+ <strong
+ >the user getting a new Airavata User Profile and losing the old
+ one</strong
+ >. Also, after updating the username the user will need to log out and log
+ back in.
+ </confirmation-button>
+ </b-card>
+</template>
+
+<script>
+import { components, errors } from "django-airavata-common-ui";
+import { validationMixin } from "vuelidate";
+import { helpers, or, required, sameAs } from "vuelidate/lib/validators";
+export default {
+ name: "change-username-panel",
+ mixins: [validationMixin],
+ props: {
+ username: {
+ type: String,
+ required: true,
+ },
+ email: {
+ type: String,
+ required: true,
+ },
+ },
+ components: {
+ "confirmation-button": components.ConfirmationButton,
+ },
+ data() {
+ return {
+ newUsername: this.username,
+ };
+ },
+ validations() {
+ const usernameRegex = helpers.regex("newUsername", /^[a-z0-9_-]+$/);
+ const emailOrMatchesRegex = or(usernameRegex, sameAs("email"));
+ return {
+ newUsername: {
+ required,
+ emailOrMatchesRegex,
+ },
+ };
+ },
+ methods: {
+ updateUsername() {
+ if (!this.$v.$invalid) {
+ this.$emit("update-username", [this.username, this.newUsername]);
+ }
+ },
+ validateState: errors.vuelidateHelpers.validateState,
+ },
+};
+</script>
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/IdentityServiceUserManagementContainer.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/IdentityServiceUserManagementContainer.vue
index f7785da..458d718 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/IdentityServiceUserManagementContainer.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/IdentityServiceUserManagementContainer.vue
@@ -47,6 +47,7 @@
@groups-updated="groupsUpdated"
@enable-user="enableUser"
@delete-user="deleteUser"
+ @update-username="updateUsername(data.item, ...$event)"
/>
</template>
</b-table>
@@ -204,6 +205,14 @@ export default {
this.reloadUserProfiles()
);
},
+ updateUsername(userProfile, username, newUsername) {
+ const updatedUserProfile = userProfile.clone();
+ updatedUserProfile.userId = newUsername;
+ services.IAMUserProfileService.updateUsername({
+ lookup: username,
+ data: updatedUserProfile,
+ }).finally(() => this.reloadUserProfiles());
+ },
},
};
</script>
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserDetailsContainer.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserDetailsContainer.vue
index 48b9516..e59428f 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserDetailsContainer.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserDetailsContainer.vue
@@ -34,6 +34,11 @@
:username="iamUserProfile.userId"
@delete-user="$emit('delete-user', $event)"
/>
+ <change-username-panel
+ :username="iamUserProfile.userId"
+ :email="iamUserProfile.email"
+ @update-username="$emit('update-username', $event)"
+ />
</div>
</template>
<script>
@@ -42,6 +47,7 @@ import UserGroupMembershipEditor from "./UserGroupMembershipEditor";
import ActivateUserPanel from "./ActivateUserPanel";
import EnableUserPanel from "./EnableUserPanel";
import DeleteUserPanel from "./DeleteUserPanel";
+import ChangeUsernamePanel from "./ChangeUsernamePanel.vue";
export default {
name: "user-details-container",
@@ -60,6 +66,7 @@ export default {
EnableUserPanel,
DeleteUserPanel,
ActivateUserPanel,
+ ChangeUsernamePanel,
},
data() {
return {
diff --git a/django_airavata/apps/admin/yarn.lock b/django_airavata/apps/admin/yarn.lock
index 1e34aef..8b1d3b2 100644
--- a/django_airavata/apps/admin/yarn.lock
+++ b/django_airavata/apps/admin/yarn.lock
@@ -8574,6 +8574,11 @@ vuedraggable@^2.16.0:
dependencies:
sortablejs "^1.10.1"
+vuelidate@^0.7.6:
+ version "0.7.6"
+ resolved "https://registry.yarnpkg.com/vuelidate/-/vuelidate-0.7.6.tgz#84100c13b943470660d0416642845cd2a1edf4b2"
+ integrity sha512-suzIuet1jGcyZ4oUSW8J27R2tNrJ9cIfklAh63EbAkFjE380iv97BAiIeolRYoB9bF9usBXCu4BxftWN1Dkn3g==
+
vuex@^2.4.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/vuex/-/vuex-2.5.0.tgz#20f0265ade6c9a5ac6724a405d3ffdb4726c9741"
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/service_config.js b/django_airavata/apps/api/static/django_airavata_api/js/service_config.js
index 64ad6b1..d3c82b0 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/service_config.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/service_config.js
@@ -287,6 +287,14 @@ export default {
requestType: "post",
modelClass: IAMUserProfile,
},
+ updateUsername: {
+ url: "/api/iam-user-profiles/<lookup>/update_username/",
+ bodyParams: {
+ name: "data",
+ },
+ requestType: "put",
+ modelClass: IAMUserProfile,
+ },
},
queryParams: ["limit", "offset", "search"],
modelClass: IAMUserProfile,
diff --git a/django_airavata/static/common/js/components/ConfirmationButton.vue b/django_airavata/static/common/js/components/ConfirmationButton.vue
new file mode 100644
index 0000000..c327045
--- /dev/null
+++ b/django_airavata/static/common/js/components/ConfirmationButton.vue
@@ -0,0 +1,52 @@
+<template>
+ <div class="confirmation-button">
+ <b-button
+ :variant="variant"
+ @click="$refs.modal.show()"
+ :disabled="disabled"
+ >
+ {{ label }}
+ </b-button>
+ <confirmation-dialog
+ ref="modal"
+ :title="dialogTitle"
+ @ok="$emit('confirmed')"
+ >
+ <slot></slot>
+ </confirmation-dialog>
+ </div>
+</template>
+<script>
+import ConfirmationDialog from "./ConfirmationDialog.vue";
+
+export default {
+ name: "confirmation-button",
+ props: {
+ dialogTitle: {
+ type: String,
+ default: "Please confirm",
+ },
+ disabled: {
+ type: Boolean,
+ default: false,
+ },
+ label: {
+ type: String,
+ default: "Update",
+ },
+ variant: {
+ type: String,
+ default: "danger",
+ },
+ },
+ components: {
+ ConfirmationDialog,
+ },
+};
+</script>
+
+<style scoped>
+.confirmation-button {
+ display: inline-block;
+}
+</style>
diff --git a/django_airavata/static/common/js/index.js b/django_airavata/static/common/js/index.js
index ac905a9..b4bcc5e 100644
--- a/django_airavata/static/common/js/index.js
+++ b/django_airavata/static/common/js/index.js
@@ -4,6 +4,7 @@ import AutocompleteTextInput from "./components/AutocompleteTextInput.vue";
import ClipboardCopyButton from "./components/ClipboardCopyButton.vue";
import ClipboardCopyLink from "./components/ClipboardCopyLink.vue";
import ComputeResourceName from "./components/ComputeResourceName";
+import ConfirmationButton from "./components/ConfirmationButton.vue";
import ConfirmationDialog from "./components/ConfirmationDialog.vue";
import DataProductViewer from "./components/DataProductViewer";
import DeleteButton from "./components/DeleteButton.vue";
@@ -47,6 +48,7 @@ const components = {
ClipboardCopyButton,
ClipboardCopyLink,
ComputeResourceName,
+ ConfirmationButton,
ConfirmationDialog,
DataProductViewer,
DeleteButton,