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/06/25 18:26:06 UTC
[airavata-django-portal] 01/03: AIRAVATA-3468 Check if profile is
complete and redirect to profile editor if not
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 96bc6bf3bf27ce096d8dc384a65e68c66eca9ac0
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Thu Jun 24 11:49:28 2021 -0400
AIRAVATA-3468 Check if profile is complete and redirect to profile editor if not
---
django_airavata/apps/auth/backends.py | 1 +
django_airavata/apps/auth/middleware.py | 26 ++++++++++++--
django_airavata/apps/auth/models.py | 40 +++++++++++++++++++---
django_airavata/apps/auth/signals.py | 17 +++++----
.../js/components/UserProfileEditor.vue | 18 ++++++++--
django_airavata/apps/auth/urls.py | 2 +-
django_airavata/settings.py | 1 +
7 files changed, 88 insertions(+), 17 deletions(-)
diff --git a/django_airavata/apps/auth/backends.py b/django_airavata/apps/auth/backends.py
index 349d4d2..24f3143 100644
--- a/django_airavata/apps/auth/backends.py
+++ b/django_airavata/apps/auth/backends.py
@@ -220,6 +220,7 @@ class KeycloakBackend(object):
# Update User model fields
user = user_profile.user
+ user.username = username
user.email = email
user.first_name = first_name
user.last_name = last_name
diff --git a/django_airavata/apps/auth/middleware.py b/django_airavata/apps/auth/middleware.py
index eb8a722..7921405 100644
--- a/django_airavata/apps/auth/middleware.py
+++ b/django_airavata/apps/auth/middleware.py
@@ -4,8 +4,10 @@ import logging
from django.conf import settings
from django.contrib.auth import logout
+from django.shortcuts import redirect
from . import utils
+from django.urls import reverse
log = logging.getLogger(__name__)
@@ -33,7 +35,10 @@ def gateway_groups_middleware(get_response):
"""Add 'is_gateway_admin' and 'is_read_only_gateway_admin' to request."""
def middleware(request):
- if not request.user.is_authenticated or not request.authz_token:
+ request.is_gateway_admin = False
+ request.is_read_only_gateway_admin = False
+
+ if not request.user.is_authenticated or not request.authz_token or not request.user.user_profile.is_complete:
return get_response(request)
try:
@@ -66,8 +71,23 @@ def gateway_groups_middleware(get_response):
except Exception as e:
log.warning("Failed to set is_gateway_admin, "
"is_read_only_gateway_admin for user", exc_info=e)
- request.is_gateway_admin = False
- request.is_read_only_gateway_admin = False
return get_response(request)
return middleware
+
+
+def user_profile_completeness_check(get_response):
+ """Check if user profile is complete and if not, redirect to user profile editor."""
+ def middleware(request):
+
+ if not request.user.is_authenticated:
+ return get_response(request)
+
+ if (not request.user.user_profile.is_complete and
+ request.path != reverse('django_airavata_auth:user_profile') and
+ request.path != reverse('django_airavata_auth:logout') and
+ request.META['HTTP_ACCEPT'] != 'application/json'):
+ return redirect('django_airavata_auth:user_profile')
+ else:
+ return get_response(request)
+ return middleware
diff --git a/django_airavata/apps/auth/models.py b/django_airavata/apps/auth/models.py
index c8c0da3..86ab451 100644
--- a/django_airavata/apps/auth/models.py
+++ b/django_airavata/apps/auth/models.py
@@ -3,6 +3,9 @@ import uuid
from django.conf import settings
from django.db import models
+from . import forms
+from django.core.exceptions import ValidationError
+
VERIFY_EMAIL_TEMPLATE = 1
NEW_USER_EMAIL_TEMPLATE = 2
PASSWORD_RESET_EMAIL_TEMPLATE = 3
@@ -58,11 +61,38 @@ class UserProfile(models.Model):
@property
def is_complete(self):
- # TODO: implement this to check if there are any missing fields on the
- # User model (email, first_name, last_name) or if the username was is
- # invalid (for example if defaulted to the IdP's 'sub' claim) or if
- # there are any extra profile fields that are not valid
- return False
+ return (self.is_username_valid and
+ self.is_first_name_valid and
+ self.is_last_name_valid and
+ self.is_email_valid)
+
+ @property
+ def is_username_valid(self):
+ # use forms.USERNAME_VALIDATOR with an exception when the username is
+ # equal to the email
+ try:
+ forms.USERNAME_VALIDATOR(self.user.username)
+ validates = True
+ except ValidationError:
+ validates = False
+ return (validates or (self.is_email_valid and self.user.email == self.user.username))
+
+ @property
+ def is_first_name_valid(self):
+ return self.is_non_empty(self.user.first_name)
+
+ @property
+ def is_last_name_valid(self):
+ return self.is_non_empty(self.user.last_name)
+
+ @property
+ def is_email_valid(self):
+ # Only checking for non-empty only; assumption is that email is verified
+ # before it is set or updated
+ return self.is_non_empty(self.user.email)
+
+ def is_non_empty(self, value: str):
+ return value is not None and value.strip() != ""
class UserInfo(models.Model):
diff --git a/django_airavata/apps/auth/signals.py b/django_airavata/apps/auth/signals.py
index dfc76cd..7cf1652 100644
--- a/django_airavata/apps/auth/signals.py
+++ b/django_airavata/apps/auth/signals.py
@@ -43,11 +43,16 @@ def initialize_user_profile(sender, request, user, **kwargs):
if not user_profile_client_pool.doesUserExist(authz_token,
user.username,
settings.GATEWAY_ID):
- user_profile_client_pool.initializeUserProfile(authz_token)
- log.info("initialized user profile for {}".format(user.username))
- # Since user profile created, inform admins of new user
- utils.send_new_user_email(
- request, user.username, user.email, user.first_name, user.last_name)
- log.info("sent new user email for user {}".format(user.username))
+ if user.user_profile.is_complete:
+ user_profile_client_pool.initializeUserProfile(authz_token)
+ log.info("initialized user profile for {}".format(user.username))
+ # Since user profile created, inform admins of new user
+ utils.send_new_user_email(
+ request, user.username, user.email, user.first_name, user.last_name)
+ log.info("sent new user email for user {}".format(user.username))
+ else:
+ log.info(f"user profile not complete for {user.username}, "
+ "skipping initializing Airavata user profile")
+
else:
log.warning(f"Logged in user {user.username} has no access token")
diff --git a/django_airavata/apps/auth/static/django_airavata_auth/js/components/UserProfileEditor.vue b/django_airavata/apps/auth/static/django_airavata_auth/js/components/UserProfileEditor.vue
index 506114c..b6b31fe 100644
--- a/django_airavata/apps/auth/static/django_airavata_auth/js/components/UserProfileEditor.vue
+++ b/django_airavata/apps/auth/static/django_airavata_auth/js/components/UserProfileEditor.vue
@@ -1,7 +1,15 @@
<template>
<b-card>
<b-form-group label="Username">
- <b-form-input disabled :value="user.username" />
+ <b-form-input
+ v-model="$v.user.username.$model"
+ @keydown.native.enter="save"
+ :state="validateState($v.user.username)"
+ />
+ <b-form-invalid-feedback v-if="!$v.user.username.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>
<b-form-group label="First Name">
<b-form-input
@@ -46,7 +54,7 @@
import { models } from "django-airavata-api";
import { errors } from "django-airavata-common-ui";
import { validationMixin } from "vuelidate";
-import { email, required } from "vuelidate/lib/validators";
+import { email, helpers, or, required, sameAs } from "vuelidate/lib/validators";
export default {
name: "user-profile-editor",
@@ -63,8 +71,14 @@ export default {
};
},
validations() {
+ const usernameRegex = helpers.regex("username", /^[a-z0-9_-]+$/);
+ const emailOrMatchesRegex = or(usernameRegex, sameAs('email'));
return {
user: {
+ username: {
+ required,
+ emailOrMatchesRegex,
+ },
first_name: {
required,
},
diff --git a/django_airavata/apps/auth/urls.py b/django_airavata/apps/auth/urls.py
index 887ed74..b9a4d3b 100644
--- a/django_airavata/apps/auth/urls.py
+++ b/django_airavata/apps/auth/urls.py
@@ -5,7 +5,7 @@ from rest_framework import routers
from . import views
router = routers.DefaultRouter()
-router.register(r'users', views.UserViewSet, base_name='user')
+router.register(r'users', views.UserViewSet, basename='user')
app_name = 'django_airavata_auth'
urlpatterns = [
url(r'^', include(router.urls)),
diff --git a/django_airavata/settings.py b/django_airavata/settings.py
index 3ab8b1e..d2ce38f 100644
--- a/django_airavata/settings.py
+++ b/django_airavata/settings.py
@@ -130,6 +130,7 @@ MIDDLEWARE = [
# Wagtail related middleware
'wagtail.core.middleware.SiteMiddleware',
'wagtail.contrib.redirects.middleware.RedirectMiddleware',
+ 'django_airavata.apps.auth.middleware.user_profile_completeness_check',
]
ROOT_URLCONF = 'django_airavata.urls'