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/28 16:54:14 UTC
[airavata-django-portal] branch master updated: AIRAVATA-2988
Initial WIP user management view
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
The following commit(s) were added to refs/heads/master by this push:
new 5609380 AIRAVATA-2988 Initial WIP user management view
5609380 is described below
commit 5609380c7b01cb9abae0bf96c522a1124a81cdfb
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Tue May 28 12:33:12 2019 -0400
AIRAVATA-2988 Initial WIP user management view
---
.../iam/admin/services/cpi/IamAdminServices-remote | 7 +
.../iam/admin/services/cpi/IamAdminServices.py | 302 ++++++++++++++++++++-
.../components/users/UserManagementContainer.vue | 96 +++++++
.../static/django_airavata_admin/src/router.js | 6 +
.../apps/admin/templates/admin/admin_base.html | 5 +
django_airavata/apps/admin/urls.py | 1 +
django_airavata/apps/admin/views.py | 9 +-
django_airavata/apps/api/serializers.py | 16 ++
.../api/static/django_airavata_api/js/index.js | 1 +
.../js/models/ManagedUserProfile.js | 25 ++
.../django_airavata_api/js/service_config.js | 12 +-
django_airavata/apps/api/urls.py | 2 +
django_airavata/apps/api/views.py | 41 +++
django_airavata/apps/auth/iam_admin_client.py | 5 +
14 files changed, 512 insertions(+), 16 deletions(-)
diff --git a/airavata/service/profile/iam/admin/services/cpi/IamAdminServices-remote b/airavata/service/profile/iam/admin/services/cpi/IamAdminServices-remote
index ba10cb0..b08c976 100755
--- a/airavata/service/profile/iam/admin/services/cpi/IamAdminServices-remote
+++ b/airavata/service/profile/iam/admin/services/cpi/IamAdminServices-remote
@@ -32,6 +32,7 @@ if len(sys.argv) <= 1 or sys.argv[1] == '--help':
print(' bool isUserEnabled(AuthzToken authzToken, string username)')
print(' bool isUserExist(AuthzToken authzToken, string username)')
print(' UserProfile getUser(AuthzToken authzToken, string username)')
+ print(' getUsers(AuthzToken authzToken, i32 offset, i32 limit, string search)')
print(' bool resetUserPassword(AuthzToken authzToken, string username, string newPassword)')
print(' findUsers(AuthzToken authzToken, string email, string userId)')
print(' void updateUserProfile(AuthzToken authzToken, UserProfile userDetails)')
@@ -165,6 +166,12 @@ elif cmd == 'getUser':
sys.exit(1)
pp.pprint(client.getUser(eval(args[0]), args[1],))
+elif cmd == 'getUsers':
+ if len(args) != 4:
+ print('getUsers requires 4 args')
+ sys.exit(1)
+ pp.pprint(client.getUsers(eval(args[0]), eval(args[1]), eval(args[2]), args[3],))
+
elif cmd == 'resetUserPassword':
if len(args) != 3:
print('resetUserPassword requires 3 args')
diff --git a/airavata/service/profile/iam/admin/services/cpi/IamAdminServices.py b/airavata/service/profile/iam/admin/services/cpi/IamAdminServices.py
index 2e6fac8..7a5ac8b 100644
--- a/airavata/service/profile/iam/admin/services/cpi/IamAdminServices.py
+++ b/airavata/service/profile/iam/admin/services/cpi/IamAdminServices.py
@@ -79,6 +79,16 @@ class Iface(object):
"""
pass
+ def getUsers(self, authzToken, offset, limit, search):
+ """
+ Parameters:
+ - authzToken
+ - offset
+ - limit
+ - search
+ """
+ pass
+
def resetUserPassword(self, authzToken, username, newPassword):
"""
Parameters:
@@ -434,6 +444,47 @@ class Client(Iface):
raise result.ae
raise TApplicationException(TApplicationException.MISSING_RESULT, "getUser failed: unknown result")
+ def getUsers(self, authzToken, offset, limit, search):
+ """
+ Parameters:
+ - authzToken
+ - offset
+ - limit
+ - search
+ """
+ self.send_getUsers(authzToken, offset, limit, search)
+ return self.recv_getUsers()
+
+ def send_getUsers(self, authzToken, offset, limit, search):
+ self._oprot.writeMessageBegin('getUsers', TMessageType.CALL, self._seqid)
+ args = getUsers_args()
+ args.authzToken = authzToken
+ args.offset = offset
+ args.limit = limit
+ args.search = search
+ args.write(self._oprot)
+ self._oprot.writeMessageEnd()
+ self._oprot.trans.flush()
+
+ def recv_getUsers(self):
+ iprot = self._iprot
+ (fname, mtype, rseqid) = iprot.readMessageBegin()
+ if mtype == TMessageType.EXCEPTION:
+ x = TApplicationException()
+ x.read(iprot)
+ iprot.readMessageEnd()
+ raise x
+ result = getUsers_result()
+ result.read(iprot)
+ iprot.readMessageEnd()
+ if result.success is not None:
+ return result.success
+ if result.Idse is not None:
+ raise result.Idse
+ if result.ae is not None:
+ raise result.ae
+ raise TApplicationException(TApplicationException.MISSING_RESULT, "getUsers failed: unknown result")
+
def resetUserPassword(self, authzToken, username, newPassword):
"""
Parameters:
@@ -675,6 +726,7 @@ class Processor(Iface, TProcessor):
self._processMap["isUserEnabled"] = Processor.process_isUserEnabled
self._processMap["isUserExist"] = Processor.process_isUserExist
self._processMap["getUser"] = Processor.process_getUser
+ self._processMap["getUsers"] = Processor.process_getUsers
self._processMap["resetUserPassword"] = Processor.process_resetUserPassword
self._processMap["findUsers"] = Processor.process_findUsers
self._processMap["updateUserProfile"] = Processor.process_updateUserProfile
@@ -894,6 +946,31 @@ class Processor(Iface, TProcessor):
oprot.writeMessageEnd()
oprot.trans.flush()
+ def process_getUsers(self, seqid, iprot, oprot):
+ args = getUsers_args()
+ args.read(iprot)
+ iprot.readMessageEnd()
+ result = getUsers_result()
+ try:
+ result.success = self._handler.getUsers(args.authzToken, args.offset, args.limit, args.search)
+ msg_type = TMessageType.REPLY
+ except (TTransport.TTransportException, KeyboardInterrupt, SystemExit):
+ raise
+ except airavata.service.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException as Idse:
+ msg_type = TMessageType.REPLY
+ result.Idse = Idse
+ except airavata.api.error.ttypes.AuthorizationException as ae:
+ msg_type = TMessageType.REPLY
+ result.ae = ae
+ except Exception as ex:
+ msg_type = TMessageType.EXCEPTION
+ logging.exception(ex)
+ result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+ oprot.writeMessageBegin("getUsers", msg_type, seqid)
+ result.write(oprot)
+ oprot.writeMessageEnd()
+ oprot.trans.flush()
+
def process_resetUserPassword(self, seqid, iprot, oprot):
args = resetUserPassword_args()
args.read(iprot)
@@ -2354,6 +2431,203 @@ class getUser_result(object):
return not (self == other)
+class getUsers_args(object):
+ """
+ Attributes:
+ - authzToken
+ - offset
+ - limit
+ - search
+ """
+
+ thrift_spec = (
+ None, # 0
+ (1, TType.STRUCT, 'authzToken', (airavata.model.security.ttypes.AuthzToken, airavata.model.security.ttypes.AuthzToken.thrift_spec), None, ), # 1
+ (2, TType.I32, 'offset', None, None, ), # 2
+ (3, TType.I32, 'limit', None, None, ), # 3
+ (4, TType.STRING, 'search', 'UTF8', None, ), # 4
+ )
+
+ def __init__(self, authzToken=None, offset=None, limit=None, search=None,):
+ self.authzToken = authzToken
+ self.offset = offset
+ self.limit = limit
+ self.search = search
+
+ def read(self, iprot):
+ if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+ iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec))
+ return
+ iprot.readStructBegin()
+ while True:
+ (fname, ftype, fid) = iprot.readFieldBegin()
+ if ftype == TType.STOP:
+ break
+ if fid == 1:
+ if ftype == TType.STRUCT:
+ self.authzToken = airavata.model.security.ttypes.AuthzToken()
+ self.authzToken.read(iprot)
+ else:
+ iprot.skip(ftype)
+ elif fid == 2:
+ if ftype == TType.I32:
+ self.offset = iprot.readI32()
+ else:
+ iprot.skip(ftype)
+ elif fid == 3:
+ if ftype == TType.I32:
+ self.limit = iprot.readI32()
+ else:
+ iprot.skip(ftype)
+ elif fid == 4:
+ if ftype == TType.STRING:
+ self.search = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString()
+ else:
+ iprot.skip(ftype)
+ else:
+ iprot.skip(ftype)
+ iprot.readFieldEnd()
+ iprot.readStructEnd()
+
+ def write(self, oprot):
+ if oprot._fast_encode is not None and self.thrift_spec is not None:
+ oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec)))
+ return
+ oprot.writeStructBegin('getUsers_args')
+ if self.authzToken is not None:
+ oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+ self.authzToken.write(oprot)
+ oprot.writeFieldEnd()
+ if self.offset is not None:
+ oprot.writeFieldBegin('offset', TType.I32, 2)
+ oprot.writeI32(self.offset)
+ oprot.writeFieldEnd()
+ if self.limit is not None:
+ oprot.writeFieldBegin('limit', TType.I32, 3)
+ oprot.writeI32(self.limit)
+ oprot.writeFieldEnd()
+ if self.search is not None:
+ oprot.writeFieldBegin('search', TType.STRING, 4)
+ oprot.writeString(self.search.encode('utf-8') if sys.version_info[0] == 2 else self.search)
+ oprot.writeFieldEnd()
+ oprot.writeFieldStop()
+ oprot.writeStructEnd()
+
+ def validate(self):
+ if self.authzToken is None:
+ raise TProtocolException(message='Required field authzToken is unset!')
+ if self.offset is None:
+ raise TProtocolException(message='Required field offset is unset!')
+ if self.limit is None:
+ raise TProtocolException(message='Required field limit is unset!')
+ return
+
+ def __repr__(self):
+ L = ['%s=%r' % (key, value)
+ for key, value in self.__dict__.items()]
+ return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+ def __eq__(self, other):
+ return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+ def __ne__(self, other):
+ return not (self == other)
+
+
+class getUsers_result(object):
+ """
+ Attributes:
+ - success
+ - Idse
+ - ae
+ """
+
+ thrift_spec = (
+ (0, TType.LIST, 'success', (TType.STRUCT, (airavata.model.user.ttypes.UserProfile, airavata.model.user.ttypes.UserProfile.thrift_spec), False), None, ), # 0
+ (1, TType.STRUCT, 'Idse', (airavata.service.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException, airavata.service.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException.thrift_spec), None, ), # 1
+ (2, TType.STRUCT, 'ae', (airavata.api.error.ttypes.AuthorizationException, airavata.api.error.ttypes.AuthorizationException.thrift_spec), None, ), # 2
+ )
+
+ def __init__(self, success=None, Idse=None, ae=None,):
+ self.success = success
+ self.Idse = Idse
+ self.ae = ae
+
+ def read(self, iprot):
+ if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None:
+ iprot._fast_decode(self, iprot, (self.__class__, self.thrift_spec))
+ return
+ iprot.readStructBegin()
+ while True:
+ (fname, ftype, fid) = iprot.readFieldBegin()
+ if ftype == TType.STOP:
+ break
+ if fid == 0:
+ if ftype == TType.LIST:
+ self.success = []
+ (_etype3, _size0) = iprot.readListBegin()
+ for _i4 in range(_size0):
+ _elem5 = airavata.model.user.ttypes.UserProfile()
+ _elem5.read(iprot)
+ self.success.append(_elem5)
+ iprot.readListEnd()
+ else:
+ iprot.skip(ftype)
+ elif fid == 1:
+ if ftype == TType.STRUCT:
+ self.Idse = airavata.service.profile.iam.admin.services.cpi.error.ttypes.IamAdminServicesException()
+ self.Idse.read(iprot)
+ else:
+ iprot.skip(ftype)
+ elif fid == 2:
+ if ftype == TType.STRUCT:
+ self.ae = airavata.api.error.ttypes.AuthorizationException()
+ self.ae.read(iprot)
+ else:
+ iprot.skip(ftype)
+ else:
+ iprot.skip(ftype)
+ iprot.readFieldEnd()
+ iprot.readStructEnd()
+
+ def write(self, oprot):
+ if oprot._fast_encode is not None and self.thrift_spec is not None:
+ oprot.trans.write(oprot._fast_encode(self, (self.__class__, self.thrift_spec)))
+ return
+ oprot.writeStructBegin('getUsers_result')
+ if self.success is not None:
+ oprot.writeFieldBegin('success', TType.LIST, 0)
+ oprot.writeListBegin(TType.STRUCT, len(self.success))
+ for iter6 in self.success:
+ iter6.write(oprot)
+ oprot.writeListEnd()
+ oprot.writeFieldEnd()
+ if self.Idse is not None:
+ oprot.writeFieldBegin('Idse', TType.STRUCT, 1)
+ self.Idse.write(oprot)
+ oprot.writeFieldEnd()
+ if self.ae is not None:
+ oprot.writeFieldBegin('ae', TType.STRUCT, 2)
+ self.ae.write(oprot)
+ oprot.writeFieldEnd()
+ oprot.writeFieldStop()
+ oprot.writeStructEnd()
+
+ def validate(self):
+ return
+
+ def __repr__(self):
+ L = ['%s=%r' % (key, value)
+ for key, value in self.__dict__.items()]
+ return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+ def __eq__(self, other):
+ return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
+
+ def __ne__(self, other):
+ return not (self == other)
+
+
class resetUserPassword_args(object):
"""
Attributes:
@@ -2652,11 +2926,11 @@ class findUsers_result(object):
if fid == 0:
if ftype == TType.LIST:
self.success = []
- (_etype3, _size0) = iprot.readListBegin()
- for _i4 in range(_size0):
- _elem5 = airavata.model.user.ttypes.UserProfile()
- _elem5.read(iprot)
- self.success.append(_elem5)
+ (_etype10, _size7) = iprot.readListBegin()
+ for _i11 in range(_size7):
+ _elem12 = airavata.model.user.ttypes.UserProfile()
+ _elem12.read(iprot)
+ self.success.append(_elem12)
iprot.readListEnd()
else:
iprot.skip(ftype)
@@ -2685,8 +2959,8 @@ class findUsers_result(object):
if self.success is not None:
oprot.writeFieldBegin('success', TType.LIST, 0)
oprot.writeListBegin(TType.STRUCT, len(self.success))
- for iter6 in self.success:
- iter6.write(oprot)
+ for iter13 in self.success:
+ iter13.write(oprot)
oprot.writeListEnd()
oprot.writeFieldEnd()
if self.Idse is not None:
@@ -3327,11 +3601,11 @@ class getUsersWithRole_result(object):
if fid == 0:
if ftype == TType.LIST:
self.success = []
- (_etype10, _size7) = iprot.readListBegin()
- for _i11 in range(_size7):
- _elem12 = airavata.model.user.ttypes.UserProfile()
- _elem12.read(iprot)
- self.success.append(_elem12)
+ (_etype17, _size14) = iprot.readListBegin()
+ for _i18 in range(_size14):
+ _elem19 = airavata.model.user.ttypes.UserProfile()
+ _elem19.read(iprot)
+ self.success.append(_elem19)
iprot.readListEnd()
else:
iprot.skip(ftype)
@@ -3360,8 +3634,8 @@ class getUsersWithRole_result(object):
if self.success is not None:
oprot.writeFieldBegin('success', TType.LIST, 0)
oprot.writeListBegin(TType.STRUCT, len(self.success))
- for iter13 in self.success:
- iter13.write(oprot)
+ for iter20 in self.success:
+ iter20.write(oprot)
oprot.writeListEnd()
oprot.writeFieldEnd()
if self.Idse is not None:
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
new file mode 100644
index 0000000..4a5d7c0
--- /dev/null
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserManagementContainer.vue
@@ -0,0 +1,96 @@
+<template>
+ <div>
+ <div class="row">
+ <div class="col">
+ <h1 class="h4 mb-4">Users</h1>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col">
+ <div class="card">
+ <div class="card-body">
+ <b-table
+ hover
+ :fields="fields"
+ :items="items"
+ >
+ </b-table>
+ <pager
+ v-bind:paginator="usersPaginator"
+ v-on:next="next"
+ v-on:previous="previous"
+ ></pager>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script>
+import { services } from "django-airavata-api";
+import { components } from "django-airavata-common-ui";
+
+export default {
+ name: "user-management-container",
+ data() {
+ return {
+ usersPaginator: null
+ };
+ },
+ components: {
+ pager: components.Pager
+ },
+ created() {
+ services.ManagedUserProfileService.list({ limit: 10 }).then(
+ users => (this.usersPaginator = users)
+ );
+ },
+ computed: {
+ fields() {
+ return [
+ {
+ label: "First Name",
+ key: "firstName"
+ },
+ {
+ label: "Last Name",
+ key: "lastName"
+ },
+ {
+ label: "Username",
+ key: "userId"
+ },
+ {
+ label: "Email",
+ key: "email"
+ },
+ {
+ label: "Enabled",
+ key: "enabled"
+ },
+ {
+ label: "Email Verified",
+ key: "emailVerified"
+ },
+ {
+ label: "Action",
+ key: "action"
+ }
+ ];
+ },
+ items() {
+ return this.usersPaginator ? this.usersPaginator.results : [];
+ }
+ },
+ methods: {
+ next() {
+ this.usersPaginator.next();
+ },
+ previous() {
+ this.usersPaginator.previous();
+ }
+ }
+};
+</script>
+
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/router.js b/django_airavata/apps/admin/static/django_airavata_admin/src/router.js
index 942cc64..be3b2c0 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/router.js
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/router.js
@@ -9,6 +9,7 @@ import ComputeResourcePreferenceDashboard from "./components/dashboards/ComputeR
import CredentialStoreDashboard from "./components/dashboards/CredentialStoreDashboard";
import GatewayResourceProfileEditorContainer from "./components/gatewayprofile/GatewayResourceProfileEditorContainer.vue";
import GroupComputeResourcePreference from "./components/admin/group_resource_preferences/GroupComputeResourcePreference";
+import UserManagementContainer from "./components/users/UserManagementContainer.vue";
import VueRouter from "vue-router";
const routes = [
@@ -116,6 +117,11 @@ const routes = [
path: "/gateway-resource-profile",
component: GatewayResourceProfileEditorContainer,
name: "gateway-resource-profile"
+ },
+ {
+ path: "/users",
+ component: UserManagementContainer,
+ name: "users"
}
];
export default new VueRouter({
diff --git a/django_airavata/apps/admin/templates/admin/admin_base.html b/django_airavata/apps/admin/templates/admin/admin_base.html
index 57fd3c3..e883735 100644
--- a/django_airavata/apps/admin/templates/admin/admin_base.html
+++ b/django_airavata/apps/admin/templates/admin/admin_base.html
@@ -14,6 +14,11 @@
<i class="fa fa-cogs"></i> <span class=sr-only>Application Catalog</span>
</a>
{% endif %}
+ {% if request.is_gateway_admin %}
+ <a href="{% url 'django_airavata_admin:users' %}" class="c-nav__item {% if request.active_nav_item == 'users' %}is-active{% endif %}" data-toggle=tooltip data-placement=right title="Manage Users">
+ <i class="fa fa-users"></i> <span class=sr-only>Manage Users</span>
+ </a>
+ {% endif %}
<a href="{% url 'django_airavata_admin:credential_store' %}" class="c-nav__item {% if request.active_nav_item == 'credential_store' %}is-active{% endif %}" data-toggle=tooltip data-placement=right title="Credential Store">
<i class="fa fa-lock"></i> <span class=sr-only>Credential Store</span>
</a>
diff --git a/django_airavata/apps/admin/urls.py b/django_airavata/apps/admin/urls.py
index 47ab077..d706078 100644
--- a/django_airavata/apps/admin/urls.py
+++ b/django_airavata/apps/admin/urls.py
@@ -11,4 +11,5 @@ urlpatterns = [
name='group_resource_profile'),
url(r'^gateway-resource-profile/', views.gateway_resource_profile,
name='gateway_resource_profile'),
+ url(r'^users/', views.users, name='users'),
]
diff --git a/django_airavata/apps/admin/views.py b/django_airavata/apps/admin/views.py
index 58c94d0..627febf 100644
--- a/django_airavata/apps/admin/views.py
+++ b/django_airavata/apps/admin/views.py
@@ -8,7 +8,8 @@ def home(request):
if request.is_gateway_admin or request.is_read_only_gateway_admin:
return redirect(reverse('django_airavata_admin:app_catalog'))
else:
- return redirect(reverse('django_airavata_admin:group_resource_profile'))
+ return redirect(
+ reverse('django_airavata_admin:group_resource_profile'))
@login_required
@@ -38,3 +39,9 @@ def group_resource_profile(request):
def gateway_resource_profile(request):
request.active_nav_item = 'gateway_resource_profile'
return render(request, 'admin/admin_base.html')
+
+
+@login_required
+def users(request):
+ request.active_nav_item = 'users'
+ return render(request, 'admin/admin_base.html')
diff --git a/django_airavata/apps/api/serializers.py b/django_airavata/apps/api/serializers.py
index 5f0685c..b36524e 100644
--- a/django_airavata/apps/api/serializers.py
+++ b/django_airavata/apps/api/serializers.py
@@ -753,3 +753,19 @@ class WorkspacePreferencesSerializer(serializers.ModelSerializer):
class Meta:
model = models.WorkspacePreferences
exclude = ('username',)
+
+
+class ManagedUserProfile(serializers.Serializer):
+ airavataInternalUserId = serializers.CharField()
+ userId = serializers.CharField()
+ gatewayId = serializers.CharField()
+ email = serializers.CharField()
+ firstName = serializers.CharField()
+ lastName = serializers.CharField()
+ enabled = serializers.BooleanField()
+ emailVerified = serializers.BooleanField()
+ groups = GroupSerializer(many=True)
+ url = FullyEncodedHyperlinkedIdentityField(
+ view_name='django_airavata_api:managed-user-profile-detail',
+ lookup_field='userId',
+ lookup_url_kwarg='user_id')
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 c4d24c8..19c53a5 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
@@ -106,6 +106,7 @@ const services = {
GroupResourceProfileService: ServiceFactory.service("GroupResourceProfiles"),
GroupService: ServiceFactory.service("Groups"),
LocaJobSubmissionService,
+ ManagedUserProfileService: ServiceFactory.service("ManagedUserProfiles"),
ParserService: ServiceFactory.service("Parsers"),
ProjectService: ServiceFactory.service("Projects"),
SCPDataMovementService,
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/ManagedUserProfile.js b/django_airavata/apps/api/static/django_airavata_api/js/models/ManagedUserProfile.js
new file mode 100644
index 0000000..d29065a
--- /dev/null
+++ b/django_airavata/apps/api/static/django_airavata_api/js/models/ManagedUserProfile.js
@@ -0,0 +1,25 @@
+import BaseModel from "./BaseModel";
+import Group from "./Group";
+
+const FIELDS = [
+ "userModelVersion",
+ "airavataInternalUserId",
+ "userId",
+ "gatewayId",
+ "email",
+ "firstName",
+ "lastName",
+ "enabled",
+ "emailVerified",
+ {
+ name: "groups",
+ type: Group,
+ list: true
+ }
+];
+
+export default class ManagedUserProfile extends BaseModel {
+ constructor(data = {}) {
+ super(FIELDS, data);
+ }
+}
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 9cdc380..ac651b7 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
@@ -12,6 +12,7 @@ import FullExperiment from "./models/FullExperiment";
import GatewayResourceProfile from "./models/GatewayResourceProfile";
import Group from "./models/Group";
import GroupResourceProfile from "./models/GroupResourceProfile";
+import ManagedUserProfile from "./models/ManagedUserProfile";
import Parser from "./models/Parser";
import Project from "./models/Project";
import SharedEntity from "./models/SharedEntity";
@@ -181,7 +182,9 @@ export default {
],
modelClass: ExperimentSummary,
pagination: true,
- queryParams: ["limit", "offset"].concat(ExperimentSearchFields.values.map(f => f.name))
+ queryParams: ["limit", "offset"].concat(
+ ExperimentSearchFields.values.map(f => f.name)
+ )
},
FullExperiments: {
url: "/api/full-experiments",
@@ -217,6 +220,13 @@ export default {
queryParams: ["limit", "offset"],
modelClass: Group
},
+ ManagedUserProfiles: {
+ url: "/api/managed-user-profiles",
+ viewSet: true,
+ pagination: true,
+ queryParams: ["limit", "offset", "search"],
+ modelClass: ManagedUserProfile
+ },
Parsers: {
url: "/api/parsers",
viewSet: true,
diff --git a/django_airavata/apps/api/urls.py b/django_airavata/apps/api/urls.py
index d835967..d7c716f 100644
--- a/django_airavata/apps/api/urls.py
+++ b/django_airavata/apps/api/urls.py
@@ -41,6 +41,8 @@ router.register(r'storage-preferences',
views.StoragePreferenceViewSet,
base_name='storage-preference')
router.register(r'parsers', views.ParserViewSet, base_name='parser')
+router.register(r'managed-user-profiles', views.ManagedUserViewSet,
+ base_name='managed-user-profile')
app_name = 'django_airavata_api'
urlpatterns = [
diff --git a/django_airavata/apps/api/views.py b/django_airavata/apps/api/views.py
index 989e4ae..bf25e84 100644
--- a/django_airavata/apps/api/views.py
+++ b/django_airavata/apps/api/views.py
@@ -31,12 +31,14 @@ from airavata.model.data.movement.ttypes import (
)
from airavata.model.experiment.ttypes import ExperimentSearchFields
from airavata.model.group.ttypes import ResourcePermissionType
+from airavata.model.user.ttypes import Status
from django_airavata.apps.api.view_utils import (
APIBackedViewSet,
APIResultIterator,
APIResultPagination,
GenericAPIBackedViewSet
)
+from django_airavata.apps.auth import iam_admin_client
from django_airavata.apps.workspace.models import User_Files
from . import datastore, helpers, models, serializers, thrift_utils
@@ -1361,3 +1363,42 @@ class WorkspacePreferencesView(APIView):
serializer = self.serializer_class(
workspace_preferences, context={'request': request})
return Response(serializer.data)
+
+
+class ManagedUserViewSet(mixins.CreateModelMixin,
+ mixins.RetrieveModelMixin,
+ mixins.UpdateModelMixin,
+ mixins.ListModelMixin,
+ GenericAPIBackedViewSet):
+ serializer_class = serializers.ManagedUserProfile
+ pagination_class = APIResultPagination
+ lookup_field = 'user_id'
+
+ def get_list(self):
+ search = self.request.GET.get('search', None)
+
+ convert_user_profile = self._convert_user_profile
+
+ class ManagedUsersResultIterator(APIResultIterator):
+ def get_results(self, limit=-1, offset=0):
+ return map(convert_user_profile,
+ iam_admin_client.get_users(offset, limit, search))
+ return ManagedUsersResultIterator()
+
+ def get_instance(self, lookup_value):
+ return self._convert_user_profile(
+ iam_admin_client.get_user(lookup_value))
+
+ def _convert_user_profile(self, user_profile):
+ return {
+ 'airavataInternalUserId': user_profile.airavataInternalUserId,
+ 'userId': user_profile.userId,
+ 'gatewayId': user_profile.gatewayId,
+ 'email': user_profile.emails[0],
+ 'firstName': user_profile.firstName,
+ 'lastName': user_profile.lastName,
+ # TODO: fix this to distinguish between enabled and emailVerified
+ 'enabled': user_profile.State == Status.CONFIRMED,
+ 'emailVerified': user_profile.State == Status.CONFIRMED,
+ 'groups': []
+ }
diff --git a/django_airavata/apps/auth/iam_admin_client.py b/django_airavata/apps/auth/iam_admin_client.py
index 6dc2ceb..05e3b2b 100644
--- a/django_airavata/apps/auth/iam_admin_client.py
+++ b/django_airavata/apps/auth/iam_admin_client.py
@@ -47,6 +47,11 @@ def get_user(username):
return iamadmin_client_pool.getUser(authz_token, username)
+def get_users(offset, limit, search=None):
+ authz_token = utils.get_service_account_authz_token()
+ return iamadmin_client_pool.getUsers(authz_token, offset, limit, search)
+
+
def reset_user_password(username, new_password):
authz_token = utils.get_service_account_authz_token()
return iamadmin_client_pool.resetUserPassword(