Skip to content

Commit

Permalink
Feat/custom user (bcgov#66)
Browse files Browse the repository at this point in the history
* Start creating a custom auth user

* Swap custom user model

* work on custom user model

* Add user to application

* tidy stuff

* fix details page
  • Loading branch information
Naomi authored Apr 13, 2022
1 parent 4e26226 commit 0f12c91
Show file tree
Hide file tree
Showing 23 changed files with 343 additions and 252 deletions.
6 changes: 5 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"python.linting.pycodestyleEnabled": true,
"python.linting.pylintEnabled": false,
"python.linting.enabled": true,
"python.formatting.provider": "black",

"editor.defaultFormatter": "esbenp.prettier-vscode",
"[javascript]": {
Expand All @@ -10,5 +11,8 @@
"editor.formatOnPaste": true,
"editor.formatOnSave": true,
"eslint.format.enable": true,
"diffEditor.codeLens": true
"diffEditor.codeLens": true,
"[python]": {
"editor.defaultFormatter": "ms-python.python"
}
}
11 changes: 5 additions & 6 deletions django/api/admin.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
from django.contrib import admin
from .models.go_electric_rebate_application import GoElectricRebateApplication
from django.contrib.admin.templatetags import admin_modify
from django.contrib.auth.models import User
from django.contrib.auth.models import Group

admin.site.unregister(User)
admin.site.unregister(Group)
submit_row = admin_modify.submit_row


def submit_row_custom(context):
ctx = submit_row(context)
ctx['show_save_and_add_another'] = False
ctx['show_save_and_continue'] = False
ctx["show_save_and_add_another"] = False
ctx["show_save_and_continue"] = False
return ctx


Expand All @@ -23,6 +21,7 @@ def submit_row_custom(context):
class GoElectricRebateApplicationAdmin(admin.ModelAdmin):
readonly_fields = (
"id",
"application_type",
"sin",
"last_name",
"first_name",
Expand All @@ -38,8 +37,8 @@ class GoElectricRebateApplicationAdmin(admin.ModelAdmin):
"doc1_tag",
"doc2",
"doc2_tag",
"create_user",
"update_user"
"user",
"spouse_email",
)

def has_delete_permission(self, request, obj=None):
Expand Down
72 changes: 41 additions & 31 deletions django/api/keycloak_authentication.py
Original file line number Diff line number Diff line change
@@ -1,61 +1,71 @@
import base64
from keycloak import KeycloakOpenID
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.contrib.auth.models import User
from django.contrib.auth import get_user_model
from rest_framework.authentication import TokenAuthentication
from rest_framework.exceptions import AuthenticationFailed

import logging


log = logging.getLogger('KeycloakAuthentication')
log = logging.getLogger("KeycloakAuthentication")

ITVRUser = get_user_model()


def base64_decode(data: str) -> str:
"""
We can check the identity provider of the token and then
verify or pass on the request.
"""

data = data.encode("ascii")

rem = len(data) % 4

if rem > 0:
data += b"=" * (4 - rem)
return base64.urlsafe_b64decode(data).decode("utf-8")


class KeycloakAuthentication(TokenAuthentication):
keyword = 'Bearer'
keyword = "Bearer"

def authenticate_credentials(self, token):
keycloak_openid = KeycloakOpenID(
server_url=settings.KEYCLOAK_URL,
client_id=settings.KEYCLOAK_CLIENT_ID,
realm_name=settings.KEYCLOAK_REALM
realm_name=settings.KEYCLOAK_REALM,
)

# Decode the token from the front-end
KEYCLOAK_PUBLIC_KEY = \
"-----BEGIN PUBLIC KEY-----\n" + \
keycloak_openid.public_key() + \
"\n-----END PUBLIC KEY-----"
KEYCLOAK_PUBLIC_KEY = (
"-----BEGIN PUBLIC KEY-----\n"
+ keycloak_openid.public_key()
+ "\n-----END PUBLIC KEY-----"
)

options = {
'verify_signature': True,
'verify_aud': True,
'verify_exp': True
}
options = {"verify_signature": True, "verify_aud": True, "verify_exp": True}

try:
token_info = keycloak_openid.decode_token(
token,
key=KEYCLOAK_PUBLIC_KEY,
options=options
token, key=KEYCLOAK_PUBLIC_KEY, options=options
)
except Exception:
raise AuthenticationFailed(
'Invalid Token'
)

username = token_info.get('preferred_username')
raise AuthenticationFailed("Invalid Token")

# TODO make a ticket to improve this
user = None
try:
user = User.objects.get(username=username)
except ObjectDoesNotExist:
log.warn(
'KeycloakAuthentication user does not exist'
)
user, created = ITVRUser.objects.get_or_create(
username=token_info.get("sub"),
identity_provider=token_info.get("identity_provider"),
defaults={
"display_name": token_info.get("display_name"),
"email": token_info.get("email"),
},
)

if user is None:
user = User.objects.create_user(username=username)
if created:
log.debug("Created user")
log.debug(user)

return user, token
36 changes: 31 additions & 5 deletions django/api/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Generated by Django 4.0.1 on 2022-03-23 22:39
# Generated by Django 4.0.1 on 2022-04-13 03:00

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import encrypted_fields.fields
import uuid

Expand All @@ -10,16 +12,13 @@ class Migration(migrations.Migration):
initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='GoElectricRebateApplication',
fields=[
('create_timestamp', models.DateTimeField(auto_now_add=True, null=True)),
('create_user', models.CharField(default='SYSTEM', max_length=130)),
('update_timestamp', models.DateTimeField(auto_now=True, null=True)),
('update_user', models.CharField(max_length=130, null=True)),
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('sin', encrypted_fields.fields.EncryptedCharField(max_length=9)),
('last_name', models.CharField(max_length=250)),
Expand All @@ -35,9 +34,36 @@ class Migration(migrations.Migration):
('doc1', models.ImageField(upload_to='docs')),
('doc2', models.ImageField(upload_to='docs')),
('verified', models.BooleanField()),
('spouse_email', models.EmailField(blank=True, max_length=250, null=True)),
('application_type', models.CharField(max_length=25)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'go_electric_rebate_application',
},
),
migrations.CreateModel(
name='HouseholdMember',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sin', encrypted_fields.fields.EncryptedCharField(max_length=9)),
('last_name', models.CharField(max_length=250)),
('first_name', models.CharField(max_length=250)),
('middle_names', models.CharField(blank=True, max_length=250, null=True)),
('email', models.EmailField(max_length=250)),
('date_of_birth', models.DateField()),
('doc1', models.ImageField(upload_to='docs')),
('doc2', models.ImageField(upload_to='docs')),
('verified', models.BooleanField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('application', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='api.goelectricrebateapplication')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'household_member',
},
),
]

This file was deleted.

Loading

0 comments on commit 0f12c91

Please sign in to comment.