You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by sm...@apache.org on 2020/04/14 18:07:10 UTC

[airavata-custos-portal] 10/14: Added emails for forgot password and reset password

This is an automated email from the ASF dual-hosted git repository.

smarru pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/airavata-custos-portal.git

commit 415ecde41b586351fa9d0cfdd07cc87ca96424b3
Author: Shivam Rastogi <sh...@yahoo.com>
AuthorDate: Tue Mar 31 15:31:39 2020 -0400

    Added emails for forgot password and reset password
---
 custos_portal/custos_portal/apps/auth/backends.py  |   1 -
 .../auth/migrations/0003_password_reset_request.py |  64 ++++
 .../auth/migrations/0004_auto_20200331_1859.py     |  18 ++
 custos_portal/custos_portal/apps/auth/urls.py      |  11 +-
 custos_portal/custos_portal/apps/auth/utils.py     |  29 ++
 custos_portal/custos_portal/apps/auth/views.py     | 357 +++++++++++++++------
 custos_portal/custos_portal/context_processors.py  |   6 -
 custos_portal/custos_portal/settings.py            |   2 +-
 custos_portal/custos_portal/settings_local.py      |   4 +-
 .../templates/custos_portal/home.html              |   5 +-
 .../custos_portal_auth/forgot_password.html        |   2 +-
 .../login_username_password.html                   |   2 +-
 .../partials/username_password_login_form.html     |   2 +-
 .../custos_portal_auth/reset_password.html         |   2 +-
 .../templates/custos_portal_auth/verify_email.html |   2 +-
 15 files changed, 393 insertions(+), 114 deletions(-)

diff --git a/custos_portal/custos_portal/apps/auth/backends.py b/custos_portal/custos_portal/apps/auth/backends.py
index 2db7af3..33b4c4b 100644
--- a/custos_portal/custos_portal/apps/auth/backends.py
+++ b/custos_portal/custos_portal/apps/auth/backends.py
@@ -21,7 +21,6 @@ class CustosAuthBackend(ModelBackend):
     @sensitive_variables('password')
     def authenticate(self, request=None, username=None, password=None, refresh_token=None):
         try:
-            userinfo = None
             if username and password:
                 token = self._get_token_and_userinfo_password_flow(username, password)
                 request.session["ACCESS_TOKEN"] = token
diff --git a/custos_portal/custos_portal/apps/auth/migrations/0003_password_reset_request.py b/custos_portal/custos_portal/apps/auth/migrations/0003_password_reset_request.py
new file mode 100644
index 0000000..b1f0242
--- /dev/null
+++ b/custos_portal/custos_portal/apps/auth/migrations/0003_password_reset_request.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.20 on 2019-05-07 15:49
+from __future__ import unicode_literals
+
+import uuid
+
+from django.db import migrations, models
+
+from custos_portal.apps.auth.models import PASSWORD_RESET_EMAIL_TEMPLATE
+
+
+def default_templates(apps, schema_editor):
+
+    EmailTemplate = apps.get_model("custos_portal_auth", "EmailTemplate")
+    verify_email_template = EmailTemplate(
+        template_type=PASSWORD_RESET_EMAIL_TEMPLATE,
+        subject="{{first_name}} {{last_name}} ({{username}}), "
+                "Reset your password in {{portal_title}}",
+        body="""
+        <p>
+        Dear {{first_name}} {{last_name}},
+        </p>
+
+        <p>
+        Please click the link below to reset your password. This link is
+        valid for 24 hours.
+        </p>
+
+        <p><a href="{{url}}">{{url}}</a></p>
+
+        <p>If you didn't request to reset your password, just ignore this message.</p>
+        """.strip())
+    verify_email_template.save()
+
+
+def delete_default_templates(apps, schema_editor):
+    EmailTemplate = apps.get_model("custos_portal_auth", "EmailTemplate")
+    EmailTemplate.objects.filter(
+        template_type=PASSWORD_RESET_EMAIL_TEMPLATE).delete()
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('custos_portal_auth', '0002_default_email_template'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='PasswordResetRequest',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('username', models.CharField(max_length=64)),
+                ('reset_code', models.CharField(default=uuid.uuid4, max_length=36, unique=True)),
+                ('created_date', models.DateTimeField(auto_now_add=True)),
+            ],
+        ),
+        migrations.AlterField(
+            model_name='emailtemplate',
+            name='template_type',
+            field=models.IntegerField(choices=[(1, 'Verify Email Template'), (2, 'New User Email Template'), (3, 'Password Reset Email Template')], primary_key=True, serialize=False),
+        ),
+        migrations.RunPython(default_templates, reverse_code=delete_default_templates)
+    ]
diff --git a/custos_portal/custos_portal/apps/auth/migrations/0004_auto_20200331_1859.py b/custos_portal/custos_portal/apps/auth/migrations/0004_auto_20200331_1859.py
new file mode 100644
index 0000000..2bf309b
--- /dev/null
+++ b/custos_portal/custos_portal/apps/auth/migrations/0004_auto_20200331_1859.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.0.4 on 2020-03-31 18:59
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('custos_portal_auth', '0003_password_reset_request'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='emailtemplate',
+            name='template_type',
+            field=models.IntegerField(choices=[(1, 'Verify Email Template'), (2, 'New User Email Template'), (3, 'Password Reset Email Template'), (4, 'User Added to Group Template')], primary_key=True, serialize=False),
+        ),
+    ]
diff --git a/custos_portal/custos_portal/apps/auth/urls.py b/custos_portal/custos_portal/apps/auth/urls.py
index 623799e..47c1d12 100644
--- a/custos_portal/custos_portal/apps/auth/urls.py
+++ b/custos_portal/custos_portal/apps/auth/urls.py
@@ -6,15 +6,16 @@ from . import views
 app_name = 'custos_portal_auth'
 urlpatterns = [
     path('login/', views.start_login, name='login'),
+    url(r'^login-password$', views.start_username_password_login, name='login_with_password'),
     url(r'^redirect_login/(\w+)/$', views.redirect_login, name='redirect_login'),
     url(r'^create-account$', views.create_account, name='create_account'),
     url(r'^redirect_login/(\w+)/$', views.redirect_login, name='redirect_login'),
     url(r'^callback/$', views.callback, name='callback'),
-    url(r'^callback-error/(?P<idp_alias>\w+)/$', views.callback_error,
-        name='callback-error'),
+    url(r'^callback-error/(?P<idp_alias>\w+)/$', views.callback_error, name='callback-error'),
     url(r'handle-login', views.handle_login, name="handle_login"),
     url(r'^logout$', views.start_logout, name='logout'),
-    url(r'^verify-email/(?P<code>[\w-]+)/$', views.verify_email,
-        name="verify_email"),
-
+    url(r'^verify-email/(?P<code>[\w-]+)/$', views.verify_email, name="verify_email"),
+    url(r'^resend-email-link/', views.resend_email_link, name="resend_email_link"),
+    url(r'^forgot-password/$', views.forgot_password, name="forgot_password"),
+    url(r'^reset-password/(?P<code>[\w-]+)/$', views.reset_password, name="reset_password"),
 ]
diff --git a/custos_portal/custos_portal/apps/auth/utils.py b/custos_portal/custos_portal/apps/auth/utils.py
index a1f5598..df68deb 100644
--- a/custos_portal/custos_portal/apps/auth/utils.py
+++ b/custos_portal/custos_portal/apps/auth/utils.py
@@ -1,5 +1,6 @@
 from django.conf import settings
 from django.core.mail import EmailMessage
+from django.http.request import split_domain_port
 
 from . import models
 from django.template import Context, Template
@@ -23,3 +24,31 @@ def send_email_to_user(template_id, context):
     )
     msg.content_subtype = 'html'
     msg.send()
+
+
+def send_new_user_email(request, username, email, first_name, last_name):
+    """Send new user email notification to portal admins."""
+    new_user_email_template = models.EmailTemplate.objects.get(
+        pk=models.NEW_USER_EMAIL_TEMPLATE)
+    domain, port = split_domain_port(request.get_host())
+    context = Context({
+        "username": username,
+        "email": email,
+        "first_name": first_name,
+        "last_name": last_name,
+        "portal_title": settings.PORTAL_TITLE,
+        "gateway_id": settings.GATEWAY_ID,
+        "http_host": domain,
+    })
+    subject = Template(new_user_email_template.subject).render(context)
+    body = Template(new_user_email_template.body).render(context)
+    msg = EmailMessage(subject=subject,
+                       body=body,
+                       from_email="{} <{}>".format(
+                           settings.PORTAL_TITLE,
+                           settings.SERVER_EMAIL),
+                       to=[a[1] for a in getattr(settings,
+                                                 'PORTAL_ADMINS',
+                                                 settings.ADMINS)])
+    msg.content_subtype = 'html'
+    msg.send()
diff --git a/custos_portal/custos_portal/apps/auth/views.py b/custos_portal/custos_portal/apps/auth/views.py
index c806245..ddf0f55 100644
--- a/custos_portal/custos_portal/apps/auth/views.py
+++ b/custos_portal/custos_portal/apps/auth/views.py
@@ -1,18 +1,22 @@
 import logging
 import json
+from time import timezone
 from urllib.parse import quote
+from datetime import datetime, timedelta, timezone
+
 
-from clients.identity_management_client import IdentityManagementClient
-from clients.user_management_client import UserManagementClient
 from django.conf import settings
 from django.contrib import messages
 from django.contrib.auth import authenticate, login, logout
-from django.core.exceptions import ValidationError
+from django.core.exceptions import ValidationError, ObjectDoesNotExist
 from django.forms import formset_factory, models
+from django.http import HttpResponseBadRequest
 from django.shortcuts import render, redirect, resolve_url
 from django.template import Context
 from django.urls import reverse
 from django.utils.http import urlencode
+from django.views.decorators.debug import sensitive_variables
+from google.protobuf.json_format import MessageToDict
 from requests_oauthlib import OAuth2Session
 
 from . import utils
@@ -24,48 +28,6 @@ from ... import user_management_client
 logger = logging.getLogger(__name__)
 
 
-def start_login(request):
-    return render(request, 'custos_portal_auth/login.html', {
-        'next': request.GET.get('next', None),
-        'options': settings.AUTHENTICATION_OPTIONS,
-    })
-
-
-def redirect_login(request, idp_alias):
-    _validate_idp_alias(idp_alias)
-
-    client_id = settings.KEYCLOAK_CLIENT_ID
-
-    auth_base_url = identity_management_client.get_oidc_configuration(settings.CUSTOS_TOKEN, client_id)
-    # auth_base_url = json.loads(auth_base_url.)
-    # print(auth_base_url)
-    base_authorize_url = settings.KEYCLOAK_AUTHORIZE_URL
-
-    redirect_uri = request.build_absolute_uri(
-        reverse('custos_portal_auth:callback'))
-
-    oauth2_session = OAuth2Session(
-        client_id, scope='openid', redirect_uri=redirect_uri)
-    authorization_url, state = oauth2_session.authorization_url(
-        base_authorize_url)
-    authorization_url += '&kc_idp_hint=' + quote("oidc")
-
-    # Store state in session for later validation (see backends.py)
-    request.session['OAUTH2_STATE'] = state
-    request.session['OAUTH2_REDIRECT_URI'] = redirect_uri
-
-    logger.debug('Redirect URI: {}'.format(redirect_uri))
-    logger.debug('Authorization URL for OpenID: {}'.format(authorization_url))
-    return redirect(authorization_url)
-
-
-def _validate_idp_alias(idp_alias):
-    external_auth_options = settings.AUTHENTICATION_OPTIONS['external']
-    valid_idp_aliases = [ext['idp_alias'] for ext in external_auth_options]
-    if idp_alias not in valid_idp_aliases:
-        raise Exception("idp_alias is not valid: {}".format(idp_alias))
-
-
 def callback(request):
     try:
         user = authenticate(request=request)
@@ -96,6 +58,110 @@ def callback_error(request, idp_alias):
     })
 
 
+def create_account(request):
+    if request.method == 'POST':
+        form = forms.CreateAccountForm(request.POST)
+        if form.is_valid():
+            try:
+                username = form.cleaned_data['username']
+                email = form.cleaned_data['email']
+                first_name = form.cleaned_data['first_name']
+                last_name = form.cleaned_data['last_name']
+                password = form.cleaned_data['password']
+                is_temp_password = False
+                result = user_management_client.register_user(settings.CUSTOS_TOKEN, username, first_name, last_name,
+                                                              password, email, is_temp_password)
+                if result.is_registered:
+                    logger.debug("User account successfully created for : {}".format(username))
+                    _create_and_send_email_verification_link(request, username, email, first_name, last_name,
+                                                             settings.LOGIN_URL)
+                    messages.success(
+                        request,
+                        "Account request processed successfully. Before you "
+                        "can login you need to confirm your email address. "
+                        "We've sent you an email with a link that you should "
+                        "click on to complete the account creation process.")
+                else:
+                    form.add_error(None, ValidationError("Failed to register the user with IAM service"))
+            except TypeError as e:
+                logger.exception(
+                    "Failed to create account for user", exc_info=e)
+                form.add_error(None, ValidationError(e))
+            return render(request, 'custos_portal_auth/create_account.html', {
+                'options': settings.AUTHENTICATION_OPTIONS,
+                'form': form
+            })
+    else:
+        form = forms.CreateAccountForm()
+    return render(request, 'custos_portal_auth/create_account.html', {
+        'options': settings.AUTHENTICATION_OPTIONS,
+        'form': form
+    })
+
+
+def forgot_password(request):
+    if request.method == 'POST':
+        form = forms.ForgotPasswordForm(request.POST)
+        if form.is_valid():
+            try:
+                username = form.cleaned_data['username']
+                if not user_management_client.is_username_available(settings.CUSTOS_TOKEN, username).is_exist:
+                    if not user_management_client.is_user_enabled(settings.CUSTOS_TOKEN, username).is_exist:
+                        messages.error(
+                            request,
+                            "Please finish creating your account before "
+                            "resetting your password. Provide your username "
+                            "below and we will send you another email "
+                            "verification link.")
+                        return redirect(
+                            reverse('custos_portal_auth:resend_email_link'))
+                    _create_and_send_password_reset_request_link(request, username)
+                # Always display this message even if you doesn't exist. Don't
+                # reveal whether a user with that username exists.
+                messages.success(
+                    request,
+                    "Reset password request processed successfully. We've "
+                    "sent an email with a password reset link to the email "
+                    "address associated with the username you provided. You "
+                    "can use that link within the next 24 hours to set a new "
+                    "password.")
+                return redirect(
+                    reverse('custos_portal_auth:forgot_password'))
+            except Exception as e:
+                logger.exception(
+                    "Failed to generate password reset request for user",
+                    exc_info=e)
+                form.add_error(None, ValidationError(str(e)))
+    else:
+        form = forms.ForgotPasswordForm()
+    return render(request, 'custos_portal_auth/forgot_password.html', {
+        'form': form
+    })
+
+
+def _create_and_send_password_reset_request_link(request, username):
+    password_reset_request = models.PasswordResetRequest(username=username)
+    password_reset_request.save()
+
+    verification_uri = request.build_absolute_uri(
+        reverse(
+            'custos_portal_auth:reset_password', kwargs={
+                'code': password_reset_request.reset_code}))
+    logger.debug(
+        "password reset verification_uri={}".format(verification_uri))
+
+    user = user_management_client.get_user(settings.CUSTOS_TOKEN, username)
+    context = Context({
+        "username": username,
+        "email": user.email,
+        "first_name": user.first_name,
+        "last_name": user.last_name,
+        "portal_title": settings.PORTAL_TITLE,
+        "url": verification_uri,
+    })
+    utils.send_email_to_user(models.PASSWORD_RESET_EMAIL_TEMPLATE, context)
+
+
 def handle_login(request):
     username = request.POST['username']
     password = request.POST['password']
@@ -122,61 +188,153 @@ def handle_login(request):
     })
 
 
-def _handle_login_redirect(request):
-    if request.is_gateway_admin:
-        return redirect(reverse('custos_portal_admin:list_requests'))
-    else:
-        return redirect(reverse('custos_portal_workspace:list_requests'))
+def redirect_login(request, idp_alias):
+    _validate_idp_alias(idp_alias)
 
+    client_id = settings.KEYCLOAK_CLIENT_ID
 
-def start_logout(request):
-    logout(request)
-    redirect_url = request.build_absolute_uri(resolve_url(settings.LOGOUT_REDIRECT_URL))
-    return redirect(settings.KEYCLOAK_LOGOUT_URL + "?redirect_uri=" + quote(redirect_url))
+    auth_base_url = identity_management_client.get_oidc_configuration(settings.CUSTOS_TOKEN, client_id)
+    # auth_base_url = json.loads(auth_base_url.)
+    # print(auth_base_url)
+    base_authorize_url = settings.KEYCLOAK_AUTHORIZE_URL
 
+    redirect_uri = request.build_absolute_uri(
+        reverse('custos_portal_auth:callback'))
 
-def create_account(request):
+    oauth2_session = OAuth2Session(
+        client_id, scope='openid', redirect_uri=redirect_uri)
+    authorization_url, state = oauth2_session.authorization_url(
+        base_authorize_url)
+    authorization_url += '&kc_idp_hint=' + quote("oidc")
+
+    # Store state in session for later validation (see backends.py)
+    request.session['OAUTH2_STATE'] = state
+    request.session['OAUTH2_REDIRECT_URI'] = redirect_uri
+
+    logger.debug('Redirect URI: {}'.format(redirect_uri))
+    logger.debug('Authorization URL for OpenID: {}'.format(authorization_url))
+    return redirect(authorization_url)
+
+
+def resend_email_link(request):
     if request.method == 'POST':
-        form = forms.CreateAccountForm(request.POST)
+        form = forms.ResendEmailVerificationLinkForm(request.POST)
         if form.is_valid():
             try:
                 username = form.cleaned_data['username']
-                email = form.cleaned_data['email']
-                first_name = form.cleaned_data['first_name']
-                last_name = form.cleaned_data['last_name']
-                password = form.cleaned_data['password']
-                is_temp_password = True
-                result = user_management_client.register_user(settings.CUSTOS_TOKEN, username, first_name, last_name,
-                                                              password, email, is_temp_password)
-                if result.is_registered:
-                    logger.debug("User account successfully created for : {}".format(username))
-                    _create_and_send_email_verification_link(request, username, email, first_name, last_name, next)
+                if not user_management_client.is_username_available(settings.CUSTOS_TOKEN, username).is_exist:
+                    user_profile = user_management_client.get_user(settings.CUSTOS_TOKEN, username)
+                    print(user_profile)
+                    _create_and_send_email_verification_link(
+                        request,
+                        username,
+                        user_profile.email,
+                        user_profile.first_name,
+                        user_profile.last_name,
+                        settings.LOGIN_URL)
                     messages.success(
                         request,
-                        "Account request processed successfully. Before you "
-                        "can login you need to confirm your email address. "
-                        "We've sent you an email with a link that you should "
-                        "click on to complete the account creation process.")
+                        "Email verification link sent successfully. Please "
+                        "click on the link in the email that we sent "
+                        "to your email address.")
                 else:
-                    form.add_error(None, ValidationError("Failed to register the user with IAM service"))
-            except TypeError as e:
+                    messages.error(
+                        request,
+                        "Unable to resend email verification link. Please "
+                        "contact the website administrator for further "
+                        "assistance.")
+                return redirect(
+                    reverse('custos_portal_auth:resend_email_link'))
+            except Exception as e:
                 logger.exception(
-                    "Failed to create account for user", exc_info=e)
-                form.add_error(None, ValidationError(e))
-            return render(request, 'custos_portal_auth/create_account.html', {
-                'options': settings.AUTHENTICATION_OPTIONS,
-                'form': form
-            })
+                    "Failed to resend email verification link", exc_info=e)
+                form.add_error(None, ValidationError(str(e)))
     else:
-        form = forms.CreateAccountForm()
-    return render(request, 'custos_portal_auth/create_account.html', {
-        'options': settings.AUTHENTICATION_OPTIONS,
+        form = forms.ResendEmailVerificationLinkForm()
+    return render(request, 'custos_portal_auth/verify_email.html', {
         'form': form
     })
 
 
-def verify_email(request, code):
+@sensitive_variables('password')
+def reset_password(request, code):
+    try:
+        password_reset_request = models.PasswordResetRequest.objects.get(
+            reset_code=code)
+    except ObjectDoesNotExist as e:
+        messages.error(
+            request,
+            "Reset password link is invalid. Please try again.")
+        return redirect(reverse('custos_portal_auth:forgot_password'))
+
+    now = datetime.now(timezone.utc)
+    if now - password_reset_request.created_date > timedelta(days=1):
+        password_reset_request.delete()
+        messages.error(
+            request,
+            "Reset password link has expired. Please try again.")
+        return redirect(reverse('custos_portal_auth:forgot_password'))
+
+    if request.method == "POST":
+        form = forms.ResetPasswordForm(request.POST)
+        if form.is_valid():
+            try:
+                password = form.cleaned_data['password']
+                # TODO Password is not updating properly
+                success = user_management_client.reset_password(settings.CUSTOS_TOKEN, password_reset_request.username,
+                                                                password)
+                logger.debug("Password reset result: {}".format(success))
+                if not success:
+                    messages.error(
+                        request, "Failed to reset password. Please try again.")
+                    return redirect(
+                        reverse('custos_portal_auth:forgot_password'))
+                else:
+                    password_reset_request.delete()
+                    messages.success(
+                        request,
+                        "You may now log in with your new password.")
+                    return redirect(
+                        reverse('custos_portal_auth:login_with_password'))
+            except Exception as e:
+                logger.exception(
+                    "Failed to reset password for user", exc_info=e)
+                form.add_error(None, ValidationError(str(e)))
+    else:
+        form = forms.ResetPasswordForm()
+    return render(request, 'custos_portal_auth/reset_password.html', {
+        'form': form,
+        'code': code
+    })
+
+
+def start_login(request):
+    return render(request, 'custos_portal_auth/login.html', {
+        'next': request.GET.get('next', None),
+        'options': settings.AUTHENTICATION_OPTIONS,
+    })
 
+
+def start_logout(request):
+    logout(request)
+    redirect_url = request.build_absolute_uri(resolve_url(settings.LOGOUT_REDIRECT_URL))
+    return redirect(settings.KEYCLOAK_LOGOUT_URL + "?redirect_uri=" + quote(redirect_url))
+
+
+def start_username_password_login(request):
+    # return bad request if password isn't a configured option
+    if 'password' not in settings.AUTHENTICATION_OPTIONS:
+        return HttpResponseBadRequest("Username/password login is not enabled")
+    return render(request,
+                  'custos_portal_auth/login_username_password.html',
+                  {
+                      'next': request.GET.get('next', None),
+                      'options': settings.AUTHENTICATION_OPTIONS,
+                      'login_type': 'password'
+                  })
+
+
+def verify_email(request, code):
     try:
         email_verification = models.EmailVerification.objects.get(verification_code=code)
         email_verification.verified = True
@@ -187,7 +345,9 @@ def verify_email(request, code):
         login_url = reverse('custos_portal_auth:login')
         if email_verification.next:
             login_url += "?" + urlencode({'next': email_verification.next})
-        if iam_admin_client.is_user_enabled(username):
+
+        print(user_management_client.is_user_enabled(settings.CUSTOS_TOKEN, "shivam_testing_3"))
+        if user_management_client.is_user_enabled(settings.CUSTOS_TOKEN, username).is_exist:
             logger.debug("User {} is already enabled".format(username))
             messages.success(
                 request,
@@ -197,11 +357,11 @@ def verify_email(request, code):
         else:
             logger.debug("Enabling user {}".format(username))
             # enable user and inform admins
-            iam_admin_client.enable_user(username)
-            user_profile = iam_admin_client.get_user(username)
-            email_address = user_profile.emails[0]
-            first_name = user_profile.firstName
-            last_name = user_profile.lastName
+            user_profile = MessageToDict(user_management_client.enable_user(settings.CUSTOS_TOKEN, username))
+            logger.debug(user_profile)
+            email_address = user_profile["email"]
+            first_name = user_profile["firstName"]
+            last_name = user_profile["lastName"]
             utils.send_new_user_email(request,
                                       username,
                                       email_address,
@@ -221,18 +381,17 @@ def verify_email(request, code):
             request,
             "Email verification failed. Please enter your username and we "
             "will send you another email verification link.")
-        return redirect(reverse('django_airavata_auth:resend_email_link'))
+        return redirect(reverse('custos_portal_auth:resend_email_link'))
     except Exception as e:
         logger.exception("Email verification processing failed!")
         messages.error(
             request,
             "Email verification failed. Please try clicking the email "
             "verification link again later.")
-        return redirect(reverse('django_airavata_auth:create_account'))
-
+        return redirect(reverse('custos_portal_auth:create_account'))
 
-def _create_and_send_email_verification_link(request, username, email, first_name, last_name, next_url):
 
+def _create_and_send_email_verification_link(request, username, email, first_name, last_name, next_url=None):
     email_verification = models.EmailVerification(username=username, next=next_url)
     email_verification.save()
 
@@ -250,3 +409,17 @@ def _create_and_send_email_verification_link(request, username, email, first_nam
         "url": verification_uri,
     })
     utils.send_email_to_user(models.VERIFY_EMAIL_TEMPLATE, context)
+
+
+def _validate_idp_alias(idp_alias):
+    external_auth_options = settings.AUTHENTICATION_OPTIONS['external']
+    valid_idp_aliases = [ext['idp_alias'] for ext in external_auth_options]
+    if idp_alias not in valid_idp_aliases:
+        raise Exception("idp_alias is not valid: {}".format(idp_alias))
+
+
+def _handle_login_redirect(request):
+    if request.is_gateway_admin:
+        return redirect(reverse('custos_portal_admin:list_requests'))
+    else:
+        return redirect(reverse('custos_portal_workspace:list_requests'))
diff --git a/custos_portal/custos_portal/context_processors.py b/custos_portal/custos_portal/context_processors.py
index e9e48ae..d1f4fd4 100644
--- a/custos_portal/custos_portal/context_processors.py
+++ b/custos_portal/custos_portal/context_processors.py
@@ -31,12 +31,6 @@ def airavata_app_registry(request):
                      if isinstance(app, CustosAppConfig) and
                      (app.app_enabled(request)
                       )]
-    for app in apps.get_app_configs():
-        if isinstance(app, CustosAppConfig):
-            print(app.url_app_name)
-            print(getattr(app, 'enabled', None))
-            print(app.app_enabled(request))
-    print("Custos apps", airavata_apps)
     # Sort by app_order then by verbose_name (case-insensitive)
     airavata_apps.sort(
         key=lambda app: "{:09}-{}".format(app.app_order,
diff --git a/custos_portal/custos_portal/settings.py b/custos_portal/custos_portal/settings.py
index 188b2b4..960825b 100644
--- a/custos_portal/custos_portal/settings.py
+++ b/custos_portal/custos_portal/settings.py
@@ -126,7 +126,7 @@ STATIC_URL = '/static/'
 STATICFILES_DIRS = [os.path.join(BASE_DIR, "custos_portal", "static")]
 
 LOGIN_URL = 'custos_portal_auth:login'
-LOGIN_REDIRECT_URL = 'django_airavata_workspace:dashboard'
+LOGIN_REDIRECT_URL = 'custos_portal_auth:dashboard'
 LOGOUT_REDIRECT_URL = '/'
 
 AUTHENTICATION_OPTIONS = {
diff --git a/custos_portal/custos_portal/settings_local.py b/custos_portal/custos_portal/settings_local.py
index 383a3d5..81b94ae 100644
--- a/custos_portal/custos_portal/settings_local.py
+++ b/custos_portal/custos_portal/settings_local.py
@@ -18,10 +18,9 @@ KEYCLOAK_AUTHORIZE_URL = 'https://keycloak.custos.scigap.org:31000/auth/realms/1
 KEYCLOAK_TOKEN_URL = 'https://airavata.host:8443/auth/realms/default/protocol/openid-connect/token'
 KEYCLOAK_LOGOUT_URL = 'https://keycloak.custos.scigap.org:31000/auth/realms/10000101/protocol/openid-connect/logout'
 # Optional: specify if using self-signed certificate or certificate from unrecognized CA
-#KEYCLOAK_CA_CERTFILE = os.path.join(BASE_DIR, "django_airavata", "resources", "incommon_rsa_server_ca.pem")
+# KEYCLOAK_CA_CERTFILE = os.path.join(BASE_DIR, "django_airavata", "resources", "incommon_rsa_server_ca.pem")
 KEYCLOAK_VERIFY_SSL = False
 
-
 SESSION_COOKIE_SECURE = False
 
 # Default email backend (for local development)
@@ -42,3 +41,4 @@ ADMINS = [('Admin Name', 'admin@example.com')]
 
 # Portal settings
 PORTAL_TITLE = 'Custos Admin Portal'
+GATEWAY_ID = 'Custos Portal'
diff --git a/custos_portal/custos_portal/templates/custos_portal/home.html b/custos_portal/custos_portal/templates/custos_portal/home.html
index d413ceb..a22b3d8 100644
--- a/custos_portal/custos_portal/templates/custos_portal/home.html
+++ b/custos_portal/custos_portal/templates/custos_portal/home.html
@@ -6,10 +6,11 @@
 <div class="container">
 <div class="jumbotron">
         <h1>Welcome!</h1>
-        <p>This is the Django based web portal for Custos Admin.</p>
+        <p>This is the Django based web portal for Custos.</p>
+    {% if not user.is_authenticated %}
         <p><a class="btn btn-primary btn-lg" href="{% url 'custos_portal_auth:login' %}" role="button">Login ยป</a></p>
         <p><a class="btn btn-primary btn-lg" href="{% url 'custos_portal_auth:create_account' %}" role="button">Register</a></p>
-
+    {% endif %}
 </div>
 {% if user.is_authenticated %}
     <div class="row">
diff --git a/custos_portal/custos_portal/templates/custos_portal_auth/forgot_password.html b/custos_portal/custos_portal/templates/custos_portal_auth/forgot_password.html
index 30524ab..d40d14f 100644
--- a/custos_portal/custos_portal/templates/custos_portal_auth/forgot_password.html
+++ b/custos_portal/custos_portal/templates/custos_portal_auth/forgot_password.html
@@ -12,7 +12,7 @@
           <h5 class="card-title">Forgot Password?</h5>
           <p class="card-text">If you forgot your password you can reset it. Just enter the username for your account and click Email Reset Link.</p>
           {% include "./partials/messages.html" %}
-          {% url 'django_airavata_auth:forgot_password' as form_url %}
+          {% url 'custos_portal_auth:forgot_password' as form_url %}
           {% include "./partials/form.html" with form=form url=form_url submit_text="Email Reset Link" %}
         </div>
       </div>
diff --git a/custos_portal/custos_portal/templates/custos_portal_auth/login_username_password.html b/custos_portal/custos_portal/templates/custos_portal_auth/login_username_password.html
index 84d2b5f..b948768 100644
--- a/custos_portal/custos_portal/templates/custos_portal_auth/login_username_password.html
+++ b/custos_portal/custos_portal/templates/custos_portal_auth/login_username_password.html
@@ -9,7 +9,7 @@
     <div class="row">
         <div class="col">
             <p>
-            <small>See <a href="{% url 'django_airavata_auth:login' %}" class="text-muted">all login options</small>
+            <small>See <a href="{% url 'custos_portal_auth:login' %}" class="text-muted">all login options</small>
             </p>
         </div>
     </div>
diff --git a/custos_portal/custos_portal/templates/custos_portal_auth/partials/username_password_login_form.html b/custos_portal/custos_portal/templates/custos_portal_auth/partials/username_password_login_form.html
index e2ca5b3..9b4ceef 100644
--- a/custos_portal/custos_portal/templates/custos_portal_auth/partials/username_password_login_form.html
+++ b/custos_portal/custos_portal/templates/custos_portal_auth/partials/username_password_login_form.html
@@ -13,7 +13,7 @@
                     <div class="form-group">
                         <label for="password">Password</label>
                         <input type="password" class="form-control" id="password" name="password" placeholder="Password" required>
-                        <small class="form-text text-muted"><a href="#}" class="text-reset">Forgot your password?</a></small>
+                        <small class="form-text text-muted"><a href="{% url 'custos_portal_auth:forgot_password' %}" class="text-reset">Forgot your password?</a></small>
                     </div>
                     {% if next %}
                     <input type="hidden" name="next" value="{{ next }}"/>
diff --git a/custos_portal/custos_portal/templates/custos_portal_auth/reset_password.html b/custos_portal/custos_portal/templates/custos_portal_auth/reset_password.html
index 4227153..196ea0f 100644
--- a/custos_portal/custos_portal/templates/custos_portal_auth/reset_password.html
+++ b/custos_portal/custos_portal/templates/custos_portal_auth/reset_password.html
@@ -11,7 +11,7 @@
         <div class="card-body">
           <h5 class="card-title">Reset Password</h5>
           {% include "./partials/messages.html" %}
-          {% url 'django_airavata_auth:reset_password' code as form_url %}
+          {% url 'custos_portal_auth:reset_password' code as form_url %}
           {% include "./partials/form.html" with form=form url=form_url submit_text="Reset Password" %}
         </div>
       </div>
diff --git a/custos_portal/custos_portal/templates/custos_portal_auth/verify_email.html b/custos_portal/custos_portal/templates/custos_portal_auth/verify_email.html
index 537d970..32e9753 100644
--- a/custos_portal/custos_portal/templates/custos_portal_auth/verify_email.html
+++ b/custos_portal/custos_portal/templates/custos_portal_auth/verify_email.html
@@ -10,7 +10,7 @@
         <div class="card-body">
           <h5 class="card-title">Resend Email Verification Link</h5>
           {% include "./partials/messages.html" %}
-          {% url 'django_airavata_auth:resend_email_link' code as form_url %}
+          {% url 'custos_portal_auth:resend_email_link' code as form_url %}
           {% include "./partials/form.html" with form=form url=form_url submit_text="Resend" %}
         </div>
       </div>