Skip to content

Commit

Permalink
Syntax revisions
Browse files Browse the repository at this point in the history
  • Loading branch information
na-stewart committed Jun 21, 2024
1 parent 73526ec commit a17ffb0
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 161 deletions.
168 changes: 82 additions & 86 deletions sanic_security/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
ExpiredError,
)
from sanic_security.models import Account, AuthenticationSession, Role, TwoStepSession
from sanic_security.utils import get_ip

"""
Copyright (c) 2020-present Nicholas Aidan Stewart
Expand All @@ -45,86 +44,6 @@
password_hasher = PasswordHasher()


def validate_email(email: str) -> str:
"""
Validates email format.
Args:
email (str): Email being validated.
Returns:
email
Raises:
CredentialsError
"""
if not re.search(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$", email):
raise CredentialsError("Please use a valid email address.", 400)
return email


def validate_username(username: str) -> str:
"""
Validates username format.
Args:
username (str): Username being validated.
Returns:
username
Raises:
CredentialsError
"""
if not re.search(r"^[A-Za-z0-9_-]{3,32}$", username):
raise CredentialsError(
"Username must be between 3-32 characters and not contain any special characters other than _ or -.",
400,
)
return username


def validate_phone(phone: str) -> str:
"""
Validates phone number format.
Args:
phone (str): Phone number being validated.
Returns:
phone
Raises:
CredentialsError
"""
if phone and not re.search(
r"^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$", phone
):
raise CredentialsError("Please use a valid phone number.", 400)
return phone


def validate_password(password: str) -> str:
"""
Validates password requirements.
Args:
password (str): Password being validated.
Returns:
password
Raises:
CredentialsError
"""
if not re.search(r"^(?=.*[A-Z])(?=.*\d)(?=.*[@#$%^&+=!]).*$", password):
raise CredentialsError(
"Password must contain one capital letter, one number, and one special character",
400,
)
return password


async def register(
request: Request, verified: bool = False, disabled: bool = False
) -> Account:
Expand Down Expand Up @@ -219,9 +138,6 @@ async def login(
request, account, requires_second_factor=require_second_factor
)
except VerifyMismatchError:
logger.warning(
f"Client ({get_ip(request)}) login password attempt is incorrect"
)
raise CredentialsError("Incorrect password.", 401)


Expand Down Expand Up @@ -303,9 +219,9 @@ async def fulfill_second_factor(request: Request) -> AuthenticationSession:
authentication_Session
"""
authentication_session = await AuthenticationSession.decode(request)
two_step_session = await TwoStepSession.decode(request)
if not authentication_session.requires_second_factor:
raise SecondFactorFulfilledError()
two_step_session = await TwoStepSession.decode(request)
two_step_session.validate()
await two_step_session.check_code(request, request.form.get("code"))
authentication_session.requires_second_factor = False
Expand Down Expand Up @@ -358,7 +274,7 @@ def create_initial_admin_account(app: Sanic) -> None:
"""

@app.listener("before_server_start")
async def generate(app, loop):
async def create(app, loop):
try:
role = await Role.filter(name="Head Admin").get()
except DoesNotExist:
Expand Down Expand Up @@ -386,3 +302,83 @@ async def generate(app, loop):
)
await account.roles.add(role)
logger.info("Initial admin account created.")


def validate_email(email: str) -> str:
"""
Validates email format.
Args:
email (str): Email being validated.
Returns:
email
Raises:
CredentialsError
"""
if not re.search(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$", email):
raise CredentialsError("Please use a valid email address.", 400)
return email


def validate_username(username: str) -> str:
"""
Validates username format.
Args:
username (str): Username being validated.
Returns:
username
Raises:
CredentialsError
"""
if not re.search(r"^[A-Za-z0-9_-]{3,32}$", username):
raise CredentialsError(
"Username must be between 3-32 characters and not contain any special characters other than _ or -.",
400,
)
return username


def validate_phone(phone: str) -> str:
"""
Validates phone number format.
Args:
phone (str): Phone number being validated.
Returns:
phone
Raises:
CredentialsError
"""
if phone and not re.search(
r"^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$", phone
):
raise CredentialsError("Please use a valid phone number.", 400)
return phone


def validate_password(password: str) -> str:
"""
Validates password requirements.
Args:
password (str): Password being validated.
Returns:
password
Raises:
CredentialsError
"""
if not re.search(r"^(?=.*[A-Z])(?=.*\d)(?=.*[@#$%^&+=!]).*$", password):
raise CredentialsError(
"Password must contain one capital letter, one number, and one special character",
400,
)
return password
48 changes: 22 additions & 26 deletions sanic_security/authorization.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import functools
import logging
from fnmatch import fnmatch

from sanic.request import Request
Expand All @@ -8,7 +7,6 @@
from sanic_security.authentication import authenticate
from sanic_security.exceptions import AuthorizationError, AnonymousError
from sanic_security.models import Role, Account, AuthenticationSession
from sanic_security.utils import get_ip

"""
Copyright (c) 2020-present Nicholas Aidan Stewart
Expand Down Expand Up @@ -67,7 +65,6 @@ async def check_permissions(
):
if fnmatch(required_permission, role_permission):
return authentication_session
logging.warning(f"Client ({get_ip(request)}) has insufficient permissions.")
raise AuthorizationError("Insufficient permissions required for this action.")


Expand Down Expand Up @@ -100,10 +97,31 @@ async def check_roles(request: Request, *required_roles: str) -> AuthenticationS
for role in roles:
if role.name in required_roles:
return authentication_session
logging.warning(f"Client ({get_ip(request)}) has insufficient roles.")
raise AuthorizationError("Insufficient roles required for this action.")


async def assign_role(
name: str, account: Account, permissions: str = None, description: str = None
) -> Role:
"""
Easy account role assignment. Role being assigned to an account will be created if it doesn't exist.
Args:
name (str): The name of the role associated with the account.
account (Account): The account associated with the created role.
permissions (str): The permissions of the role associated with the account. Permissions must be separated via comma and in wildcard format.
description (str): The description of the role associated with the account.
"""
try:
role = await Role.filter(name=name).get()
except DoesNotExist:
role = await Role.create(
description=description, permissions=permissions, name=name
)
await account.roles.add(role)
return role


def require_permissions(*required_permissions: str):
"""
Authenticates client and determines if the account has sufficient permissions for an action.
Expand Down Expand Up @@ -180,25 +198,3 @@ async def wrapper(request, *args, **kwargs):
return wrapper

return decorator


async def assign_role(
name: str, account: Account, permissions: str = None, description: str = None
) -> Role:
"""
Easy account role assignment. Role being assigned to an account will be created if it doesn't exist.
Args:
name (str): The name of the role associated with the account.
account (Account): The account associated with the created role.
permissions (str): The permissions of the role associated with the account. Permissions must be separated via comma and in wildcard format.
description (str): The description of the role associated with the account.
"""
try:
role = await Role.filter(name=name).get()
except DoesNotExist:
role = await Role.create(
description=description, permissions=permissions, name=name
)
await account.roles.add(role)
return role
27 changes: 9 additions & 18 deletions sanic_security/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ def __init__(self, message):
super().__init__(message, 404)


class CredentialsError(SecurityError):
"""
Raised when credentials are invalid.
"""

def __init__(self, message, code=400):
super().__init__(message, code)


class AccountError(SecurityError):
"""
Base account error that all other account errors derive from.
Expand Down Expand Up @@ -123,15 +132,6 @@ def __init__(self, message: str = "Session is deactivated.", code: int = 401):
super().__init__(message, code)


class UnfamiliarLocationError(SessionError):
"""
Raised when session is accessed from an unfamiliar ip address
"""

def __init__(self):
super().__init__("Session accessed from an unfamiliar location.")


class ExpiredError(SessionError):
"""
Raised when session has expired.
Expand Down Expand Up @@ -202,12 +202,3 @@ class AnonymousError(AuthorizationError):

def __init__(self):
super().__init__("Session is anonymous.")


class CredentialsError(SecurityError):
"""
Raised when credentials are invalid.
"""

def __init__(self, message, code=400):
super().__init__(message, code)
Empty file removed sanic_security/middleware.py
Empty file.
Loading

0 comments on commit a17ffb0

Please sign in to comment.