Start implementation of OAuth client
This commit is contained in:
parent
faf697d3cf
commit
e2aa645bbf
|
@ -169,6 +169,21 @@ AUTH_USER_MODEL = 'users.User'
|
|||
|
||||
MAX_EMPRUNT = 5 # Max emprunts
|
||||
|
||||
# AUTHLIB CLIENTS
|
||||
AUTHLIB_OAUTH_CLIENTS = {
|
||||
'notekfet': {
|
||||
'client_id': 'qtElmOUj67YNvSZjA5l70ITUMxd3NJ9kksBsK5e9',
|
||||
'client_secret': 'SwF909sLIeU5GhruXsFzKfdBhFNgs8nvkVpFKgP4pIQ80BmLLlf3ZkMoNL7Cpox6Ke3MXNWGswTtbKkM8AiB9v6pys8PNfYH0MDFWAi3tnffjwaMQBzRFhjx20qb6S4W',
|
||||
'access_token_url': 'https://note-dev.crans.org/o/token/',
|
||||
'refresh_token_url': 'https://note-dev.crans.org/o/token/',
|
||||
'authorize_url': 'https://note-dev.crans.org/o/authorize/',
|
||||
'userinfo_endpoint': 'https://note-dev.crans.org/api/me/',
|
||||
'client_kwargs': {
|
||||
'scope': '1_1 2_1 48_1',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
from .settings_local import *
|
||||
except ImportError:
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
authlib~=0.15
|
||||
docutils~=0.16 # for Django-admin docs
|
||||
Django~=2.2
|
||||
django-filter~=2.4
|
||||
|
|
|
@ -54,7 +54,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||
{% if user.is_authenticated %}
|
||||
<a href="{% url 'logout' %}">{% trans 'Log out' %}</a>
|
||||
{% else %}
|
||||
<a href="{% url 'login' %}">{% trans 'Log in' %}</a>
|
||||
<a href="{% url 'users:login' %}">{% trans 'Log in' %}</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# Generated by Django 2.2.24 on 2021-11-02 15:11
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0042_delete_adhesion'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AccessToken',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('access_token', models.CharField(max_length=32, verbose_name='access token')),
|
||||
('expires_in', models.PositiveSmallIntegerField(verbose_name='expires in')),
|
||||
('scopes', models.CharField(max_length=255, verbose_name='scopes')),
|
||||
('refresh_token', models.CharField(max_length=32, verbose_name='refresh token')),
|
||||
('expires_at', models.DateTimeField(verbose_name='expires at')),
|
||||
('owner', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='owner')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'access token',
|
||||
'verbose_name_plural': 'access tokens',
|
||||
},
|
||||
),
|
||||
]
|
|
@ -2,6 +2,10 @@
|
|||
# Copyright (C) 2017-2019 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from authlib.integrations.django_client import OAuth
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
@ -44,3 +48,61 @@ class User(AbstractUser):
|
|||
def is_member(self):
|
||||
# FIXME Use NK20
|
||||
return True
|
||||
|
||||
|
||||
class AccessToken(models.Model):
|
||||
owner = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
default=None,
|
||||
verbose_name=_('owner'),
|
||||
)
|
||||
|
||||
access_token = models.CharField(
|
||||
max_length=32,
|
||||
verbose_name=_('access token'),
|
||||
)
|
||||
|
||||
expires_in = models.PositiveSmallIntegerField(
|
||||
verbose_name=_('expires in'),
|
||||
)
|
||||
|
||||
scopes = models.CharField(
|
||||
max_length=255,
|
||||
verbose_name=_('scopes'),
|
||||
)
|
||||
|
||||
refresh_token = models.CharField(
|
||||
max_length=32,
|
||||
verbose_name=_('refresh token'),
|
||||
)
|
||||
|
||||
expires_at = models.DateTimeField(
|
||||
verbose_name=_('expires at'),
|
||||
)
|
||||
|
||||
def refresh(self):
|
||||
"""
|
||||
Refresh the access token.
|
||||
"""
|
||||
oauth = OAuth()
|
||||
oauth.register('notekfet')
|
||||
# Get the OAuth client
|
||||
oauth_client = oauth.notekfet._get_oauth_client()
|
||||
# Actually refresh the token
|
||||
token = oauth_client.refresh_token(oauth.notekfet.access_token_url,
|
||||
refresh_token=self.refresh_token)
|
||||
self.access_token = token['access_token']
|
||||
self.expires_in = token['expires_in']
|
||||
self.scopes = token['scope']
|
||||
self.refresh_token = token['refresh_token']
|
||||
self.expires_at = timezone.utc.fromutc(
|
||||
datetime.fromtimestamp(token['expires_at'])
|
||||
)
|
||||
|
||||
self.save()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('access token')
|
||||
verbose_name_plural = _('access tokens')
|
||||
|
|
|
@ -8,5 +8,6 @@ from . import views
|
|||
|
||||
app_name = 'users'
|
||||
urlpatterns = [
|
||||
url(r'^edit_info/$', views.edit_info, name='edit-info'),
|
||||
url('login/', views.LoginView.as_view(), name='login'),
|
||||
url('authorize/', views.AuthorizeView.as_view(), name='auth'),
|
||||
]
|
||||
|
|
|
@ -1,47 +1,42 @@
|
|||
# -*- mode: python; coding: utf-8 -*-
|
||||
# Copyright (C) 2017-2019 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
from datetime import datetime
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from authlib.integrations.django_client import OAuth
|
||||
from django.contrib.auth.models import Group
|
||||
from django.db import transaction
|
||||
from django.shortcuts import redirect, render
|
||||
from django.template.context_processors import csrf
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.views.generic import RedirectView
|
||||
from rest_framework import viewsets
|
||||
from reversion import revisions as reversion
|
||||
from users.forms import BaseInfoForm
|
||||
from users.models import User
|
||||
from users.models import User, AccessToken
|
||||
|
||||
from .serializers import GroupSerializer, UserSerializer
|
||||
|
||||
|
||||
def form(ctx, template, request):
|
||||
c = ctx
|
||||
c.update(csrf(request))
|
||||
return render(request, template, c)
|
||||
class LoginView(RedirectView):
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
oauth = OAuth()
|
||||
oauth.register('notekfet')
|
||||
redirect_url = self.request.build_absolute_uri(reverse('users:auth'))
|
||||
return oauth.notekfet.authorize_redirect(self.request, redirect_url).url
|
||||
|
||||
|
||||
@login_required
|
||||
def edit_info(request):
|
||||
"""
|
||||
Edite son utilisateur
|
||||
"""
|
||||
user = BaseInfoForm(request.POST or None, instance=request.user)
|
||||
if user.is_valid():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
user.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(
|
||||
field for field in user.changed_data))
|
||||
messages.success(request, "L'user a bien été modifié")
|
||||
return redirect("index")
|
||||
return form({
|
||||
'form': user,
|
||||
'password_change': True,
|
||||
'title': _('Edit user profile'),
|
||||
}, 'users/user.html', request)
|
||||
class AuthorizeView(RedirectView):
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
oauth = OAuth()
|
||||
oauth.register('notekfet')
|
||||
token = oauth.notekfet.authorize_access_token(self.request)
|
||||
token_obj = AccessToken.objects.create(
|
||||
access_token=token['access_token'],
|
||||
expires_in=token['expires_in'],
|
||||
scopes=token['scope'],
|
||||
refresh_token=token['refresh_token'],
|
||||
expires_at=timezone.utc.fromutc(
|
||||
datetime.fromtimestamp(token['expires_at'])),
|
||||
)
|
||||
# TODO Log in or create user
|
||||
return '/'
|
||||
|
||||
|
||||
class UserViewSet(viewsets.ModelViewSet):
|
||||
|
|
Loading…
Reference in New Issue