Ajout type contrat
This commit is contained in:
1
venv/lib/python3.12/site-packages/simple_sso/__init__.py
Normal file
1
venv/lib/python3.12/site-packages/simple_sso/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
__version__ = '1.3.0'
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
class WebserviceError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class BadRequest(WebserviceError):
|
||||
pass
|
||||
3
venv/lib/python3.12/site-packages/simple_sso/models.py
Normal file
3
venv/lib/python3.12/site-packages/simple_sso/models.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
This is only here so I can run tests
|
||||
"""
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,129 @@
|
||||
from copy import copy
|
||||
from urllib.parse import urlparse, urlunparse, urljoin, urlencode
|
||||
|
||||
from django.urls import re_path
|
||||
from django.contrib.auth import login
|
||||
from django.contrib.auth.backends import ModelBackend
|
||||
from django.contrib.auth.models import User
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.urls import NoReverseMatch, reverse
|
||||
from django.views.generic import View
|
||||
from itsdangerous import URLSafeTimedSerializer
|
||||
|
||||
from simple_sso.utils import SyncConsumer
|
||||
|
||||
|
||||
class LoginView(View):
|
||||
client = None
|
||||
|
||||
def get(self, request):
|
||||
next_ = self.get_next()
|
||||
scheme = 'https' if request.is_secure() else 'http'
|
||||
query = urlencode([('next', next_)])
|
||||
netloc = request.get_host()
|
||||
path = reverse('simple-sso-authenticate')
|
||||
redirect_to = urlunparse((scheme, netloc, path, '', query, ''))
|
||||
request_token = self.client.get_request_token(redirect_to)
|
||||
host = urljoin(self.client.server_url, 'authorize/')
|
||||
url = '%s?%s' % (host, urlencode([('token', request_token)]))
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
def get_next(self):
|
||||
"""
|
||||
Given a request, returns the URL where a user should be redirected to
|
||||
after login. Defaults to '/'
|
||||
"""
|
||||
next_ = self.request.GET.get('next', None)
|
||||
if not next_:
|
||||
return '/'
|
||||
netloc = urlparse(next_)[1]
|
||||
# Heavier security check -- don't allow redirection to a different
|
||||
# host.
|
||||
# Taken from django.contrib.auth.views.login
|
||||
if netloc and netloc != self.request.get_host():
|
||||
return '/'
|
||||
return next_
|
||||
|
||||
|
||||
class AuthenticateView(LoginView):
|
||||
client = None
|
||||
|
||||
def get(self, request):
|
||||
raw_access_token = request.GET['access_token']
|
||||
access_token = URLSafeTimedSerializer(self.client.private_key).loads(raw_access_token)
|
||||
user = self.client.get_user(access_token)
|
||||
user.backend = self.client.backend
|
||||
login(request, user)
|
||||
next_ = self.get_next()
|
||||
return HttpResponseRedirect(next_)
|
||||
|
||||
|
||||
class Client:
|
||||
login_view = LoginView
|
||||
authenticate_view = AuthenticateView
|
||||
backend = "%s.%s" % (ModelBackend.__module__, ModelBackend.__name__)
|
||||
user_extra_data = None
|
||||
|
||||
def __init__(self, server_url, public_key, private_key,
|
||||
user_extra_data=None):
|
||||
self.server_url = server_url
|
||||
self.public_key = public_key
|
||||
self.private_key = private_key
|
||||
self.consumer = SyncConsumer(self.server_url, self.public_key, self.private_key)
|
||||
if user_extra_data:
|
||||
self.user_extra_data = user_extra_data
|
||||
|
||||
@classmethod
|
||||
def from_dsn(cls, dsn):
|
||||
parse_result = urlparse(dsn)
|
||||
public_key = parse_result.username
|
||||
private_key = parse_result.password
|
||||
netloc = parse_result.hostname
|
||||
if parse_result.port:
|
||||
netloc += ':%s' % parse_result.port
|
||||
server_url = urlunparse((parse_result.scheme, netloc, parse_result.path,
|
||||
parse_result.params, parse_result.query, parse_result.fragment))
|
||||
return cls(server_url, public_key, private_key)
|
||||
|
||||
def get_request_token(self, redirect_to):
|
||||
try:
|
||||
url = reverse('simple-sso-request-token')
|
||||
except NoReverseMatch:
|
||||
# thisisfine
|
||||
url = '/request-token/'
|
||||
return self.consumer.consume(url, {'redirect_to': redirect_to})['request_token']
|
||||
|
||||
def get_user(self, access_token):
|
||||
data = {'access_token': access_token}
|
||||
if self.user_extra_data:
|
||||
data['extra_data'] = self.user_extra_data
|
||||
|
||||
try:
|
||||
url = reverse('simple-sso-verify')
|
||||
except NoReverseMatch:
|
||||
# thisisfine
|
||||
url = '/verify/'
|
||||
user_data = self.consumer.consume(url, data)
|
||||
user = self.build_user(user_data)
|
||||
return user
|
||||
|
||||
def build_user(self, user_data):
|
||||
try:
|
||||
user = User.objects.get(username=user_data['username'])
|
||||
# Update user data, excluding username changes
|
||||
# Work on copied _tmp dict to keep an untouched user_data
|
||||
user_data_tmp = copy(user_data)
|
||||
del user_data_tmp['username']
|
||||
for _attr, _val in user_data_tmp.items():
|
||||
setattr(user, _attr, _val)
|
||||
except User.DoesNotExist:
|
||||
user = User(**user_data)
|
||||
user.set_unusable_password()
|
||||
user.save()
|
||||
return user
|
||||
|
||||
def get_urls(self):
|
||||
return [
|
||||
re_path(r'^$', self.login_view.as_view(client=self), name='simple-sso-login'),
|
||||
re_path(r'^authenticate/$', self.authenticate_view.as_view(client=self), name='simple-sso-authenticate'),
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
default_app_config = 'simple_sso.sso_server.apps.SimpleSSOServer'
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class SimpleSSOServer(AppConfig):
|
||||
name = 'simple_sso.sso_server'
|
||||
@@ -0,0 +1,35 @@
|
||||
from django.db import migrations, models
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
import simple_sso.sso_server.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Consumer',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('name', models.CharField(unique=True, max_length=100)),
|
||||
('private_key', models.CharField(default=simple_sso.sso_server.models.ConsumerSecretKeyGenerator('private_key'), unique=True, max_length=64)),
|
||||
('public_key', models.CharField(default=simple_sso.sso_server.models.ConsumerSecretKeyGenerator('public_key'), unique=True, max_length=64)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Token',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('request_token', models.CharField(default=simple_sso.sso_server.models.TokenSecretKeyGenerator('request_token'), unique=True, max_length=64)),
|
||||
('access_token', models.CharField(default=simple_sso.sso_server.models.TokenSecretKeyGenerator('access_token'), unique=True, max_length=64)),
|
||||
('timestamp', models.DateTimeField(default=timezone.now)),
|
||||
('redirect_to', models.CharField(max_length=255)),
|
||||
('consumer', models.ForeignKey(related_name='tokens', to='sso_server.Consumer', on_delete=models.CASCADE)),
|
||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('sso_server', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='consumer',
|
||||
name='name',
|
||||
field=models.CharField(unique=True, max_length=255),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('sso_server', '0002_consumer_name_max_length'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='token',
|
||||
name='redirect_to',
|
||||
field=models.CharField(max_length=1023),
|
||||
),
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,79 @@
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
from django.utils.deconstruct import deconstructible
|
||||
|
||||
from ..utils import gen_secret_key
|
||||
|
||||
|
||||
@deconstructible
|
||||
class SecretKeyGenerator:
|
||||
"""
|
||||
Helper to give default values to Client.secret and Client.key
|
||||
"""
|
||||
|
||||
def __init__(self, field):
|
||||
self.field = field
|
||||
|
||||
def __call__(self):
|
||||
key = gen_secret_key(64)
|
||||
while self.get_model().objects.filter(**{self.field: key}).exists():
|
||||
key = gen_secret_key(64)
|
||||
return key
|
||||
|
||||
|
||||
class ConsumerSecretKeyGenerator(SecretKeyGenerator):
|
||||
def get_model(self):
|
||||
return Consumer
|
||||
|
||||
|
||||
class TokenSecretKeyGenerator(SecretKeyGenerator):
|
||||
def get_model(self):
|
||||
return Token
|
||||
|
||||
|
||||
class Consumer(models.Model):
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
private_key = models.CharField(
|
||||
max_length=64, unique=True,
|
||||
default=ConsumerSecretKeyGenerator('private_key')
|
||||
)
|
||||
public_key = models.CharField(
|
||||
max_length=64, unique=True,
|
||||
default=ConsumerSecretKeyGenerator('public_key')
|
||||
)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
def rotate_keys(self):
|
||||
self.secret = ConsumerSecretKeyGenerator('private_key')()
|
||||
self.key = ConsumerSecretKeyGenerator('public_key')()
|
||||
self.save()
|
||||
|
||||
|
||||
class Token(models.Model):
|
||||
consumer = models.ForeignKey(
|
||||
Consumer,
|
||||
related_name='tokens',
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
request_token = models.CharField(
|
||||
unique=True, max_length=64,
|
||||
default=TokenSecretKeyGenerator('request_token')
|
||||
)
|
||||
access_token = models.CharField(
|
||||
unique=True, max_length=64,
|
||||
default=TokenSecretKeyGenerator('access_token')
|
||||
)
|
||||
timestamp = models.DateTimeField(default=timezone.now)
|
||||
redirect_to = models.CharField(max_length=1023)
|
||||
user = models.ForeignKey(
|
||||
getattr(settings, 'AUTH_USER_MODEL', 'auth.User'),
|
||||
null=True,
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
def refresh(self):
|
||||
self.timestamp = timezone.now()
|
||||
self.save()
|
||||
@@ -0,0 +1,175 @@
|
||||
import datetime
|
||||
from urllib.parse import urlparse, urlencode, urlunparse
|
||||
|
||||
from django.contrib import admin
|
||||
from django.contrib.admin.options import ModelAdmin
|
||||
from django.http import (HttpResponseForbidden, HttpResponseBadRequest, HttpResponseRedirect, QueryDict)
|
||||
from django.urls import re_path
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.views.generic.base import View
|
||||
from itsdangerous import URLSafeTimedSerializer
|
||||
|
||||
from simple_sso.sso_server.models import Token, Consumer
|
||||
from simple_sso.utils import BaseProvider, provider_wrapper
|
||||
|
||||
|
||||
class Provider(BaseProvider):
|
||||
max_age = 5
|
||||
|
||||
def __init__(self, server):
|
||||
self.server = server
|
||||
|
||||
def get_private_key(self, public_key):
|
||||
try:
|
||||
self.consumer = Consumer.objects.get(public_key=public_key)
|
||||
except Consumer.DoesNotExist:
|
||||
return None
|
||||
return self.consumer.private_key
|
||||
|
||||
|
||||
class RequestTokenProvider(Provider):
|
||||
def provide(self, data):
|
||||
redirect_to = data['redirect_to']
|
||||
token = Token.objects.create(consumer=self.consumer, redirect_to=redirect_to)
|
||||
return {'request_token': token.request_token}
|
||||
|
||||
|
||||
class AuthorizeView(View):
|
||||
"""
|
||||
The client get's redirected to this view with the `request_token` obtained
|
||||
by the Request Token Request by the client application beforehand.
|
||||
|
||||
This view checks if the user is logged in on the server application and if
|
||||
that user has the necessary rights.
|
||||
|
||||
If the user is not logged in, the user is prompted to log in.
|
||||
"""
|
||||
server = None
|
||||
|
||||
def get(self, request):
|
||||
request_token = request.GET.get('token', None)
|
||||
if not request_token:
|
||||
return self.missing_token_argument()
|
||||
try:
|
||||
self.token = Token.objects.select_related('consumer').get(request_token=request_token)
|
||||
except Token.DoesNotExist:
|
||||
return self.token_not_found()
|
||||
if not self.check_token_timeout():
|
||||
return self.token_timeout()
|
||||
self.token.refresh()
|
||||
if request.user.is_authenticated:
|
||||
return self.handle_authenticated_user()
|
||||
else:
|
||||
return self.handle_unauthenticated_user()
|
||||
|
||||
def missing_token_argument(self):
|
||||
return HttpResponseBadRequest('Token missing')
|
||||
|
||||
def token_not_found(self):
|
||||
return HttpResponseForbidden('Token not found')
|
||||
|
||||
def token_timeout(self):
|
||||
return HttpResponseForbidden('Token timed out')
|
||||
|
||||
def check_token_timeout(self):
|
||||
delta = timezone.now() - self.token.timestamp
|
||||
if delta > self.server.token_timeout:
|
||||
self.token.delete()
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def handle_authenticated_user(self):
|
||||
if self.server.has_access(self.request.user, self.token.consumer):
|
||||
return self.success()
|
||||
else:
|
||||
return self.access_denied()
|
||||
|
||||
def handle_unauthenticated_user(self):
|
||||
next_ = '%s?%s' % (self.request.path, urlencode([('token', self.token.request_token)]))
|
||||
url = '%s?%s' % (reverse(self.server.auth_view_name), urlencode([('next', next_)]))
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
def access_denied(self):
|
||||
return HttpResponseForbidden("Access denied")
|
||||
|
||||
def success(self):
|
||||
self.token.user = self.request.user
|
||||
self.token.save()
|
||||
serializer = URLSafeTimedSerializer(self.token.consumer.private_key)
|
||||
parse_result = urlparse(self.token.redirect_to)
|
||||
query_dict = QueryDict(parse_result.query, mutable=True)
|
||||
query_dict['access_token'] = serializer.dumps(self.token.access_token)
|
||||
url = urlunparse((parse_result.scheme, parse_result.netloc, parse_result.path, '', query_dict.urlencode(), ''))
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
|
||||
class VerificationProvider(Provider, AuthorizeView):
|
||||
def provide(self, data):
|
||||
token = data['access_token']
|
||||
try:
|
||||
self.token = Token.objects.select_related('user').get(access_token=token, consumer=self.consumer)
|
||||
except Token.DoesNotExist:
|
||||
return self.token_not_found()
|
||||
if not self.check_token_timeout():
|
||||
return self.token_timeout()
|
||||
if not self.token.user:
|
||||
return self.token_not_bound()
|
||||
extra_data = data.get('extra_data', None)
|
||||
return self.server.get_user_data(
|
||||
self.token.user, self.consumer, extra_data=extra_data)
|
||||
|
||||
def token_not_bound(self):
|
||||
return HttpResponseForbidden("Invalid token")
|
||||
|
||||
|
||||
class ConsumerAdmin(ModelAdmin):
|
||||
readonly_fields = ['public_key', 'private_key']
|
||||
|
||||
|
||||
class Server:
|
||||
request_token_provider = RequestTokenProvider
|
||||
authorize_view = AuthorizeView
|
||||
verification_provider = VerificationProvider
|
||||
token_timeout = datetime.timedelta(minutes=5)
|
||||
client_admin = ConsumerAdmin
|
||||
auth_view_name = 'login'
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
self.register_admin()
|
||||
|
||||
def register_admin(self):
|
||||
admin.site.register(Consumer, self.client_admin)
|
||||
|
||||
def has_access(self, user, consumer):
|
||||
return True
|
||||
|
||||
def get_user_extra_data(self, user, consumer, extra_data):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_user_data(self, user, consumer, extra_data=None):
|
||||
user_data = {
|
||||
'username': user.username,
|
||||
'email': user.email,
|
||||
'first_name': user.first_name,
|
||||
'last_name': user.last_name,
|
||||
'is_staff': False,
|
||||
'is_superuser': False,
|
||||
'is_active': user.is_active,
|
||||
}
|
||||
if extra_data:
|
||||
user_data['extra_data'] = self.get_user_extra_data(
|
||||
user, consumer, extra_data)
|
||||
return user_data
|
||||
|
||||
def get_urls(self):
|
||||
return [
|
||||
re_path(r'^request-token/$', provider_wrapper(self.request_token_provider(server=self)),
|
||||
name='simple-sso-request-token'),
|
||||
re_path(r'^authorize/$', self.authorize_view.as_view(server=self), name='simple-sso-authorize'),
|
||||
re_path(r'^verify/$', provider_wrapper(
|
||||
self.verification_provider(server=self)), name='simple-sso-verify'),
|
||||
]
|
||||
158
venv/lib/python3.12/site-packages/simple_sso/utils.py
Normal file
158
venv/lib/python3.12/site-packages/simple_sso/utils.py
Normal file
@@ -0,0 +1,158 @@
|
||||
import string
|
||||
from random import SystemRandom
|
||||
from urllib.parse import urlparse, urlunparse, urljoin
|
||||
|
||||
import requests
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponse
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from itsdangerous import TimedSerializer, SignatureExpired, BadSignature
|
||||
|
||||
from simple_sso.exceptions import BadRequest, WebserviceError
|
||||
|
||||
random = SystemRandom()
|
||||
|
||||
KEY_CHARACTERS = string.ascii_letters + string.digits
|
||||
PUBLIC_KEY_HEADER = 'x-services-public-key'
|
||||
|
||||
|
||||
def default_gen_secret_key(length=40):
|
||||
return ''.join([random.choice(KEY_CHARACTERS) for _ in range(length)])
|
||||
|
||||
|
||||
def gen_secret_key(length=40):
|
||||
generator = getattr(settings, 'SIMPLE_SSO_KEYGENERATOR', default_gen_secret_key)
|
||||
return generator(length)
|
||||
|
||||
|
||||
def _split_dsn(dsn):
|
||||
parse_result = urlparse(dsn)
|
||||
host = parse_result.hostname
|
||||
if parse_result.port:
|
||||
host += ':%s' % parse_result.port
|
||||
base_url = urlunparse((
|
||||
parse_result.scheme,
|
||||
host,
|
||||
parse_result.path,
|
||||
parse_result.params,
|
||||
parse_result.query,
|
||||
parse_result.fragment,
|
||||
))
|
||||
return base_url, parse_result.username, parse_result.password
|
||||
|
||||
|
||||
class BaseConsumer(object):
|
||||
def __init__(self, base_url, public_key, private_key):
|
||||
self.base_url = base_url
|
||||
self.public_key = public_key
|
||||
self.signer = TimedSerializer(private_key)
|
||||
|
||||
@classmethod
|
||||
def from_dsn(cls, dsn):
|
||||
base_url, public_key, private_key = _split_dsn(dsn)
|
||||
return cls(base_url, public_key, private_key)
|
||||
|
||||
def consume(self, path, data, max_age=None):
|
||||
if not path.startswith('/'):
|
||||
raise ValueError("Paths must start with a slash")
|
||||
signed_data = self.signer.dumps(data)
|
||||
headers = {
|
||||
PUBLIC_KEY_HEADER: self.public_key,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
url = self.build_url(path)
|
||||
body = self.send_request(url, data=signed_data, headers=headers)
|
||||
return self.handle_response(body, max_age)
|
||||
|
||||
def handle_response(self, body, max_age):
|
||||
return self.signer.loads(body, max_age=max_age)
|
||||
|
||||
def send_request(self, url, data, headers):
|
||||
raise NotImplementedError(
|
||||
'Implement send_request on BaseConsumer subclasses')
|
||||
|
||||
@staticmethod
|
||||
def raise_for_status(status_code, message):
|
||||
if status_code == 400:
|
||||
raise BadRequest(message)
|
||||
elif status_code >= 300:
|
||||
raise WebserviceError(message)
|
||||
|
||||
def build_url(self, path):
|
||||
path = path.lstrip('/')
|
||||
return urljoin(self.base_url, path)
|
||||
|
||||
|
||||
class SyncConsumer(BaseConsumer):
|
||||
def __init__(self, base_url, public_key, private_key):
|
||||
super(SyncConsumer, self).__init__(base_url, public_key, private_key)
|
||||
self.session = requests.session()
|
||||
|
||||
def send_request(self, url, data, headers): # pragma: no cover
|
||||
response = self.session.post(url, data=data, headers=headers)
|
||||
self.raise_for_status(response.status_code, response.content)
|
||||
return response.content
|
||||
|
||||
|
||||
class BaseProvider(object):
|
||||
max_age = None
|
||||
|
||||
def provide(self, data):
|
||||
raise NotImplementedError(
|
||||
'Subclasses of services.models.Provider must implement '
|
||||
'the provide method'
|
||||
)
|
||||
|
||||
def get_private_key(self, public_key):
|
||||
raise NotImplementedError(
|
||||
'Subclasses of services.models.Provider must implement '
|
||||
'the get_private_key method'
|
||||
)
|
||||
|
||||
def report_exception(self):
|
||||
pass
|
||||
|
||||
def get_response(self, method, signed_data, get_header):
|
||||
if method != 'POST':
|
||||
return 405, ['POST']
|
||||
public_key = get_header(PUBLIC_KEY_HEADER, None)
|
||||
if not public_key:
|
||||
return 400, "No public key"
|
||||
private_key = self.get_private_key(public_key)
|
||||
if not private_key:
|
||||
return 400, "Invalid public key"
|
||||
signer = TimedSerializer(private_key)
|
||||
try:
|
||||
data = signer.loads(signed_data, max_age=self.max_age)
|
||||
except SignatureExpired:
|
||||
return 400, "Signature expired"
|
||||
except BadSignature:
|
||||
return 400, "Bad Signature"
|
||||
try:
|
||||
raw_response_data = self.provide(data)
|
||||
except:
|
||||
self.report_exception()
|
||||
return 400, "Failed to process the request"
|
||||
response_data = signer.dumps(raw_response_data)
|
||||
return 200, response_data
|
||||
|
||||
|
||||
def provider_wrapper(provider):
|
||||
def provider_view(request):
|
||||
def get_header(key, default):
|
||||
django_key = 'HTTP_%s' % key.upper().replace('-', '_')
|
||||
return request.META.get(django_key, default)
|
||||
|
||||
method = request.method
|
||||
if getattr(request, 'body', None):
|
||||
signed_data = request.body
|
||||
else:
|
||||
signed_data = request.raw_post_data
|
||||
status_code, data = provider.get_response(
|
||||
method,
|
||||
signed_data,
|
||||
get_header,
|
||||
)
|
||||
return HttpResponse(data, status=status_code)
|
||||
|
||||
return csrf_exempt(provider_view)
|
||||
Reference in New Issue
Block a user