Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Account activation has been enabled. #276

Merged
merged 1 commit into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading