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 2019/05/31 18:18:16 UTC
[airavata-django-portal] 01/05: AIRAVATA-3040 Persist changes to
user group membership
This is an automated email from the ASF dual-hosted git repository.
machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git
commit f447a126ff4f1fbb731af09d7660736e1d461a79
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Thu May 30 11:34:19 2019 -0400
AIRAVATA-3040 Persist changes to user group membership
---
.../src/components/users/UserDetailsContainer.vue | 44 ++++++
.../components/users/UserGroupMembershipEditor.vue | 38 ++++--
.../components/users/UserManagementContainer.vue | 27 +++-
django_airavata/apps/api/serializers.py | 18 +--
.../api/static/django_airavata_api/js/index.js | 2 +
django_airavata/apps/api/views.py | 11 ++
django_airavata/settings_local_seagrid.py | 147 +++++++++++++++++++++
7 files changed, 265 insertions(+), 22 deletions(-)
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
new file mode 100644
index 0000000..f47101b
--- /dev/null
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserDetailsContainer.vue
@@ -0,0 +1,44 @@
+<template>
+ <user-group-membership-editor
+ v-model="localManagedUserProfile.groups"
+ :editable-groups="editableGroups"
+ @input="groupsUpdated"
+ />
+</template>
+<script>
+import { models } from "django-airavata-api";
+import UserGroupMembershipEditor from "./UserGroupMembershipEditor";
+
+export default {
+ name: "user-details-container",
+ props: {
+ managedUserProfile: {
+ type: models.ManagedUserProfile,
+ required: true
+ },
+ editableGroups: {
+ type: Array,
+ required: true
+ }
+ },
+ components: {
+ UserGroupMembershipEditor
+ },
+ data() {
+ return {
+ localManagedUserProfile: this.managedUserProfile.clone()
+ };
+ },
+ watch: {
+ managedUserProfile(newValue) {
+ this.localManagedUserProfile = newValue.clone();
+ }
+ },
+ methods: {
+ groupsUpdated() {
+ this.$emit("groups-updated", this.localManagedUserProfile);
+ }
+ }
+};
+</script>
+
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserGroupMembershipEditor.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserGroupMembershipEditor.vue
index 494f862..ba8a940 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserGroupMembershipEditor.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserGroupMembershipEditor.vue
@@ -3,16 +3,19 @@
<b-form-checkbox-group
:checked="selected"
:options="options"
+ @input="groupsUpdated"
stacked
></b-form-checkbox-group>
</b-form-group>
</template>
<script>
+import { mixins } from "django-airavata-common-ui";
export default {
name: "user-group-membership-editor",
+ mixins: [mixins.VModelMixin],
props: {
- groups: {
+ value: {
type: Array,
required: true
},
@@ -21,21 +24,17 @@ export default {
required: true
}
},
- data() {
- return {
- }
- },
computed: {
selected() {
- return this.groups.map(g => g.id);
+ return this.data.map(g => g.id);
},
options() {
- const options = this.groups.map(g => {
+ const options = this.data.map(g => {
return {
text: g.name,
value: g.id,
disabled: true
- }
+ };
});
this.editableGroups.forEach(g => {
const editableOption = options.find(opt => opt.value === g.id);
@@ -46,11 +45,30 @@ export default {
text: g.name,
value: g.id,
disabled: false
- })
+ });
}
- })
+ });
+ // TODO: sort the options?
return options;
}
+ },
+ methods: {
+ groupsUpdated(checkedGroups) {
+ // Check for added groups
+ for (const checkedGroupId of checkedGroups) {
+ if (!this.data.find(g => g.id === checkedGroupId)) {
+ const addedGroup = this.editableGroups.find(g => g.id === checkedGroupId);
+ this.data.push(addedGroup);
+ }
+ }
+ // Check for removed groups
+ for (const group of this.data) {
+ if (!checkedGroups.find(groupId => groupId === group.id)) {
+ const groupIndex = this.data.findIndex(g => g.id === group.id);
+ this.data.splice(groupIndex, 1);
+ }
+ }
+ }
}
};
</script>
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserManagementContainer.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserManagementContainer.vue
index dbbcb13..49ca5db 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserManagementContainer.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserManagementContainer.vue
@@ -29,7 +29,11 @@
slot="row-details"
slot-scope="data"
>
- <user-group-membership-editor :groups="data.item.groups" :editableGroups="editableGroups" />
+ <user-details-container
+ :managed-user-profile="data.item"
+ :editable-groups="editableGroups"
+ @groups-updated="groupsUpdated"
+ />
</template>
</b-table>
<pager
@@ -47,7 +51,7 @@
<script>
import { services } from "django-airavata-api";
import { components } from "django-airavata-common-ui";
-import UserGroupMembershipEditor from "./UserGroupMembershipEditor.vue";
+import UserDetailsContainer from "./UserDetailsContainer.vue";
export default {
name: "user-management-container",
@@ -59,7 +63,7 @@ export default {
},
components: {
pager: components.Pager,
- UserGroupMembershipEditor
+ UserDetailsContainer
},
created() {
services.ManagedUserProfileService.list({ limit: 10 }).then(
@@ -109,6 +113,9 @@ export default {
return this.allGroups
? this.allGroups.filter(g => g.isAdmin || g.isOwner)
: [];
+ },
+ currentOffset() {
+ return this.usersPaginator ? this.usersPaginator.offset : 0;
}
},
methods: {
@@ -117,6 +124,20 @@ export default {
},
previous() {
this.usersPaginator.previous();
+ },
+ groupsUpdated(managedUserProfile) {
+ services.ManagedUserProfileService.update({
+ lookup: managedUserProfile.userId,
+ data: managedUserProfile
+ }).finally(() => {
+ this.reloadUserProfiles();
+ });
+ },
+ reloadUserProfiles() {
+ services.ManagedUserProfileService.list({
+ limit: 10,
+ offset: this.currentOffset
+ }).then(users => (this.usersPaginator = users));
}
}
};
diff --git a/django_airavata/apps/api/serializers.py b/django_airavata/apps/api/serializers.py
index bbb66a8..1589702 100644
--- a/django_airavata/apps/api/serializers.py
+++ b/django_airavata/apps/api/serializers.py
@@ -592,15 +592,6 @@ class GroupResourceProfileSerializer(
ResourcePermissionType.WRITE)
-class SharedGroups(serializers.Serializer):
- groupList = serializers.ListField(child=serializers.CharField())
- entityId = serializers.CharField()
-
- def update(self, instance, validated_data):
- instance["groupList"] = validated_data["groupList"]
- return instance
-
-
class UserPermissionSerializer(serializers.Serializer):
user = UserProfileSerializer()
permissionType = serializers.IntegerField()
@@ -830,3 +821,12 @@ class ManagedUserProfile(serializers.Serializer):
view_name='django_airavata_api:managed-user-profile-detail',
lookup_field='userId',
lookup_url_kwarg='user_id')
+
+ def update(self, instance, validated_data):
+ existing_group_ids = [group.id for group in instance['groups']]
+ new_group_ids = [group['id'] for group in validated_data['groups']]
+ instance['_added_group_ids'] = list(
+ set(new_group_ids) - set(existing_group_ids))
+ instance['_removed_group_ids'] = list(
+ set(existing_group_ids) - set(new_group_ids))
+ return instance
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/index.js b/django_airavata/apps/api/static/django_airavata_api/js/index.js
index 49b8f75..67a26d5 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/index.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/index.js
@@ -22,6 +22,7 @@ import GroupComputeResourcePreference from "./models/GroupComputeResourcePrefere
import GroupPermission from "./models/GroupPermission";
import GroupResourceProfile from "./models/GroupResourceProfile";
import InputDataObjectType from "./models/InputDataObjectType";
+import ManagedUserProfile from "./models/ManagedUserProfile";
import OutputDataObjectType from "./models/OutputDataObjectType";
import ParallelismType from "./models/ParallelismType";
import Project from "./models/Project";
@@ -76,6 +77,7 @@ const models = {
GroupPermission,
GroupResourceProfile,
InputDataObjectType,
+ ManagedUserProfile,
OutputDataObjectType,
ParallelismType,
Project,
diff --git a/django_airavata/apps/api/views.py b/django_airavata/apps/api/views.py
index c42d304..b0797ec 100644
--- a/django_airavata/apps/api/views.py
+++ b/django_airavata/apps/api/views.py
@@ -1406,6 +1406,17 @@ class ManagedUserViewSet(mixins.CreateModelMixin,
return self._convert_user_profile(
iam_admin_client.get_user(lookup_value))
+ def perform_update(self, serializer):
+ managed_user_profile = serializer.save()
+ group_manager_client = self.request.profile_service['group_manager']
+ user_id = managed_user_profile['airavataInternalUserId']
+ for group_id in managed_user_profile['_added_group_ids']:
+ group_manager_client.addUsersToGroup(
+ self.authz_token, [user_id], group_id)
+ for group_id in managed_user_profile['_removed_group_ids']:
+ group_manager_client.removeUsersFromGroup(
+ self.authz_token, [user_id], group_id)
+
def _convert_user_profile(self, user_profile):
user_profile_client = self.request.profile_service['user_profile']
group_manager_client = self.request.profile_service['group_manager']
diff --git a/django_airavata/settings_local_seagrid.py b/django_airavata/settings_local_seagrid.py
new file mode 100644
index 0000000..c193a3a
--- /dev/null
+++ b/django_airavata/settings_local_seagrid.py
@@ -0,0 +1,147 @@
+"""
+Override default Django settings for a particular instance.
+
+Copy this file to settings_local.py and modify as appropriate. This file will
+be imported into settings.py last of all so settings in this file override any
+defaults specified in settings.py.
+"""
+
+import os
+
+from . import webpack_loader_util
+
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+# Django configuration
+
+DEBUG = True
+ALLOWED_HOSTS = ['localhost']
+
+# Django - Email Settings
+# Uncomment and specify the following for sending emails (default email backend
+# just prints to the console)
+# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
+# EMAIL_HOST = 'smtp.gmail.com'
+# EMAIL_PORT = 587
+# EMAIL_HOST_USER = 'pga.airavata@gmail.com'
+# EMAIL_HOST_PASSWORD = 'airavata12'
+# EMAIL_USE_TLS = True
+ADMINS = [('Marcus Christie', 'machrist@iu.edu')]
+SERVER_EMAIL = 'pga.airavata@gmail.com'
+
+# Keycloak Configuration
+KEYCLOAK_CLIENT_ID = 'pga'
+KEYCLOAK_CLIENT_SECRET = '5d2dc66a-f54e-4fa9-b78f-80d33aa862c1'
+KEYCLOAK_AUTHORIZE_URL = 'https://iamdev.scigap.org/auth/realms/seagrid/protocol/openid-connect/auth'
+KEYCLOAK_TOKEN_URL = 'https://iamdev.scigap.org/auth/realms/seagrid/protocol/openid-connect/token'
+KEYCLOAK_USERINFO_URL = 'https://iamdev.scigap.org/auth/realms/seagrid/protocol/openid-connect/userinfo'
+KEYCLOAK_LOGOUT_URL = 'https://iamdev.scigap.org/auth/realms/seagrid/protocol/openid-connect/logout'
+KEYCLOAK_CA_CERTFILE = os.path.join(
+ BASE_DIR,
+ "django_airavata",
+ "resources",
+ "incommon_rsa_server_ca.pem")
+KEYCLOAK_VERIFY_SSL = True
+
+AUTHENTICATION_OPTIONS = {
+ 'password': {
+ 'name': 'SEAGrid'
+ },
+ 'external': [
+ {
+ 'idp_alias': 'oidc',
+ 'name': 'CILogon',
+ },
+ # {
+ # 'idp_alias': 'iucas',
+ # 'name': 'IU CAS',
+ # },
+ ]
+}
+
+# Airavata API Configuration
+GATEWAY_ID = 'seagrid'
+AIRAVATA_API_HOST = 'apidev.scigap.org'
+AIRAVATA_API_PORT = 9930
+AIRAVATA_API_SECURE = True
+# AIRAVATA_API_HOST = 'localhost'
+# AIRAVATA_API_PORT = 8930
+# AIRAVATA_API_SECURE = False
+GATEWAY_DATA_STORE_RESOURCE_ID = 'pgadev.scigap.org_7ddf28fd-d503-4ff8-bbc5-3279a7c3b99e'
+GATEWAY_DATA_STORE_DIR = '/tmp/experiment-data-dir'
+GATEWAY_DATA_STORE_HOSTNAME = 'localhost'
+
+# Profile Service Configuration
+PROFILE_SERVICE_HOST = AIRAVATA_API_HOST
+PROFILE_SERVICE_PORT = 8962
+PROFILE_SERVICE_SECURE = False
+
+# Sharing API Configuration
+SHARING_API_HOST = AIRAVATA_API_HOST
+SHARING_API_PORT = 7878
+SHARING_API_SECURE = False
+
+# Portal settings
+PORTAL_TITLE = 'Django Airavata Gateway'
+
+# Logging configuration
+LOGGING = {
+ 'version': 1,
+ 'disable_existing_loggers': False,
+ 'formatters': {
+ 'verbose': {
+ 'format': '[%(asctime)s %(name)s:%(lineno)d %(levelname)s] %(message)s'
+ },
+ },
+ 'filters': {
+ 'require_debug_true': {
+ '()': 'django.utils.log.RequireDebugTrue',
+ },
+ },
+ 'handlers': {
+ 'console': {
+ 'filters': ['require_debug_true'],
+ 'class': 'logging.StreamHandler',
+ 'formatter': 'verbose',
+ 'level': 'DEBUG',
+ },
+ # 'file': {
+ # 'class': 'logging.FileHandler',
+ # 'filename': 'django_airavata.log',
+ # 'formatter': 'verbose',
+ # },
+ },
+ 'loggers': {
+ 'django': {
+ 'handlers': ['console'],
+ 'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
+ },
+ 'airavata': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ },
+ 'django_airavata': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ },
+ 'django_airavata.utils': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ },
+ 'thrift_connector': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ },
+ 'simccs_maptool': {
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ },
+ # 'requests_oauthlib': {
+ # 'handlers': ['console'],
+ # 'level': 'DEBUG',
+ # },
+ },
+}
+
+# WEBPACK_LOADER = webpack_loader_util.create_webpack_loader_config("/tmp")