Skip to content

Commit

Permalink
feat: set staff and superuser from roles (#18)
Browse files Browse the repository at this point in the history
Update is_staff and is_superuser based based on roles
  • Loading branch information
omercnet authored Oct 20, 2022
1 parent b639a27 commit da4dfca
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 52 deletions.
25 changes: 15 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ This plugin integrates Descope with your Django app.

## Quick start

1. Sign up for Descope and set admin roles

- Get your project id
- Create two roles in Descope, that will be mapped to Django permissions
- is_staff
- is_superuser

Map these roles to any user you would like to make a staff or superuser in your Django app.
_The names of these roles can be customized in the settings below._

1. Add "django_descope" to your INSTALLED_APPS setting like this:

```
Expand All @@ -28,12 +38,12 @@ This plugin integrates Descope with your Django app.
1. Include descope URLconf in your project urls.py like this:

```
path('descope/', include('django_descope.urls')),
path('auth/', include('django_descope.urls')),
```

1. Start the development server and visit http://127.0.0.1:8000/descope/signup
1. Start the development server and visit http://127.0.0.1:8000/auth/signup

1. Visit http://127.0.0.1:8000/tokens to see the user tokens after login
1. Visit http://127.0.0.1:8000/auth/tokens to see the user tokens after login

## Settings

Expand All @@ -48,11 +58,6 @@ DESCOPE_LOGIN_TEMPLATE_NAME
DESCOPE_LOGIN_SENT_TEMPLATE_NAME
DESCOPE_LOGIN_FAILED_TEMPLATE_NAME
DESCOPE_SIGNUP_TEMPLATE_NAME
DESCOPE_IS_STAFF_ROLE
DESCOPE_IS_SUPERUSER_ROLE
```

### TODO:

- [ ] Get user details (name?) from jwt
- [ ] Get user permissions from claims
- [ ] Add additional authentication methods
- [ ] Use descope web sdk for templates
18 changes: 14 additions & 4 deletions django_descope/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,31 @@ def middleware(request: HttpRequest) -> HttpResponse:
Returns:
HttpResponse: Django HTTP Response
"""
refresh_token: dict = request.session.get("descopeRefresh")
session_token: dict = request.session.get("descopeSession")
r: dict = request.session.get("descopeRefresh")
s: dict = request.session.get("descopeSession")

if not (refresh_token and session_token):
if not (r and s):
logout(request)
return get_response(request)
try:
jwt_response: dict = descope_client.validate_session_request(
session_token.get("jwt"), refresh_token.get("jwt")
s.get("jwt"), r.get("jwt")
)
request.session["descopeSession"] = jwt_response[SESSION_TOKEN_NAME]
except AuthException as e:
logger.error(e)
logout(request)

# Update roles
is_staff = settings.IS_STAFF_ROLE in s["roles"]
is_superuser = settings.IS_SUPERUSER_ROLE in s["roles"]
if request.user.is_staff != is_staff:
request.user.is_staff = is_staff
request.user.save(update_fields=["is_staff"])
if request.user.is_superuser != is_superuser:
request.user.is_superuser = is_superuser
request.user.save(update_fields=["is_superuser"])

return get_response(request)

return middleware
4 changes: 4 additions & 0 deletions django_descope/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@
REQUIRE_SIGNUP = getattr(settings, "DESCOPE_REQUIRE_SIGNUP", True)
if not isinstance(REQUIRE_SIGNUP, bool):
raise ImproperlyConfigured('"DESCOPE_REQUIRE_SIGNUP" must be a boolean')

# Role names to create in Descope that will map to User attributes
IS_STAFF_ROLE = getattr(settings, "DESCOPE_IS_STAFF_ROLE", "is_staff")
IS_SUPERUSER_ROLE = getattr(settings, "DESCOPE_IS_SUPERUSER_ROLE", "is_superuser")
30 changes: 18 additions & 12 deletions django_descope/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ def post(self, request: HttpRequest, *args, **kwargs):
return self.render_to_response(context)

email = form.cleaned_data["email"]
if not settings.REQUIRE_SIGNUP:
User.objects.get_or_create(username=email, email=email)

try:
descope_client.magiclink.sign_in(
DeliveryMethod.EMAIL,
Expand Down Expand Up @@ -82,12 +79,21 @@ def get(self, request, *args, **kwargs):
return self.render_to_response(context)

logger.info("Login successful", jwt_response)
request.session["descopeSession"] = jwt_response[SESSION_TOKEN_NAME]
request.session["descopeUser"] = u = jwt_response["user"]
request.session["descopeSession"] = s = jwt_response[SESSION_TOKEN_NAME]
request.session["descopeRefresh"] = jwt_response[REFRESH_SESSION_TOKEN_NAME]
user = User.objects.get(email=jwt_response["user"]["email"])

user, created = User.objects.get_or_create(
username=u["userId"],
email=u["email"],
is_staff=("is_staff" in s["roles"]),
first_name=u["name"].split()[0],
last_name=" ".join(u["name"].split()[0:]),
)

login(request, user)

return HttpResponseRedirect(settings.LOGIN_SUCCESS_REDIRECT)
return HttpResponseRedirect(reverse(settings.LOGIN_SUCCESS_REDIRECT))


@method_decorator(csrf_protect, name="dispatch")
Expand All @@ -110,11 +116,6 @@ def post(self, request, *args, **kwargs):
return self.render_to_response(context)

email = form.cleaned_data["email"]

user, created = User.objects.get_or_create(
email=email,
username=email,
)
try:
descope_client.magiclink.sign_up_or_in(
DeliveryMethod.EMAIL,
Expand Down Expand Up @@ -145,8 +146,13 @@ class ShowTokens(View):
def get(self, request: HttpRequest, *args, **kwargs):
return JsonResponse(
{
"user": str(request.user),
"user": {
"user": str(request.user),
"is_staff": request.user.is_staff,
"is_superuser": request.user.is_superuser,
},
"session": request.session.get("descopeSession"),
"refresh": request.session.get("descopeRefresh"),
"login": request.session.get("descopeUser"),
}
)
27 changes: 14 additions & 13 deletions example/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion example/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
ALLOWED_HOSTS = ["*"]


DESCOPE_PROJECT_ID = "P2CqCdq2bnO9JS2awFKlIPngwPUK" # <-- Set this to your project ID
DESCOPE_PROJECT_ID = "P2GMsgxPSSQrq3Ig7M0ExAwoRGbP" # <-- Set this to your project ID


# Application definition
Expand Down
16 changes: 8 additions & 8 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ classifiers = [

[tool.poetry.dependencies]
python = ">=3.7,<4.0"
descope = "0.1.0"
Django = ">=3.2,<5"
descope = "0.2.0"

[tool.poetry.group.dev.dependencies]
flake8 = "5.0.4"
Expand Down
6 changes: 3 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@ cryptography==38.0.1 ; python_version >= "3.7" and python_version < "4.0" \
--hash=sha256:d3971e2749a723e9084dd507584e2a2761f78ad2c638aa31e80bc7a15c9db4f9 \
--hash=sha256:d4ef6cc305394ed669d4d9eebf10d3a101059bdcf2669c366ec1d14e4fb227bd \
--hash=sha256:d9e69ae01f99abe6ad646947bba8941e896cb3aa805be2597a0400e0764b5818
descope==0.1.0 ; python_version >= "3.7" and python_version < "4.0" \
--hash=sha256:b00b2d108c0b90725a41752157083ff76b74ccbe7f2c27e3758d0219a32a6d0f \
--hash=sha256:d20be12b9ee6aa8ac0a1d01c61b32903350c513f903e616591597a3aa0127fae
descope==0.2.0 ; python_version >= "3.7" and python_version < "4.0" \
--hash=sha256:0e4980f9ee73d50771b11dd03661a5ecba3fd253002950fed340670e8f2be712 \
--hash=sha256:b8230a8a284582d72183d650274aa02e8116ba12008aee365b464b8a23b2c287
django==3.2.16 ; python_version >= "3.7" and python_version < "4.0" \
--hash=sha256:18ba8efa36b69cfcd4b670d0fa187c6fe7506596f0ababe580e16909bcdec121 \
--hash=sha256:3adc285124244724a394fa9b9839cc8cd116faf7d159554c43ecdaa8cdf0b94d
Expand Down

0 comments on commit da4dfca

Please sign in to comment.