diff --git a/bd_api/apps/account_auth/views.py b/bd_api/apps/account_auth/views.py
index 61d1b406..6c0d7f7f 100644
--- a/bd_api/apps/account_auth/views.py
+++ b/bd_api/apps/account_auth/views.py
@@ -12,49 +12,75 @@
from bd_api.apps.account.models import Account
from bd_api.apps.account_auth.models import Access, Domain, Token
+URI = str
+Status = bool
-def get_redirect_uri(request: HttpRequest, default: str = None) -> str:
- # First from X-Original-Url header.
- redirect_uri = request.headers.get("X-Original-URL", None)
- if redirect_uri:
- return redirect_uri
-
- # Then from the query string.
- redirect_uri = request.GET.get("rd", None)
- if redirect_uri:
- return redirect_uri
-
- # Finally, from the default.
- return default
+def auth(request: HttpRequest) -> HttpResponse:
+ _, token, domain, user, success = authorize(request)
+ if success:
+ return HttpResponse(status=200)
+ if not success:
+ store_access(token=token, domain=domain, user=user, success=success)
+ return HttpResponse(status=401)
-def is_authenticated(request: HttpRequest) -> Tuple[str, Token, Domain, Account, bool]:
- """
- This checks for authentication. It returns a tuple with 5 elements:
- - The redirect URI.
- - The token.
- - The domain.
- - The user.
- - Whether the authentication was successful.
- How it works:
+def signin(request: HttpRequest) -> HttpResponse:
+ redirect_uri, _, _, user, success = authorize(request)
+ if success and redirect_uri:
+ return redirect(redirect_uri)
+ if success and not redirect_uri:
+ return HttpResponse(
+ "Please specify a redirect URL. Sign out.",
+ status=422,
+ )
+ if request.user and request.user.is_authenticated and redirect_uri:
+ return HttpResponse(
+ "Please contact the support. Sign out.",
+ status=403,
+ )
+ if request.user and request.user.is_authenticated and not redirect_uri:
+ return HttpResponse(
+ "Please contact the support. Sign out.",
+ status=422,
+ )
+ if request.method == "GET":
+ return render(
+ request,
+ "signin.html",
+ context={"recaptcha_site_key": settings.RECAPTCHA_SITE_KEY},
+ )
+ if request.method == "POST":
+ username = request.POST.get("username", None)
+ password = request.POST.get("password", None)
+ recaptcha_response = request.POST.get("g-recaptcha-response", None)
+ if not recaptcha_response or not validate_recaptcha_token(recaptcha_response):
+ return HttpResponse(
+ "Invalid captcha. Try again.",
+ status=401,
+ )
+ if username and password:
+ if user := authenticate(request, username=username, password=password):
+ login(request, user)
+ if redirect_uri:
+ return redirect(redirect_uri)
+ return HttpResponse(
+ "Please specify a redirect URL. Sign out.",
+ status=422,
+ )
+ return HttpResponse(
+ "Invalid username or password. Try again.",
+ status=401,
+ )
- - First, it tries to extract the desired domain from the request.
- - If it fails, it returns a 401.
- - Then, if there's an user logged in, it iterates over its tokens,
- looking for a token with the desired domain.
- - If it finds one, it returns a 200.
- - If it doesn't, it returns a 401.
+def signout(request: HttpRequest) -> HttpResponse:
+ if request.user.is_authenticated:
+ logout(request)
+ return redirect("login")
- - Finally, if no user is logged in, tries to extract the token from
- the request headers.
- - If it finds one, it checks if it's valid for the domain.
- - If it is, it returns a 200.
- - If it isn't, it returns a 401.
- - If no token is found, it returns a 401.
- """
+def authorize(request: HttpRequest) -> Tuple[URI, Token, Domain, Account, Status]:
# Tries to extract the desired domain from the request.
redirect_uri = get_redirect_uri(request)
if not redirect_uri:
@@ -69,8 +95,12 @@ def is_authenticated(request: HttpRequest) -> Tuple[str, Token, Domain, Account,
# If it fails, it returns false.
return redirect_uri, None, None, None, False
- # If there's an user logged in, it iterates over its tokens
+ # If there's an user logged in
if request.user.is_authenticated:
+ # If user is staff, it returns true
+ if request.user.is_staff:
+ return redirect_uri, None, domain, request.user, True
+ # If user is not staff, it iterates over its tokens
for token in request.user.tokens.all():
# If it finds one, it returns true.
if token.domain == domain:
@@ -78,11 +108,11 @@ def is_authenticated(request: HttpRequest) -> Tuple[str, Token, Domain, Account,
# If it doesn't, it returns false.
return redirect_uri, None, domain, request.user, False
- # Finally, if no user is logged in, tries to extract the token from
- # the request headers.
+ # Finally, if no user is logged in,
+ # tries to extract the token from the request headers
token = request.headers.get("Authorization", None)
if not token:
- # If it fails, it returns false.
+ # If it fails, it returns false
return redirect_uri, None, domain, None, False
# If it finds the Authorization header, extract token
@@ -99,9 +129,9 @@ def is_authenticated(request: HttpRequest) -> Tuple[str, Token, Domain, Account,
# If it fails, it returns false.
return redirect_uri, None, domain, None, False
- # Token must have same domain, its expiry date must be in the future,
- # and it must be active.
- if (token.domain == domain) and (token.expiry_date > timezone.now()) and (token.is_active):
+ # Token must have same domain,
+ # its expiry date must be in the future, and it must be active.
+ if token.domain == domain and token.expiry_date > timezone.now() and token.is_active:
return redirect_uri, token, domain, token.user, True
# If it isn't, it returns a 401.
@@ -127,85 +157,16 @@ def store_access(
access.save()
+def get_redirect_uri(request: HttpRequest, default: str = None) -> str:
+ if redirect_uri := request.headers.get("X-Original-URL", None):
+ return redirect_uri
+ if redirect_uri := request.GET.get("rd", None):
+ return redirect_uri
+ return default
+
+
def validate_recaptcha_token(token: str) -> bool:
- """
- Validates the recaptcha token.
- """
url = "https://www.google.com/recaptcha/api/siteverify"
data = {"secret": settings.RECAPTCHA_SECRET_KEY, "response": token}
response = post(url, data=data)
return response.json()["success"]
-
-
-def auth(request: HttpRequest) -> HttpResponse:
- """
- This returns either 200 or 401. For either case, we store the
- access log in the database.
-
- How it works:
-
- - First, it tries to extract the desired domain from the request.
- - If it fails, it returns a 401.
-
- - Then, if there's an user logged in, it iterates over its tokens,
- looking for a token with the desired domain.
- - If it finds one, it returns a 200.
- - If it doesn't, it returns a 401.
-
- - Finally, if no user is logged in, tries to extract the token from
- the request headers.
- - If it finds one, it checks if it's valid for the domain.
- - If it is, it returns a 200.
- - If it isn't, it returns a 401.
-
- - If no token is found, it returns a 401.
- """
- _, token, domain, user, success = is_authenticated(request)
- if not success:
- store_access(token=token, domain=domain, user=user, success=success)
- if success:
- return HttpResponse(status=200)
- return HttpResponse(status=401)
-
-
-def signin(request: HttpRequest) -> HttpResponse:
- redirect_uri, _, _, user, success = is_authenticated(request)
- if success:
- if redirect_uri:
- return redirect(redirect_uri)
- return HttpResponse("You are already signed in. Sign out.")
- if request.user and request.user.is_authenticated:
- if redirect_uri:
- return HttpResponse("You do not have access to this page.", status=401)
- return HttpResponse("You are already signed in. Sign out.")
- if request.method == "POST":
- username = request.POST.get("username", None)
- password = request.POST.get("password", None)
- recaptcha_response = request.POST.get("g-recaptcha-response", None)
- if (not recaptcha_response) or (not validate_recaptcha_token(recaptcha_response)):
- return HttpResponse(
- "Captcha is invalid. Try again.", status=401
- )
- if username and password:
- user = authenticate(request, username=username, password=password)
- if user is not None:
- login(request, user)
- if redirect_uri:
- return redirect(redirect_uri)
- return HttpResponse("You are now signed in. Sign out.")
- return HttpResponse("Invalid username or password.", status=401)
- return render(
- request,
- "signin.html",
- context={"recaptcha_site_key": settings.RECAPTCHA_SITE_KEY},
- )
-
-
-def signout(request: HttpRequest) -> HttpResponse:
- if request.user.is_authenticated:
- logout(request)
- redirect_url = get_redirect_uri(request, default=None)
- if redirect_url:
- return redirect(redirect_url)
- return HttpResponse("You are now signed out. Sign in.")
- return HttpResponse("You are not signed in. Sign in.", status=401)