Dramatically improved processing of Micropub tokens which supports both the Authorization header and the access_token field approaches
This commit is contained in:
parent
e5f2e9d537
commit
b89405ed88
4 changed files with 71 additions and 27 deletions
|
@ -1,7 +1,61 @@
|
||||||
from jose import jwt
|
from jose import jwt
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.http import HttpResponse
|
||||||
|
|
||||||
|
|
||||||
|
def auth(request):
|
||||||
|
if 'HTTP_AUTHORIZATION' in request.META:
|
||||||
|
auth = request.META.get('HTTP_AUTHORIZATION').split(' ')
|
||||||
|
if auth[0] != 'Bearer':
|
||||||
|
return HttpResponse(
|
||||||
|
'authorisation with {0} not supported'.format(auth[0]),
|
||||||
|
content_type='text/plain',
|
||||||
|
status=400
|
||||||
|
)
|
||||||
|
if len(auth) != 2:
|
||||||
|
return HttpResponse(
|
||||||
|
'invalid Bearer auth format, must be Bearer <token>',
|
||||||
|
content_type='text/plain',
|
||||||
|
status=400
|
||||||
|
)
|
||||||
|
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')
|
||||||
|
else:
|
||||||
|
return HttpResponse(
|
||||||
|
'authorisation required',
|
||||||
|
content_type='text/plain',
|
||||||
|
status=401
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
token = decode(token)
|
||||||
|
except Exception as e:
|
||||||
|
return HttpResponse(
|
||||||
|
'invalid micropub token',
|
||||||
|
content_type='text/plain',
|
||||||
|
status=403,
|
||||||
|
)
|
||||||
|
|
||||||
|
return MicropubToken(token)
|
||||||
|
|
||||||
|
|
||||||
|
class MicropubToken:
|
||||||
|
def __init__(self, tok):
|
||||||
|
self.user = get_user_model().objects.get(pk=tok['uid'])
|
||||||
|
self.client = tok['cid']
|
||||||
|
self.scope = tok['sco']
|
||||||
|
|
||||||
|
self.me = self.user.full_url
|
||||||
|
self.scopes = self.scope.split(' ')
|
||||||
|
|
||||||
|
def __contains__(self, scope):
|
||||||
|
return scope in self.scopes
|
||||||
|
|
||||||
|
|
||||||
def encode(payload):
|
def encode(payload):
|
||||||
|
|
|
@ -11,22 +11,13 @@ from lemoncurry import utils
|
||||||
@method_decorator(csrf_exempt, name='dispatch')
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
class TokenView(View):
|
class TokenView(View):
|
||||||
def get(self, req):
|
def get(self, req):
|
||||||
token = req.META.get('HTTP_AUTHORIZATION', '').split(' ')
|
token = tokens.auth(req)
|
||||||
if not token:
|
if hasattr(token, 'content'):
|
||||||
return utils.bad_req('missing Authorization header')
|
return token
|
||||||
if token[0] != 'Bearer':
|
|
||||||
return utils.bad_req('only Bearer auth is supported')
|
|
||||||
try:
|
|
||||||
token = tokens.decode(token[1])
|
|
||||||
except Exception:
|
|
||||||
return utils.forbid('invalid token')
|
|
||||||
|
|
||||||
user = get_user_model().objects.get(pk=token['uid'])
|
|
||||||
me = urljoin(utils.origin(req), user.url)
|
|
||||||
res = {
|
res = {
|
||||||
'me': me,
|
'me': token.me,
|
||||||
'client_id': token['cid'],
|
'client_id': token.client,
|
||||||
'scope': token['sco'],
|
'scope': token.scope,
|
||||||
}
|
}
|
||||||
return utils.choose_type(req, res)
|
return utils.choose_type(req, res)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
from django.contrib.auth import get_user_model
|
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
|
@ -16,19 +15,14 @@ from lemonauth import tokens
|
||||||
@method_decorator(csrf_exempt, name='dispatch')
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
class MicropubView(View):
|
class MicropubView(View):
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
auth = request.META.get('HTTP_AUTHORIZATION', '').split(' ')
|
token = tokens.auth(request)
|
||||||
if auth[0] != 'Bearer':
|
if hasattr(token, 'content'):
|
||||||
return utils.bad_req('only Bearer auth supported')
|
return token
|
||||||
try:
|
|
||||||
token = tokens.decode(auth[1])
|
|
||||||
except Exception:
|
|
||||||
return utils.forbid('invalid token')
|
|
||||||
user = get_user_model().objects.get(pk=token['uid'])
|
|
||||||
|
|
||||||
post = request.POST
|
post = request.POST
|
||||||
if post.get('h') != 'entry':
|
if post.get('h') != 'entry':
|
||||||
return utils.bad_req('only h=entry supported')
|
return utils.bad_req('only h=entry supported')
|
||||||
entry = Entry(author=user)
|
entry = Entry(author=token.user)
|
||||||
kind = Note
|
kind = Note
|
||||||
if 'name' in post:
|
if 'name' in post:
|
||||||
entry.name = post['name']
|
entry.name = post['name']
|
||||||
|
|
|
@ -46,6 +46,11 @@ class User(ModelMeta, AbstractUser):
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return self.url
|
return self.url
|
||||||
|
|
||||||
|
@property
|
||||||
|
def full_url(self):
|
||||||
|
base = 'https://' + DjangoSite.objects.get_current().domain
|
||||||
|
return urljoin(base, self.url)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def description(self):
|
def description(self):
|
||||||
return utils.to_plain(self.note)
|
return utils.to_plain(self.note)
|
||||||
|
@ -74,8 +79,8 @@ class User(ModelMeta, AbstractUser):
|
||||||
return {
|
return {
|
||||||
'@context': 'http://schema.org',
|
'@context': 'http://schema.org',
|
||||||
'@type': 'Person',
|
'@type': 'Person',
|
||||||
'@id': urljoin(base, self.url),
|
'@id': self.full_url,
|
||||||
'url': urljoin(base, self.url),
|
'url': self.full_url,
|
||||||
'name': self.name,
|
'name': self.name,
|
||||||
'email': self.email,
|
'email': self.email,
|
||||||
'image': urljoin(base, self.avatar.url),
|
'image': urljoin(base, self.avatar.url),
|
||||||
|
|
Loading…
Reference in a new issue