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/06/20 15:21:05 UTC

[airavata-django-portal] 01/06: AIRAVATA-3047 Separate service to load users with unverified emails

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 cab1814f2c5da1938c06e753aba6727fcd0f2bc0
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Sat Jun 15 16:44:00 2019 -0400

    AIRAVATA-3047 Separate service to load users with unverified emails
---
 ... => IdentityServiceUserManagementContainer.vue} |  19 +-
 .../UnverifiedEmailUserManagementContainer.vue     |  93 +++++++++
 .../src/components/users/UserDetailsContainer.vue  |  16 +-
 .../components/users/UserManagementContainer.vue   | 207 ++-------------------
 .../static/django_airavata_admin/src/router.js     |  12 +-
 django_airavata/apps/api/serializers.py            |  19 +-
 .../api/static/django_airavata_api/js/index.js     |   7 +-
 .../{ManagedUserProfile.js => IAMUserProfile.js}   |   2 +-
 ...serProfile.js => UnverifiedEmailUserProfile.js} |  11 +-
 .../django_airavata_api/js/service_config.js       |  16 +-
 django_airavata/apps/api/urls.py                   |   6 +-
 django_airavata/apps/api/views.py                  |  80 ++++++--
 12 files changed, 249 insertions(+), 239 deletions(-)

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/IdentityServiceUserManagementContainer.vue
similarity index 91%
copy from django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserManagementContainer.vue
copy to django_airavata/apps/admin/static/django_airavata_admin/src/components/users/IdentityServiceUserManagementContainer.vue
index 92e5c25..dba6213 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/IdentityServiceUserManagementContainer.vue
@@ -2,11 +2,6 @@
   <div>
     <div class="row">
       <div class="col">
-        <h1 class="h4 mb-4">Manage Users</h1>
-      </div>
-    </div>
-    <div class="row">
-      <div class="col">
         <div class="card">
           <div class="card-body">
             <b-input-group>
@@ -57,7 +52,7 @@
                 slot-scope="data"
               >
                 <user-details-container
-                  :managed-user-profile="data.item"
+                  :iam-user-profile="data.item"
                   :editable-groups="editableGroups"
                   @groups-updated="groupsUpdated"
                 />
@@ -96,7 +91,7 @@ export default {
     UserDetailsContainer
   },
   created() {
-    services.ManagedUserProfileService.list({ limit: 10 }).then(
+    services.IAMUserProfileService.list({ limit: 10 }).then(
       users => (this.usersPaginator = users)
     );
     services.GroupService.list({ limit: -1 }).then(
@@ -166,10 +161,10 @@ export default {
     previous() {
       this.usersPaginator.previous();
     },
-    groupsUpdated(managedUserProfile) {
-      services.ManagedUserProfileService.update({
-        lookup: managedUserProfile.userId,
-        data: managedUserProfile
+    groupsUpdated(user) {
+      services.IAMUserProfileService.update({
+        lookup: user.userId,
+        data: user
       }).finally(() => {
         this.reloadUserProfiles();
       });
@@ -182,7 +177,7 @@ export default {
       if (this.search) {
         params["search"] = this.search;
       }
-      services.ManagedUserProfileService.list(params).then(
+      services.IAMUserProfileService.list(params).then(
         users => (this.usersPaginator = users)
       );
     },
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UnverifiedEmailUserManagementContainer.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UnverifiedEmailUserManagementContainer.vue
new file mode 100644
index 0000000..5cb7edd
--- /dev/null
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UnverifiedEmailUserManagementContainer.vue
@@ -0,0 +1,93 @@
+<template>
+  <div>
+    <div class="row">
+      <div class="col">
+        <div class="card">
+          <div class="card-body">
+            <b-table
+              hover
+              :fields="fields"
+              :items="items"
+            >
+              <template
+                slot="creationTime"
+                slot-scope="data">
+                <human-date :date="data.value"/>
+              </template>
+            </b-table>
+            <pager
+              v-bind:paginator="usersPaginator"
+              v-on:next="next"
+              v-on:previous="previous"
+            ></pager>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import { components } from "django-airavata-common-ui";
+import { services } from "django-airavata-api";
+export default {
+  name: "unverified-email-user-management-container",
+  data() {
+    return {
+      usersPaginator: null,
+    };
+  },
+  components: {
+    pager: components.Pager,
+    'human-date': components.HumanDate,
+  },
+  created() {
+    services.UnverifiedEmailUserProfileService.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: "Email Verified",
+          key: "emailVerified"
+        },
+        {
+          label: "Created",
+          key: "creationTime"
+        },
+      ];
+    },
+    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/components/users/UserDetailsContainer.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/UserDetailsContainer.vue
index 33f96c5..18ce57f 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
@@ -1,8 +1,8 @@
 <template>
   <user-group-membership-editor
-    v-model="localManagedUserProfile.groups"
+    v-model="localIAMUserProfile.groups"
     :editable-groups="editableGroups"
-    :airavata-internal-user-id="managedUserProfile.airavataInternalUserId"
+    :airavata-internal-user-id="iamUserProfile.airavataInternalUserId"
     @input="groupsUpdated"
   />
 </template>
@@ -13,8 +13,8 @@ import UserGroupMembershipEditor from "./UserGroupMembershipEditor";
 export default {
   name: "user-details-container",
   props: {
-    managedUserProfile: {
-      type: models.ManagedUserProfile,
+    iamUserProfile: {
+      type: models.IAMUserProfile,
       required: true
     },
     editableGroups: {
@@ -27,17 +27,17 @@ export default {
   },
   data() {
     return {
-      localManagedUserProfile: this.managedUserProfile.clone()
+      localIAMUserProfile: this.iamUserProfile.clone()
     };
   },
   watch: {
-    managedUserProfile(newValue) {
-      this.localManagedUserProfile = newValue.clone();
+    iamUserProfile(newValue) {
+      this.localIAMUserProfile = newValue.clone();
     }
   },
   methods: {
     groupsUpdated() {
-      this.$emit("groups-updated", this.localManagedUserProfile);
+      this.$emit("groups-updated", this.localIAMUserProfile);
     }
   }
 };
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 92e5c25..be47876 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
@@ -1,205 +1,38 @@
 <template>
   <div>
     <div class="row">
-      <div class="col">
+      <div class="col-auto mr-auto">
         <h1 class="h4 mb-4">Manage Users</h1>
       </div>
-    </div>
-    <div class="row">
-      <div class="col">
-        <div class="card">
-          <div class="card-body">
-            <b-input-group>
-              <b-form-input
-                v-model="search"
-                placeholder="Search by name, email or username"
-                @keydown.native.enter="searchUsers"
-              />
-              <b-input-group-append>
-                <b-button @click="resetSearch">Reset</b-button>
-                <b-button
-                  variant="primary"
-                  @click="searchUsers"
-                >Search</b-button>
-              </b-input-group-append>
-            </b-input-group>
-          </div>
-        </div>
-      </div>
-    </div>
-    <div class="row">
-      <div class="col">
-        <div class="card">
-          <div class="card-body">
-            <b-table
-              hover
-              :fields="fields"
-              :items="items"
-            >
-              <template
-                slot="creationTime"
-                slot-scope="data">
-                <human-date :date="data.value"/>
-              </template>
-              <template
-                slot="action"
-                slot-scope="data"
-              >
-                <b-button
-                  v-if="data.item.airavataUserProfileExists"
-                  @click="toggleDetails(data)"
-                >
-                  Edit Groups
-                </b-button>
-              </template>
-              <template
-                slot="row-details"
-                slot-scope="data"
-              >
-                <user-details-container
-                  :managed-user-profile="data.item"
-                  :editable-groups="editableGroups"
-                  @groups-updated="groupsUpdated"
-                />
-              </template>
-            </b-table>
-            <pager
-              v-bind:paginator="usersPaginator"
-              v-on:next="next"
-              v-on:previous="previous"
-            ></pager>
-          </div>
-        </div>
+      <div class="col-auto">
+        <b-dropdown :text="menuText">
+          <b-dropdown-item
+            :to="{ name: 'identity-service-users'}"
+            :exact="true"
+          >Identity Service</b-dropdown-item>
+          <b-dropdown-item
+            :to="{ name: 'unverified-email-users'}"
+            :exact="true"
+          >Unverified Emails</b-dropdown-item>
+        </b-dropdown>
       </div>
     </div>
+    <router-view></router-view>
   </div>
 </template>
 
 <script>
-import { services } from "django-airavata-api";
-import { components } from "django-airavata-common-ui";
-import UserDetailsContainer from "./UserDetailsContainer.vue";
-
 export default {
   name: "user-management-container",
-  data() {
-    return {
-      usersPaginator: null,
-      allGroups: null,
-      showingDetails: {},
-      search: null
-    };
-  },
-  components: {
-    pager: components.Pager,
-    'human-date': components.HumanDate,
-    UserDetailsContainer
-  },
-  created() {
-    services.ManagedUserProfileService.list({ limit: 10 }).then(
-      users => (this.usersPaginator = users)
-    );
-    services.GroupService.list({ limit: -1 }).then(
-      groups => (this.allGroups = groups)
-    );
-  },
   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: "Created",
-          key: "creationTime"
-        },
-        {
-          label: "Action",
-          key: "action"
-        }
-      ];
-    },
-    items() {
-      return this.usersPaginator
-        ? this.usersPaginator.results.map(u => {
-            const user = u.clone();
-            user._showDetails =
-              this.showingDetails[u.airavataInternalUserId] || false;
-            return user;
-          })
-        : [];
-    },
-    editableGroups() {
-      return this.allGroups
-        ? this.allGroups.filter(g => g.isAdmin || g.isOwner)
-        : [];
-    },
-    currentOffset() {
-      return this.usersPaginator ? this.usersPaginator.offset : 0;
-    }
-  },
-  methods: {
-    next() {
-      this.usersPaginator.next();
-    },
-    previous() {
-      this.usersPaginator.previous();
-    },
-    groupsUpdated(managedUserProfile) {
-      services.ManagedUserProfileService.update({
-        lookup: managedUserProfile.userId,
-        data: managedUserProfile
-      }).finally(() => {
-        this.reloadUserProfiles();
-      });
-    },
-    reloadUserProfiles() {
-      const params = {
-        limit: 10,
-        offset: this.currentOffset
-      };
-      if (this.search) {
-        params["search"] = this.search;
+    menuText() {
+      if (this.$route.name === "identity-service-users") {
+        return "Identity Service";
+      } else if (this.$route.name === "unverified-email-users") {
+        return "Unverified Emails";
+      } else {
+        return "Menu";
       }
-      services.ManagedUserProfileService.list(params).then(
-        users => (this.usersPaginator = users)
-      );
-    },
-    toggleDetails(row) {
-      row.toggleDetails();
-      this.showingDetails[row.item.airavataInternalUserId] = !this
-        .showingDetails[row.item.airavataInternalUserId];
-    },
-    searchUsers() {
-      // Reset paginator when starting a search
-      this.usersPaginator = null;
-      this.reloadUserProfiles();
-    },
-    resetSearch() {
-      this.usersPaginator = null;
-      this.search = null;
-      this.reloadUserProfiles();
     }
   }
 };
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 b4070d4..8cf1357 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
@@ -10,6 +10,8 @@ import ExperimentStatisticsContainer from "./components/statistics/ExperimentSta
 import CredentialStoreDashboard from "./components/dashboards/CredentialStoreDashboard";
 import GatewayResourceProfileEditorContainer from "./components/gatewayprofile/GatewayResourceProfileEditorContainer.vue";
 import GroupComputeResourcePreference from "./components/admin/group_resource_preferences/GroupComputeResourcePreference";
+import IdentityServiceUserManagementContainer from "./components/users/IdentityServiceUserManagementContainer.vue";
+import UnverifiedEmailUserManagementContainer from "./components/users/UnverifiedEmailUserManagementContainer.vue";
 import UserManagementContainer from "./components/users/UserManagementContainer.vue";
 import VueRouter from "vue-router";
 
@@ -122,7 +124,15 @@ const routes = [
   {
     path: "/users",
     component: UserManagementContainer,
-    name: "users"
+    name: "users",
+    children: [
+      { path: "", component: IdentityServiceUserManagementContainer, name: "identity-service-users" },
+      {
+        path: "unverified-email",
+        component: UnverifiedEmailUserManagementContainer,
+        name: "unverified-email-users"
+      }
+    ]
   },
   {
     path: "/experiment-statistics",
diff --git a/django_airavata/apps/api/serializers.py b/django_airavata/apps/api/serializers.py
index a33287d..94a9f2b 100644
--- a/django_airavata/apps/api/serializers.py
+++ b/django_airavata/apps/api/serializers.py
@@ -828,7 +828,7 @@ class WorkspacePreferencesSerializer(serializers.ModelSerializer):
         exclude = ('username',)
 
 
-class ManagedUserProfile(serializers.Serializer):
+class IAMUserProfile(serializers.Serializer):
     airavataInternalUserId = serializers.CharField()
     userId = serializers.CharField()
     gatewayId = serializers.CharField()
@@ -841,7 +841,7 @@ class ManagedUserProfile(serializers.Serializer):
     creationTime = UTCPosixTimestampDateTimeField()
     groups = GroupSerializer(many=True)
     url = FullyEncodedHyperlinkedIdentityField(
-        view_name='django_airavata_api:managed-user-profile-detail',
+        view_name='django_airavata_api:iam-user-profile-detail',
         lookup_field='userId',
         lookup_url_kwarg='user_id')
 
@@ -863,3 +863,18 @@ class ExperimentStatisticsSerializer(
     cancelledExperiments = ExperimentSummarySerializer(many=True)
     createdExperiments = ExperimentSummarySerializer(many=True)
     runningExperiments = ExperimentSummarySerializer(many=True)
+
+
+class UnverifiedEmailUserProfile(serializers.Serializer):
+    userId = serializers.CharField()
+    gatewayId = serializers.CharField()
+    email = serializers.CharField()
+    firstName = serializers.CharField()
+    lastName = serializers.CharField()
+    enabled = serializers.BooleanField()
+    emailVerified = serializers.BooleanField()
+    creationTime = UTCPosixTimestampDateTimeField()
+    url = FullyEncodedHyperlinkedIdentityField(
+        view_name='django_airavata_api:unverified-email-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 a43fecf..9edc7d9 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
@@ -21,6 +21,7 @@ import Group from "./models/Group";
 import GroupComputeResourcePreference from "./models/GroupComputeResourcePreference";
 import GroupPermission from "./models/GroupPermission";
 import GroupResourceProfile from "./models/GroupResourceProfile";
+import IAMUserProfile from "./models/IAMUserProfile";
 import InputDataObjectType from "./models/InputDataObjectType";
 import JobState from "./models/JobState";
 import ManagedUserProfile from "./models/ManagedUserProfile";
@@ -77,6 +78,7 @@ const models = {
   GroupComputeResourcePreference,
   GroupPermission,
   GroupResourceProfile,
+  IAMUserProfile,
   InputDataObjectType,
   JobState,
   ManagedUserProfile,
@@ -113,7 +115,7 @@ const services = {
   GroupResourceProfileService: ServiceFactory.service("GroupResourceProfiles"),
   GroupService: ServiceFactory.service("Groups"),
   LocaJobSubmissionService,
-  ManagedUserProfileService: ServiceFactory.service("ManagedUserProfiles"),
+  IAMUserProfileService: ServiceFactory.service("IAMUserProfiles"),
   ParserService: ServiceFactory.service("Parsers"),
   ProjectService: ServiceFactory.service("Projects"),
   SCPDataMovementService,
@@ -124,6 +126,9 @@ const services = {
   StorageResourceService: ServiceFactory.service("StorageResources"),
   UnicoreDataMovementService,
   UnicoreJobSubmissionService,
+  UnverifiedEmailUserProfileService: ServiceFactory.service(
+    "UnverifiedEmailUsers"
+  ),
   UserProfileService,
   UserStoragePathService: ServiceFactory.service("UserStoragePaths"),
   WorkspacePreferencesService: ServiceFactory.service("WorkspacePreferences")
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/IAMUserProfile.js
similarity index 87%
copy from django_airavata/apps/api/static/django_airavata_api/js/models/ManagedUserProfile.js
copy to django_airavata/apps/api/static/django_airavata_api/js/models/IAMUserProfile.js
index 3c87783..2b2e6f5 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/models/ManagedUserProfile.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/models/IAMUserProfile.js
@@ -23,7 +23,7 @@ const FIELDS = [
   }
 ];
 
-export default class ManagedUserProfile extends BaseModel {
+export default class IAMUserProfile extends BaseModel {
   constructor(data = {}) {
     super(FIELDS, data);
   }
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/UnverifiedEmailUserProfile.js
similarity index 53%
rename from django_airavata/apps/api/static/django_airavata_api/js/models/ManagedUserProfile.js
rename to django_airavata/apps/api/static/django_airavata_api/js/models/UnverifiedEmailUserProfile.js
index 3c87783..1b1b59b 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/models/ManagedUserProfile.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/models/UnverifiedEmailUserProfile.js
@@ -1,9 +1,6 @@
 import BaseModel from "./BaseModel";
-import Group from "./Group";
 
 const FIELDS = [
-  "userModelVersion",
-  "airavataInternalUserId",
   "userId",
   "gatewayId",
   "email",
@@ -11,19 +8,13 @@ const FIELDS = [
   "lastName",
   "enabled",
   "emailVerified",
-  "airavataUserProfileExists",
   {
     name: "creationTime",
     type: 'date',
   },
-  {
-    name: "groups",
-    type: Group,
-    list: true
-  }
 ];
 
-export default class ManagedUserProfile extends BaseModel {
+export default class UnverifiedEmailUserProfile 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 ef94c66..ed76e06 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
@@ -13,12 +13,13 @@ 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 IAMUserProfile from "./models/IAMUserProfile";
 import Parser from "./models/Parser";
 import Project from "./models/Project";
 import SharedEntity from "./models/SharedEntity";
 import StoragePreference from "./models/StoragePreference";
 import StorageResourceDescription from "./models/StorageResourceDescription";
+import UnverifiedEmailUserProfile from "./models/UnverifiedEmailUserProfile";
 import UserProfile from "./models/UserProfile";
 import UserStoragePath from "./models/UserStoragePath";
 import WorkspacePreferences from "./models/WorkspacePreferences";
@@ -239,12 +240,12 @@ export default {
     queryParams: ["limit", "offset"],
     modelClass: Group
   },
-  ManagedUserProfiles: {
-    url: "/api/managed-user-profiles",
+  IAMUserProfiles: {
+    url: "/api/iam-user-profiles",
     viewSet: true,
     pagination: true,
     queryParams: ["limit", "offset", "search"],
-    modelClass: ManagedUserProfile
+    modelClass: IAMUserProfile
   },
   Parsers: {
     url: "/api/parsers",
@@ -297,6 +298,13 @@ export default {
     },
     modelClass: StorageResourceDescription
   },
+  UnverifiedEmailUsers: {
+    url: "/api/unverified-email-users",
+    viewSet: true,
+    pagination: true,
+    queryParams: ["limit", "offset"],
+    modelClass: UnverifiedEmailUserProfile
+  },
   UserProfiles: {
     url: "/api/user-profiles",
     viewSet: ["list"],
diff --git a/django_airavata/apps/api/urls.py b/django_airavata/apps/api/urls.py
index 267da92..18d66ea 100644
--- a/django_airavata/apps/api/urls.py
+++ b/django_airavata/apps/api/urls.py
@@ -41,8 +41,10 @@ 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')
+router.register(r'iam-user-profiles', views.IAMUserViewSet,
+                base_name='iam-user-profile')
+router.register(r'unverified-email-users', views.UnverifiedEmailUserViewSet,
+                base_name='unverified-email-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 e13bdd6..135f112 100644
--- a/django_airavata/apps/api/views.py
+++ b/django_airavata/apps/api/views.py
@@ -40,6 +40,7 @@ from django_airavata.apps.api.view_utils import (
     GenericAPIBackedViewSet
 )
 from django_airavata.apps.auth import iam_admin_client
+from django_airavata.apps.auth.models import EmailVerification
 
 from . import (
     data_products_helper,
@@ -1384,12 +1385,12 @@ class WorkspacePreferencesView(APIView):
         return Response(serializer.data)
 
 
-class ManagedUserViewSet(mixins.CreateModelMixin,
-                         mixins.RetrieveModelMixin,
-                         mixins.UpdateModelMixin,
-                         mixins.ListModelMixin,
-                         GenericAPIBackedViewSet):
-    serializer_class = serializers.ManagedUserProfile
+class IAMUserViewSet(mixins.CreateModelMixin,
+                     mixins.RetrieveModelMixin,
+                     mixins.UpdateModelMixin,
+                     mixins.ListModelMixin,
+                     GenericAPIBackedViewSet):
+    serializer_class = serializers.IAMUserProfile
     pagination_class = APIResultPagination
     lookup_field = 'user_id'
 
@@ -1398,11 +1399,11 @@ class ManagedUserViewSet(mixins.CreateModelMixin,
 
         convert_user_profile = self._convert_user_profile
 
-        class ManagedUsersResultIterator(APIResultIterator):
+        class IAMUsersResultIterator(APIResultIterator):
             def get_results(self, limit=-1, offset=0):
                 return map(convert_user_profile,
                            iam_admin_client.get_users(offset, limit, search))
-        return ManagedUsersResultIterator()
+        return IAMUsersResultIterator()
 
     def get_instance(self, lookup_value):
         return self._convert_user_profile(
@@ -1435,9 +1436,9 @@ class ManagedUserViewSet(mixins.CreateModelMixin,
             '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,
+            'enabled': user_profile.State == Status.ACTIVE,
+            'emailVerified': (user_profile.State == Status.CONFIRMED or
+                              user_profile.State == Status.ACTIVE),
             'airavataUserProfileExists': airavata_user_profile_exists,
             'creationTime': user_profile.creationTime,
             'groups': groups
@@ -1469,3 +1470,60 @@ class ExperimentStatisticsView(APIView):
         serializer = self.serializer_class(
             statistics, context={'request': request})
         return Response(serializer.data)
+
+
+class UnverifiedEmailUserViewSet(mixins.ListModelMixin,
+                                 mixins.RetrieveModelMixin,
+                                 GenericAPIBackedViewSet):
+    serializer_class = serializers.UnverifiedEmailUserProfile
+    pagination_class = APIResultPagination
+    lookup_field = 'user_id'
+
+    def get_list(self):
+        get_users = self._get_unverified_email_user_profiles
+
+        class UnverifiedEmailUsersResultIterator(APIResultIterator):
+            def get_results(self, limit=-1, offset=0):
+                return get_users(limit, offset)
+        return UnverifiedEmailUsersResultIterator()
+
+    def get_instance(self, lookup_value):
+        users = self._get_unverified_email_user_profiles(
+            limit=1, username=lookup_value)
+        if len(users) == 0:
+            raise Http404("No unverified email record found for user {}"
+                          .format(lookup_value))
+        else:
+            return users[0]
+
+    def _get_unverified_email_user_profiles(
+            self, limit=-1, offset=0, username=None):
+        unverified_emails = EmailVerification.objects.filter(
+            verified=False).order_by('username').values('username').distinct()
+        if username is not None:
+            unverified_emails = unverified_emails.filter(username=username)
+        if limit > 0:
+            unverified_emails = unverified_emails[offset:offset+limit]
+        results = []
+        for unverified_email in unverified_emails:
+            username = unverified_email['username']
+            user_profile = iam_admin_client.get_user(username)
+            if (user_profile.State == Status.CONFIRMED or
+                    user_profile.State == Status.ACTIVE):
+                # TODO: test this
+                EmailVerification.objects.filter(
+                    username=username).update(
+                    verified=True)
+                continue
+            results.append({
+                'userId': user_profile.userId,
+                'gatewayId': user_profile.gatewayId,
+                'email': user_profile.emails[0],
+                'firstName': user_profile.firstName,
+                'lastName': user_profile.lastName,
+                'enabled': user_profile.State == Status.ACTIVE,
+                'emailVerified': (user_profile.State == Status.CONFIRMED or
+                                  user_profile.State == Status.ACTIVE),
+                'creationTime': user_profile.creationTime,
+            })
+        return results