Skip to content

Commit

Permalink
Account activation has been enabled.
Browse files Browse the repository at this point in the history
On branch account_authorization
Changes to be committed:
	modified:   api/urls.py
	modified:   api/views.py
	modified:   authentication/apis.py
	modified:   authentication/selectors.py
	modified:   authentication/services.py
	modified:   authentication/urls.py
  • Loading branch information
HadleyKing committed Mar 13, 2024
1 parent 448b1e9 commit b3abd90
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 94 deletions.
5 changes: 0 additions & 5 deletions api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from drf_yasg import openapi

from api.views import (
ApiAccountsActivateUsernameTempIdentifier,
ApiAccountsDescribe,
ApiGroupsCreate,
ApiGroupsInfo,
Expand Down Expand Up @@ -127,10 +126,6 @@
ObjectIdRootObjectIdVersion.as_view(),
),
path("<str:object_id_root>", ObjectIdRootObjectId.as_view()),
path(
"api/accounts/activate/<str:username>/<str:temp_identifier>",
ApiAccountsActivateUsernameTempIdentifier.as_view(),
),
path("api/accounts/describe/", ApiAccountsDescribe.as_view()),
path("api/groups/group_info/", ApiGroupsInfo.as_view()),
path("api/groups/create/", ApiGroupsCreate.as_view()),
Expand Down
65 changes: 0 additions & 65 deletions api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,71 +130,6 @@ def check_get(request) -> Response:
# Placeholder
return Response(status=status.HTTP_200_OK)


class ApiAccountsActivateUsernameTempIdentifier(APIView):
"""
Activate an account
--------------------
This endpoint is a GET request to activate a new account.
To activate an account during registration we receive an email or a
temporary identifier to authenticate and activate account. This endpoint
will check the validity of the provided temporary identifier for a specific
user account. This is open to anyone to activate a new account, as long as
they have a valid token generated by this host. This allows other users
to act as the verification layer in addition to the system.
"""

authentication_classes = []
permission_classes = []

# For the success and error messages
renderer_classes = [TemplateHTMLRenderer]
template_name = "api/account_activation_message.html"

auth = []
auth.append(
openapi.Parameter(
"username",
openapi.IN_PATH,
description="Username to be authenticated.",
type=openapi.TYPE_STRING,
)
)
auth.append(
openapi.Parameter(
"temp_identifier",
openapi.IN_PATH,
description="The temporary identifier needed to authenticate the activation. This "
"is found in the temporary account table (i.e. where an account is "
"staged).",
type=openapi.TYPE_STRING,
)
)

@swagger_auto_schema(
manual_parameters=auth,
responses={
200: "Account has been activated.",
403: "Requestor's credentials were rejected.",
},
tags=["Account Management"],
)
def get(self, request, username: str, temp_identifier: str):
check_get(request)
checked = None
if checked is None:
return GET_activate_account(
username=username, temp_identifier=temp_identifier
)
else:
return Response(
{"activation_success": False, "status": status.HTTP_400_BAD_REQUEST}
)


# Source: https://www.django-rest-framework.org/api-guide/authentication/#by-exposing-an-api-endpoint
class ApiAccountsDescribe(APIView):
"""
Expand Down
90 changes: 77 additions & 13 deletions authentication/apis.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
create_bcodb_user,
send_bcodb,
validate_auth_service,
new_user_email
send_new_user_email
)

class NewAccountApi(APIView):
Expand All @@ -33,14 +33,6 @@ class NewAccountApi(APIView):
The account create depends on creation of an account in the associated
user database. The authentication as well as the user database host
information is used to make this request.
```JSON
{
"hostname": "http://localhost:8000",
"email": "[email protected]",
"token": "eyJ1c2VyX2lkIjoyNCwidXNlcm5hbWUiOiJoYWRsZXlraW5nIiwiZXhwIjoxNjQwNzE5NTUwLCJlbWFpbCI6ImhhZGxleV9raW5nQGd3dS5lZHUiLCJvcmlnX2lhdCI6MTY0MDExNDc1MH0.7G3VPmxUBOWFfu-fMt1_UsWAcH_Gd1DfpQa83EwFwYY"
}
```
"""

class InputSerializer(serializers.Serializer):
Expand Down Expand Up @@ -109,6 +101,7 @@ def post(self, request) -> Response:
status=status.HTTP_201_CREATED,
data={"message":"Testing account request successful!!"}
)

if check_user_email(email) is True:
return Response(
status=status.HTTP_409_CONFLICT,
Expand All @@ -129,14 +122,84 @@ def post(self, request) -> Response:
)

try:
new_user_email(serializer.validated_data)
return Response(status=status.HTTP_201_CREATED, data={"message":""})
send_new_user_email(serializer.validated_data)
return Response(
status=status.HTTP_201_CREATED,
data={"message":"Account request granted. Check your email"\
+ " for an activation link."}
)
except Exception as error:
return Response(
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
data={"message": str(error)}
)

class AccountActivateApi(APIView):
"""
Activate an account
--------------------
This endpoint is a GET request to activate a new account.
To activate an account during registration the userwill receive an email
or a temporary identifier to authenticate and activate account. This
endpoint will check the validity of the provided temporary identifier for
a specific user account. This is open to anyone to activate a new account,
as long as they have a valid token generated by this host. This can allow
other users to act as the verification layer in addition to the system.
"""

authentication_classes = []
permission_classes = []

auth = []
auth.append(
openapi.Parameter(
"username",
openapi.IN_PATH,
description="Username to be authenticated.",
type=openapi.TYPE_STRING,
default="[email protected]"
)
)
auth.append(
openapi.Parameter(
"temp_identifier",
openapi.IN_PATH,
description="The temporary identifier sent",
type=openapi.TYPE_STRING,
default="testTempIdentifier123456789"
)
)

@swagger_auto_schema(
manual_parameters=auth,
responses={
200: "Account has been activated.",
403: "Requestor's credentials were rejected.",
},
tags=["Authentication and Account Management"],
)

def get(self, request, username: str, temp_identifier: str) -> Response:
if check_user_email(username) is True:
return Response(
status=status.HTTP_409_CONFLICT,
data={
"message":f"CONFLICT: That account, {username}, has already "\
+ "been activated."
}
)
new_user = check_new_user(username, temp_identifier)
print(new_user)
create_bcodb_user(new_user.email)
new_user.delete()
return Response(
status=status.HTTP_200_OK,
data={"message":f"Account for {username} has been activated"}
)


class RegisterUserNoVerificationAPI(APIView):
"""Register BCODB
API View to register a new BCODB user with out an email verification step.
Expand Down Expand Up @@ -181,14 +244,15 @@ def post(self, request):
user_info.is_valid(raise_exception=True)
token = user_info.validated_data['token']
url = user_info.validated_data['hostname']
email = user_info.validated_data['email']
if validate_token(token, url) is False:
return Response(status=status.HTTP_401_UNAUTHORIZED, data={"message": "portal authentication was invalid"})
if check_user_email(user_info.validated_data['email']) is True:
if check_user_email(email) is True:
return Response(
status=status.HTTP_409_CONFLICT,
data={"message": "A BCODB account with that email already exists"}
)
user = create_bcodb_user(user_info=user_info.validated_data)
user = create_bcodb_user(email)
data = json.dumps(get_user_info(user), default=str)
response = send_bcodb(
data=data, request_info=user_info.validated_data
Expand Down
12 changes: 8 additions & 4 deletions authentication/selectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,18 @@ def check_user_email(email: str)-> bool:
except User.DoesNotExist:
return False

def check_new_user(email: str)-> bool:
def check_new_user(email: str, temp_identifier:str=None):
"""Check for new user
Using the provided email check for a new user in the DB
Using the provided email check for a new user in the DB.
If the temp id is supplied and matches it will return the new user object.
"""

try:
if NewUser.objects.get(email=email):
new_user = NewUser.objects.get(email=email)
if new_user.temp_identifier == temp_identifier:
return new_user
else:
return True
except NewUser.DoesNotExist:
return False
Expand Down
10 changes: 5 additions & 5 deletions authentication/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def validate_token(token: str, url: str)-> bool:
return True

@transaction.atomic
def new_user_email(user_info: dict) -> 0:
def send_new_user_email(user_info: dict) -> 0:
"""Send New User Email
New BCODB user authentication email
Expand All @@ -171,7 +171,7 @@ def new_user_email(user_info: dict) -> 0:
subject="Registration for BioCompute Portal",
message="Testing.",
html_message='<html><body><p>Please click this link within the next' \
+ ' 10 minutes to activate your BioCompute Portal account: ' \
+ ' 24 hours to activate your BioCompute Portal account: ' \
+ f'<a href={activation_link} target="_blank">{activation_link}' \
+ '</a>.</p></body></html>',
from_email="[email protected]",
Expand All @@ -182,13 +182,13 @@ def new_user_email(user_info: dict) -> 0:
print("Email signal sent")
return 0

def create_bcodb_user(user_info: dict) -> User:
def create_bcodb_user(email: str) -> User:
"""Create BCODB user
"""

username = user_info["email"].split("@")[0]
username = email.split("@")[0]
user = User.objects.create_user(
username=username, email=user_info["email"]
username=username, email=email
)
user.set_unusable_password()
user.full_clean()
Expand Down
17 changes: 15 additions & 2 deletions authentication/urls.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
# authentication/urls.py

from django.urls import path
from authentication.apis import RegisterBcodbAPI, AddAuthenticationApi, RemoveAuthenticationApi, ResetTokenApi
from authentication.apis import (
NewAccountApi,
AccountActivateApi,
RegisterUserNoVerificationAPI,
AddAuthenticationApi,
RemoveAuthenticationApi,
ResetTokenApi
)

urlpatterns = [
path("auth/register/", RegisterBcodbAPI.as_view()),
path(
"accounts/activate/<str:username>/<str:temp_identifier>",
AccountActivateApi.as_view(),
),
# path("api/accounts/describe/", ApiAccountsDescribe.as_view()),
path("accounts/new/", NewAccountApi.as_view()),
path("auth/register/", RegisterUserNoVerificationAPI.as_view()),
path("auth/add/", AddAuthenticationApi.as_view()),
path("auth/remove/", RemoveAuthenticationApi.as_view()),
path("auth/reset_token/", ResetTokenApi.as_view())
Expand Down

0 comments on commit b3abd90

Please sign in to comment.