forked from 00dani/lemoncurry
Run Black over the whole codebase
This commit is contained in:
parent
cd990e4e2f
commit
2e7d12b3e6
109 changed files with 1539 additions and 1209 deletions
|
@ -7,25 +7,36 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
] # type: List[Tuple[str, str]]
|
||||
dependencies = [] # type: List[Tuple[str, str]]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='IndieAuthCode',
|
||||
name="IndieAuthCode",
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True,
|
||||
primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('code', models.CharField(max_length=64, unique=True)),
|
||||
('me', models.CharField(max_length=255)),
|
||||
('client_id', models.CharField(max_length=255)),
|
||||
('redirect_uri', models.CharField(max_length=255)),
|
||||
('response_type', models.CharField(choices=[
|
||||
('id', 'id'), ('code', 'code')], default='id', max_length=4)),
|
||||
('scope', models.CharField(blank=True, max_length=200)),
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("code", models.CharField(max_length=64, unique=True)),
|
||||
("me", models.CharField(max_length=255)),
|
||||
("client_id", models.CharField(max_length=255)),
|
||||
("redirect_uri", models.CharField(max_length=255)),
|
||||
(
|
||||
"response_type",
|
||||
models.CharField(
|
||||
choices=[("id", "id"), ("code", "code")],
|
||||
default="id",
|
||||
max_length=4,
|
||||
),
|
||||
),
|
||||
("scope", models.CharField(blank=True, max_length=200)),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
|
@ -6,13 +6,12 @@ from django.db import migrations
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('lemonauth', '0001_initial'),
|
||||
("lemonauth", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='IndieAuthCode',
|
||||
name="IndieAuthCode",
|
||||
),
|
||||
]
|
||||
|
|
|
@ -9,43 +9,112 @@ import randomslugfield.fields
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('lemonauth', '0002_delete_indieauthcode'),
|
||||
("lemonauth", "0002_delete_indieauthcode"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='IndieAuthCode',
|
||||
name="IndieAuthCode",
|
||||
fields=[
|
||||
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||
('id', randomslugfield.fields.RandomSlugField(blank=True, editable=False, length=30, max_length=30, primary_key=True, serialize=False, unique=True)),
|
||||
('client_id', models.URLField()),
|
||||
('scope', models.TextField(blank=True)),
|
||||
('redirect_uri', models.URLField()),
|
||||
('response_type', model_utils.fields.StatusField(choices=[('id', 'id'), ('code', 'code')], default='id', max_length=100, no_check_for_status=True)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
(
|
||||
"created",
|
||||
model_utils.fields.AutoCreatedField(
|
||||
default=django.utils.timezone.now,
|
||||
editable=False,
|
||||
verbose_name="created",
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
model_utils.fields.AutoLastModifiedField(
|
||||
default=django.utils.timezone.now,
|
||||
editable=False,
|
||||
verbose_name="modified",
|
||||
),
|
||||
),
|
||||
(
|
||||
"id",
|
||||
randomslugfield.fields.RandomSlugField(
|
||||
blank=True,
|
||||
editable=False,
|
||||
length=30,
|
||||
max_length=30,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
unique=True,
|
||||
),
|
||||
),
|
||||
("client_id", models.URLField()),
|
||||
("scope", models.TextField(blank=True)),
|
||||
("redirect_uri", models.URLField()),
|
||||
(
|
||||
"response_type",
|
||||
model_utils.fields.StatusField(
|
||||
choices=[("id", "id"), ("code", "code")],
|
||||
default="id",
|
||||
max_length=100,
|
||||
no_check_for_status=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Token',
|
||||
name="Token",
|
||||
fields=[
|
||||
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
|
||||
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
|
||||
('id', randomslugfield.fields.RandomSlugField(blank=True, editable=False, length=30, max_length=30, primary_key=True, serialize=False, unique=True)),
|
||||
('client_id', models.URLField()),
|
||||
('scope', models.TextField(blank=True)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
(
|
||||
"created",
|
||||
model_utils.fields.AutoCreatedField(
|
||||
default=django.utils.timezone.now,
|
||||
editable=False,
|
||||
verbose_name="created",
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
model_utils.fields.AutoLastModifiedField(
|
||||
default=django.utils.timezone.now,
|
||||
editable=False,
|
||||
verbose_name="modified",
|
||||
),
|
||||
),
|
||||
(
|
||||
"id",
|
||||
randomslugfield.fields.RandomSlugField(
|
||||
blank=True,
|
||||
editable=False,
|
||||
length=30,
|
||||
max_length=30,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
unique=True,
|
||||
),
|
||||
),
|
||||
("client_id", models.URLField()),
|
||||
("scope", models.TextField(blank=True)),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -17,6 +17,7 @@ class AuthSecret(TimeStampedModel):
|
|||
authorisation codes and tokens in IndieAuth - the two contain many
|
||||
identical fields, but just a few differences.
|
||||
"""
|
||||
|
||||
id = RandomSlugField(primary_key=True, length=30)
|
||||
client_id = models.URLField()
|
||||
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
|
||||
|
@ -27,7 +28,7 @@ class AuthSecret(TimeStampedModel):
|
|||
return self.user.full_url
|
||||
|
||||
def __contains__(self, scope):
|
||||
return scope in self.scope.split(' ')
|
||||
return scope in self.scope.split(" ")
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
@ -41,10 +42,11 @@ class IndieAuthCode(AuthSecret):
|
|||
Codes are single-use, and if unused will be expired automatically after
|
||||
thirty seconds.
|
||||
"""
|
||||
|
||||
redirect_uri = models.URLField()
|
||||
|
||||
RESPONSE_TYPE = Choices('id', 'code')
|
||||
response_type = StatusField(choices_name='RESPONSE_TYPE')
|
||||
RESPONSE_TYPE = Choices("id", "code")
|
||||
response_type = StatusField(choices_name="RESPONSE_TYPE")
|
||||
|
||||
@property
|
||||
def expired(self):
|
||||
|
@ -56,4 +58,5 @@ class Token(AuthSecret):
|
|||
A Token grants a client long-term authorisation - it will not expire unless
|
||||
explicitly revoked by the user.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
|
|
@ -3,17 +3,17 @@ from .models import IndieAuthCode, Token
|
|||
|
||||
|
||||
def auth(request) -> Token:
|
||||
if 'HTTP_AUTHORIZATION' in request.META:
|
||||
auth = request.META.get('HTTP_AUTHORIZATION').split(' ')
|
||||
if auth[0] != 'Bearer':
|
||||
raise error.bad_req('auth type {0} not supported'.format(auth[0]))
|
||||
if "HTTP_AUTHORIZATION" in request.META:
|
||||
auth = request.META.get("HTTP_AUTHORIZATION").split(" ")
|
||||
if auth[0] != "Bearer":
|
||||
raise error.bad_req("auth type {0} not supported".format(auth[0]))
|
||||
if len(auth) != 2:
|
||||
raise error.bad_req('invalid Bearer auth format, must be Bearer <token>')
|
||||
raise error.bad_req("invalid Bearer auth format, must be Bearer <token>")
|
||||
token = auth[1]
|
||||
elif 'access_token' in request.POST:
|
||||
token = request.POST.get('access_token')
|
||||
elif 'access_token' in request.GET:
|
||||
token = request.GET.get('access_token')
|
||||
elif "access_token" in request.POST:
|
||||
token = request.POST.get("access_token")
|
||||
elif "access_token" in request.GET:
|
||||
token = request.GET.get("access_token")
|
||||
else:
|
||||
raise error.unauthorized()
|
||||
|
||||
|
@ -28,11 +28,11 @@ def auth(request) -> Token:
|
|||
def gen_auth_code(req):
|
||||
code = IndieAuthCode()
|
||||
code.user = req.user
|
||||
code.client_id = req.POST['client_id']
|
||||
code.redirect_uri = req.POST['redirect_uri']
|
||||
code.response_type = req.POST.get('response_type', 'id')
|
||||
if 'scope' in req.POST:
|
||||
code.scope = ' '.join(req.POST.getlist('scope'))
|
||||
code.client_id = req.POST["client_id"]
|
||||
code.redirect_uri = req.POST["redirect_uri"]
|
||||
code.response_type = req.POST.get("response_type", "id")
|
||||
if "scope" in req.POST:
|
||||
code.scope = " ".join(req.POST.getlist("scope"))
|
||||
code.save()
|
||||
return code.id
|
||||
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
app_name = 'lemonauth'
|
||||
app_name = "lemonauth"
|
||||
urlpatterns = [
|
||||
path('login', views.login, name='login'),
|
||||
path('logout', views.logout, name='logout'),
|
||||
path('indie', views.IndieView.as_view(), name='indie'),
|
||||
path('indie/approve', views.indie_approve, name='indie_approve'),
|
||||
path('token', views.TokenView.as_view(), name='token'),
|
||||
path('tokens', views.TokensListView.as_view(), name='tokens'),
|
||||
path('tokens/<path:client_id>', views.TokensRevokeView.as_view(), name='tokens_revoke'),
|
||||
path("login", views.login, name="login"),
|
||||
path("logout", views.logout, name="logout"),
|
||||
path("indie", views.IndieView.as_view(), name="indie"),
|
||||
path("indie/approve", views.indie_approve, name="indie_approve"),
|
||||
path("token", views.TokenView.as_view(), name="token"),
|
||||
path("tokens", views.TokensListView.as_view(), name="tokens"),
|
||||
path(
|
||||
"tokens/<path:client_id>",
|
||||
views.TokensRevokeView.as_view(),
|
||||
name="tokens_revoke",
|
||||
),
|
||||
]
|
||||
|
|
|
@ -12,120 +12,114 @@ from urllib.parse import urlencode, urljoin, urlunparse, urlparse
|
|||
from .. import tokens
|
||||
from ..models import IndieAuthCode
|
||||
|
||||
breadcrumbs.add('lemonauth:indie', parent='home:index')
|
||||
breadcrumbs.add("lemonauth:indie", parent="home:index")
|
||||
|
||||
|
||||
def canonical(url):
|
||||
if '//' not in url:
|
||||
url = '//' + url
|
||||
if "//" not in url:
|
||||
url = "//" + url
|
||||
(scheme, netloc, path, params, query, fragment) = urlparse(url)
|
||||
if not scheme or scheme == 'http':
|
||||
scheme = 'https'
|
||||
if not scheme or scheme == "http":
|
||||
scheme = "https"
|
||||
if not path:
|
||||
path = '/'
|
||||
path = "/"
|
||||
return urlunparse((scheme, netloc, path, params, query, fragment))
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name='dispatch')
|
||||
@method_decorator(csrf_exempt, name="dispatch")
|
||||
class IndieView(TemplateView):
|
||||
template_name = 'lemonauth/indie.html'
|
||||
required_params = ('client_id', 'redirect_uri')
|
||||
template_name = "lemonauth/indie.html"
|
||||
required_params = ("client_id", "redirect_uri")
|
||||
|
||||
@method_decorator(login_required)
|
||||
@method_decorator(render_to(template_name))
|
||||
def get(self, request):
|
||||
params = request.GET.dict()
|
||||
params.setdefault('response_type', 'id')
|
||||
params.setdefault("response_type", "id")
|
||||
|
||||
for param in self.required_params:
|
||||
if param not in params:
|
||||
return utils.bad_req(
|
||||
'parameter {0} is required'.format(param)
|
||||
)
|
||||
return utils.bad_req("parameter {0} is required".format(param))
|
||||
|
||||
me = request.user.full_url
|
||||
if 'me' in params:
|
||||
param_me = canonical(params['me'])
|
||||
if "me" in params:
|
||||
param_me = canonical(params["me"])
|
||||
if me != param_me:
|
||||
return utils.forbid(
|
||||
'you are logged in as {}, not as {}'.format(me, param_me)
|
||||
"you are logged in as {}, not as {}".format(me, param_me)
|
||||
)
|
||||
|
||||
redirect_uri = urljoin(params['client_id'], params['redirect_uri'])
|
||||
redirect_uri = urljoin(params["client_id"], params["redirect_uri"])
|
||||
|
||||
type = params['response_type']
|
||||
if type not in ('id', 'code'):
|
||||
return utils.bad_req(
|
||||
'unknown response_type: {0}'.format(type)
|
||||
)
|
||||
type = params["response_type"]
|
||||
if type not in ("id", "code"):
|
||||
return utils.bad_req("unknown response_type: {0}".format(type))
|
||||
|
||||
scopes = ()
|
||||
if type == 'code':
|
||||
if 'scope' not in params:
|
||||
return utils.bad_req(
|
||||
'scopes required for code type'
|
||||
)
|
||||
scopes = params['scope'].split(' ')
|
||||
if type == "code":
|
||||
if "scope" not in params:
|
||||
return utils.bad_req("scopes required for code type")
|
||||
scopes = params["scope"].split(" ")
|
||||
|
||||
client = requests.mf2(params['client_id'])
|
||||
rels = (client.to_dict()['rel-urls']
|
||||
.get(redirect_uri, {})
|
||||
.get('rels', ()))
|
||||
verified = 'redirect_uri' in rels
|
||||
client = requests.mf2(params["client_id"])
|
||||
rels = client.to_dict()["rel-urls"].get(redirect_uri, {}).get("rels", ())
|
||||
verified = "redirect_uri" in rels
|
||||
|
||||
try:
|
||||
app = client.to_dict(filter_by_type='h-x-app')[0]['properties']
|
||||
app = client.to_dict(filter_by_type="h-x-app")[0]["properties"]
|
||||
except IndexError:
|
||||
app = None
|
||||
|
||||
return {
|
||||
'app': app,
|
||||
'me': me,
|
||||
'redirect_uri': redirect_uri,
|
||||
'verified': verified,
|
||||
'params': params,
|
||||
'scopes': scopes,
|
||||
'title': 'indieauth from {client_id}'.format(**params),
|
||||
"app": app,
|
||||
"me": me,
|
||||
"redirect_uri": redirect_uri,
|
||||
"verified": verified,
|
||||
"params": params,
|
||||
"scopes": scopes,
|
||||
"title": "indieauth from {client_id}".format(**params),
|
||||
}
|
||||
|
||||
def post(self, request):
|
||||
post = request.POST.dict()
|
||||
try:
|
||||
code = IndieAuthCode.objects.get(pk=post.get('code'))
|
||||
code = IndieAuthCode.objects.get(pk=post.get("code"))
|
||||
except IndieAuthCode.DoesNotExist:
|
||||
# if anything at all goes wrong when decoding the auth code, bail
|
||||
# out immediately.
|
||||
return utils.forbid('invalid auth code')
|
||||
return utils.forbid("invalid auth code")
|
||||
code.delete()
|
||||
if code.expired:
|
||||
return utils.forbid('invalid auth code')
|
||||
return utils.forbid("invalid auth code")
|
||||
|
||||
if code.response_type != 'id':
|
||||
return utils.bad_req(
|
||||
'this endpoint only supports response_type=id'
|
||||
)
|
||||
if code.client_id != post.get('client_id'):
|
||||
return utils.forbid('client id did not match')
|
||||
if code.redirect_uri != post.get('redirect_uri'):
|
||||
return utils.forbid('redirect uri did not match')
|
||||
if code.response_type != "id":
|
||||
return utils.bad_req("this endpoint only supports response_type=id")
|
||||
if code.client_id != post.get("client_id"):
|
||||
return utils.forbid("client id did not match")
|
||||
if code.redirect_uri != post.get("redirect_uri"):
|
||||
return utils.forbid("redirect uri did not match")
|
||||
|
||||
# If we got here, it's valid! Yay!
|
||||
return utils.choose_type(request, {'me': code.me}, {
|
||||
'application/x-www-form-urlencoded': utils.form_encoded_response,
|
||||
'application/json': JsonResponse,
|
||||
})
|
||||
return utils.choose_type(
|
||||
request,
|
||||
{"me": code.me},
|
||||
{
|
||||
"application/x-www-form-urlencoded": utils.form_encoded_response,
|
||||
"application/json": JsonResponse,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
def approve(request):
|
||||
params = {
|
||||
'me': urljoin(utils.origin(request), request.user.url),
|
||||
'code': tokens.gen_auth_code(request),
|
||||
"me": urljoin(utils.origin(request), request.user.url),
|
||||
"code": tokens.gen_auth_code(request),
|
||||
}
|
||||
if 'state' in request.POST:
|
||||
params['state'] = request.POST['state']
|
||||
if "state" in request.POST:
|
||||
params["state"] = request.POST["state"]
|
||||
|
||||
uri = request.POST['redirect_uri']
|
||||
sep = '&' if '?' in uri else '?'
|
||||
uri = request.POST["redirect_uri"]
|
||||
sep = "&" if "?" in uri else "?"
|
||||
return redirect(uri + sep + urlencode(params))
|
||||
|
|
|
@ -2,11 +2,11 @@ import django.contrib.auth.views
|
|||
from otp_agents.forms import OTPAuthenticationForm
|
||||
from lemoncurry import breadcrumbs
|
||||
|
||||
breadcrumbs.add(route='lemonauth:login', label='log in', parent='home:index')
|
||||
breadcrumbs.add(route="lemonauth:login", label="log in", parent="home:index")
|
||||
|
||||
login = django.contrib.auth.views.LoginView.as_view(
|
||||
authentication_form=OTPAuthenticationForm,
|
||||
extra_context={'title': 'log in'},
|
||||
template_name='lemonauth/login.html',
|
||||
extra_context={"title": "log in"},
|
||||
template_name="lemonauth/login.html",
|
||||
redirect_authenticated_user=True,
|
||||
)
|
||||
|
|
|
@ -7,41 +7,42 @@ from ..models import IndieAuthCode
|
|||
from lemoncurry import utils
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name='dispatch')
|
||||
@method_decorator(csrf_exempt, name="dispatch")
|
||||
class TokenView(View):
|
||||
def get(self, req):
|
||||
token = tokens.auth(req)
|
||||
res = {
|
||||
'me': token.me,
|
||||
'client_id': token.client_id,
|
||||
'scope': token.scope,
|
||||
"me": token.me,
|
||||
"client_id": token.client_id,
|
||||
"scope": token.scope,
|
||||
}
|
||||
return utils.choose_type(req, res)
|
||||
|
||||
def post(self, req):
|
||||
post = req.POST
|
||||
try:
|
||||
code = IndieAuthCode.objects.get(pk=post.get('code'))
|
||||
code = IndieAuthCode.objects.get(pk=post.get("code"))
|
||||
except IndieAuthCode.DoesNotExist:
|
||||
return utils.forbid('invalid auth code')
|
||||
return utils.forbid("invalid auth code")
|
||||
code.delete()
|
||||
if code.expired:
|
||||
return utils.forbid('invalid auth code')
|
||||
return utils.forbid("invalid auth code")
|
||||
|
||||
if code.response_type != 'code':
|
||||
return utils.bad_req(
|
||||
'this endpoint only supports response_type=code'
|
||||
)
|
||||
if 'client_id' in post and code.client_id != post['client_id']:
|
||||
return utils.forbid('client id did not match')
|
||||
if code.redirect_uri != post.get('redirect_uri'):
|
||||
return utils.forbid('redirect uri did not match')
|
||||
if code.response_type != "code":
|
||||
return utils.bad_req("this endpoint only supports response_type=code")
|
||||
if "client_id" in post and code.client_id != post["client_id"]:
|
||||
return utils.forbid("client id did not match")
|
||||
if code.redirect_uri != post.get("redirect_uri"):
|
||||
return utils.forbid("redirect uri did not match")
|
||||
|
||||
if 'me' in post and code.me != post['me']:
|
||||
return utils.forbid('me did not match')
|
||||
if "me" in post and code.me != post["me"]:
|
||||
return utils.forbid("me did not match")
|
||||
|
||||
return utils.choose_type(req, {
|
||||
'access_token': tokens.gen_token(code),
|
||||
'me': code.me,
|
||||
'scope': code.scope,
|
||||
})
|
||||
return utils.choose_type(
|
||||
req,
|
||||
{
|
||||
"access_token": tokens.gen_token(code),
|
||||
"me": code.me,
|
||||
"scope": code.scope,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -20,15 +20,15 @@ class Client:
|
|||
self.id = client_id
|
||||
self.count = 0
|
||||
self.scopes = set()
|
||||
apps = mf2(self.id).to_dict(filter_by_type='h-x-app')
|
||||
apps = mf2(self.id).to_dict(filter_by_type="h-x-app")
|
||||
try:
|
||||
self.app = apps[0]['properties']
|
||||
self.app = apps[0]["properties"]
|
||||
except IndexError:
|
||||
self.app = None
|
||||
|
||||
|
||||
class TokensListView(LoginRequiredMixin, TemplateView):
|
||||
template_name = 'lemonauth/tokens.html'
|
||||
template_name = "lemonauth/tokens.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
@ -36,6 +36,6 @@ class TokensListView(LoginRequiredMixin, TemplateView):
|
|||
for token in self.request.user.token_set.all():
|
||||
client = clients[token.client_id]
|
||||
client.count += 1
|
||||
client.scopes |= set(token.scope.split(' '))
|
||||
context.update({'clients': clients, 'title': 'tokens'})
|
||||
client.scopes |= set(token.scope.split(" "))
|
||||
context.update({"clients": clients, "title": "tokens"})
|
||||
return context
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue