Skip to content

Commit

Permalink
Update flask version and expose new user methods (#19)
Browse files Browse the repository at this point in the history
* Add new methods to the LoggedInUser delegate

* Switch to g so we support flask 3
  • Loading branch information
andrew-propelauth authored Apr 1, 2024
1 parent b8bba0f commit cd9c323
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 47 deletions.
10 changes: 3 additions & 7 deletions propelauth_flask/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from collections import namedtuple

from flask import _request_ctx_stack
from flask import g
from propelauth_py import TokenVerificationMetadata, init_base_auth
from werkzeug.local import LocalProxy

Expand All @@ -14,14 +14,10 @@
)

"""Returns the current user. Must be used with one of require_user, optional_user, or require_org_member"""
current_user = LocalProxy(
lambda: getattr(_request_ctx_stack.top, "propelauth_current_user", None)
)
current_user = LocalProxy(lambda: g.propelauth_current_user)

"""Returns the current org. Must be used with require_org_member"""
current_org = LocalProxy(
lambda: getattr(_request_ctx_stack.top, "propelauth_current_org", None)
)
current_org = LocalProxy(lambda: g.propelauth_current_org)

Auth = namedtuple(
"Auth",
Expand Down
96 changes: 61 additions & 35 deletions propelauth_flask/auth_decorator.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
import functools

from flask import _request_ctx_stack, request, abort, Response
from flask import g, request, abort, Response
from propelauth_py import UnauthorizedException
from propelauth_py.errors import ForbiddenException

from propelauth_flask.user import LoggedOutUser, LoggedInUser


def _get_user_credential_decorator(validate_access_token_and_get_user, require_user, debug_mode):
def _get_user_credential_decorator(
validate_access_token_and_get_user, require_user, debug_mode
):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
authorization_header = request.headers.get("Authorization")
user = validate_access_token_and_get_user(authorization_header)

_request_ctx_stack.top.propelauth_current_user = LoggedInUser(user)
g.propelauth_current_user = LoggedInUser(user)

except UnauthorizedException as e:
_request_ctx_stack.top.propelauth_current_user = LoggedOutUser()
g.propelauth_current_user = LoggedOutUser()
_return_401_if_user_required(e, require_user, debug_mode)

return func(*args, **kwargs)
Expand All @@ -36,10 +38,12 @@ def wrapper(*args, **kwargs):
try:
authorization_header = request.headers.get("Authorization")
required_org_id = req_to_org_id(request)
user_and_org = validate_access_token_and_get_user_with_org(authorization_header, required_org_id)
user_and_org = validate_access_token_and_get_user_with_org(
authorization_header, required_org_id
)

_request_ctx_stack.top.propelauth_current_user = user_and_org.user
_request_ctx_stack.top.propelauth_current_org = user_and_org.org_member_info
g.propelauth_current_user = user_and_org.user
g.propelauth_current_org = user_and_org.org_member_info

except UnauthorizedException as e:
_return_401_if_user_required(e, True, debug_mode)
Expand All @@ -56,20 +60,26 @@ def wrapper(*args, **kwargs):
return decorator_that_takes_arguments


def _require_org_member_with_minimum_role_decorator(validate_access_token_and_get_user_with_org_by_minimum_role,
debug_mode):
def decorator_that_takes_arguments(minimum_required_role, req_to_org_id=_default_req_to_org_id):
def _require_org_member_with_minimum_role_decorator(
validate_access_token_and_get_user_with_org_by_minimum_role, debug_mode
):
def decorator_that_takes_arguments(
minimum_required_role, req_to_org_id=_default_req_to_org_id
):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
authorization_header = request.headers.get("Authorization")
required_org_id = req_to_org_id(request)
user_and_org = validate_access_token_and_get_user_with_org_by_minimum_role(
authorization_header, required_org_id, minimum_required_role)
user_and_org = (
validate_access_token_and_get_user_with_org_by_minimum_role(
authorization_header, required_org_id, minimum_required_role
)
)

_request_ctx_stack.top.propelauth_current_user = user_and_org.user
_request_ctx_stack.top.propelauth_current_org = user_and_org.org_member_info
g.propelauth_current_user = user_and_org.user
g.propelauth_current_org = user_and_org.org_member_info

except UnauthorizedException as e:
_return_401_if_user_required(e, True, debug_mode)
Expand All @@ -86,20 +96,24 @@ def wrapper(*args, **kwargs):
return decorator_that_takes_arguments


def _require_org_member_with_exact_role_decorator(validate_access_token_and_get_user_with_org_by_exact_role,
debug_mode):
def _require_org_member_with_exact_role_decorator(
validate_access_token_and_get_user_with_org_by_exact_role, debug_mode
):
def decorator_that_takes_arguments(role, req_to_org_id=_default_req_to_org_id):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
authorization_header = request.headers.get("Authorization")
required_org_id = req_to_org_id(request)
user_and_org = validate_access_token_and_get_user_with_org_by_exact_role(
authorization_header, required_org_id, role)
user_and_org = (
validate_access_token_and_get_user_with_org_by_exact_role(
authorization_header, required_org_id, role
)
)

_request_ctx_stack.top.propelauth_current_user = user_and_org.user
_request_ctx_stack.top.propelauth_current_org = user_and_org.org_member_info
g.propelauth_current_user = user_and_org.user
g.propelauth_current_org = user_and_org.org_member_info

except UnauthorizedException as e:
_return_401_if_user_required(e, True, debug_mode)
Expand All @@ -116,20 +130,26 @@ def wrapper(*args, **kwargs):
return decorator_that_takes_arguments


def _require_org_member_with_permission_decorator(validate_access_token_and_get_user_with_org_by_permission,
debug_mode):
def decorator_that_takes_arguments(permission, req_to_org_id=_default_req_to_org_id):
def _require_org_member_with_permission_decorator(
validate_access_token_and_get_user_with_org_by_permission, debug_mode
):
def decorator_that_takes_arguments(
permission, req_to_org_id=_default_req_to_org_id
):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
authorization_header = request.headers.get("Authorization")
required_org_id = req_to_org_id(request)
user_and_org = validate_access_token_and_get_user_with_org_by_permission(
authorization_header, required_org_id, permission)
user_and_org = (
validate_access_token_and_get_user_with_org_by_permission(
authorization_header, required_org_id, permission
)
)

_request_ctx_stack.top.propelauth_current_user = user_and_org.user
_request_ctx_stack.top.propelauth_current_org = user_and_org.org_member_info
g.propelauth_current_user = user_and_org.user
g.propelauth_current_org = user_and_org.org_member_info

except UnauthorizedException as e:
_return_401_if_user_required(e, True, debug_mode)
Expand All @@ -146,20 +166,26 @@ def wrapper(*args, **kwargs):
return decorator_that_takes_arguments


def _require_org_member_with_all_permissions_decorator(validate_access_token_and_get_user_with_org_by_all_permissions,
debug_mode):
def decorator_that_takes_arguments(permissions, req_to_org_id=_default_req_to_org_id):
def _require_org_member_with_all_permissions_decorator(
validate_access_token_and_get_user_with_org_by_all_permissions, debug_mode
):
def decorator_that_takes_arguments(
permissions, req_to_org_id=_default_req_to_org_id
):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
authorization_header = request.headers.get("Authorization")
required_org_id = req_to_org_id(request)
user_and_org = validate_access_token_and_get_user_with_org_by_all_permissions(
authorization_header, required_org_id, permissions)
user_and_org = (
validate_access_token_and_get_user_with_org_by_all_permissions(
authorization_header, required_org_id, permissions
)
)

_request_ctx_stack.top.propelauth_current_user = user_and_org.user
_request_ctx_stack.top.propelauth_current_org = user_and_org.org_member_info
g.propelauth_current_user = user_and_org.user
g.propelauth_current_org = user_and_org.org_member_info

except UnauthorizedException as e:
_return_401_if_user_required(e, True, debug_mode)
Expand Down Expand Up @@ -191,4 +217,4 @@ def _return_exception(e, status, debug_mode):


def _default_req_to_org_id(req):
return req.view_args.get('org_id')
return req.view_args.get("org_id")
50 changes: 48 additions & 2 deletions propelauth_flask/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,63 @@

class LoggedInUser:
def __init__(self, user: User):
self.user = user

# We only need the user, but these are here for backwards compatibility
self.user_id = user.user_id
self.org_id_to_org_member_info = user.org_id_to_org_member_info
self.legacy_user_id = user.legacy_user_id

def exists(self):
return True

def is_impersonated(self):
"""Returns true if the user is impersonated"""
return self.user.is_impersonated()

def get_active_org(self):
"""Returns the active org member info, if the user has an active org."""
return self.user.get_active_org()

def get_active_org_id(self):
"""Returns the active org id, if the user has an active org."""
return self.user.get_active_org_id()

def get_org(self, org_id):
"""Returns the org member info for the org_id, if the user is in the org."""
return self.user.get_org(org_id)

def get_org_by_name(self, org_name):
"""Returns the org member info for the org_name, if the user is in the org."""
return self.user.get_org_by_name(org_name)

def get_user_property(self, property_name):
"""Returns the user property value, if it exists."""
return self.user.get_user_property(property_name)

def get_orgs(self):
"""Returns the orgs the user is in."""
return self.user.get_orgs()

def is_role_in_org(self, org_id, role):
"""Returns true if the user is the role in the org."""
return self.user.is_role_in_org(org_id, role)

def is_at_least_role_in_org(self, org_id, role):
"""Returns true if the user is at least the role in the org."""
return self.user.is_at_least_role_in_org(org_id, role)

def has_permission_in_org(self, org_id, permission):
"""Returns true if the user has the permission in the org."""
return self.user.has_permission_in_org(org_id, permission)

def has_all_permissions_in_org(self, org_id, permissions):
"""Returns true if the user has all the permissions in the org."""
return self.user.has_all_permissions_in_org(org_id, permissions)

def __eq__(self, other):
if isinstance(other, LoggedInUser):
return self.user_id == other.user_id and self.org_id_to_org_member_info == other.org_id_to_org_member_info \
and self.legacy_user_id == other.legacy_user_id
return self.user == other.user
return False


Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
flask<3
flask<4
propelauth-py
pytest
requests-mock
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

setup(
name="propelauth-flask",
version="2.1.11",
version="2.1.12",
description="A library for managing authentication in Flask",
long_description=README,
long_description_content_type="text/markdown",
Expand All @@ -20,7 +20,7 @@
author="PropelAuth",
author_email="[email protected]",
license="MIT",
install_requires=["flask<3", "propelauth-py==3.1.12", "requests"],
install_requires=["flask<4", "propelauth-py==3.1.12", "requests"],
setup_requires=pytest_runner,
tests_require=["pytest==4.4.1"],
test_suite="tests",
Expand Down

0 comments on commit cd9c323

Please sign in to comment.