1
# This file is part of Software Center Ratings and Reviews
2
# Copyright (C) 2016 Canonical Ltd.
4
# This program is free software: you can redistribute it and/or modify
5
# it under the terms of the GNU Affero General Public License as
6
# published by the Free Software Foundation, either version 3 of the
7
# License, or (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU Affero General Public License for more details.
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
"""Macaroon authenticator for Piston."""
19
from __future__ import absolute_import
23
from django.http import HttpResponse
24
from piston.authentication import OAuthAuthentication
26
from core.utilities import (
27
update_or_create_user_from_sso,
32
logger = logging.getLogger(__name__)
35
def has_macaroon_bits(authorization):
36
"""Return whether there are Macaroon bits in authorization.
38
Macaroon bits are detected by ensuring the authorization starts with
43
authorization and authorization.split()[0].lower() == 'macaroon')
47
class MacaroonsAuthentication(OAuthAuthentication):
48
""" This class is a Piston Authentication class.
50
See http://bitbucket.org/jespern/django-piston/wiki/Documentation for
53
def __init__(self, realm='API'):
57
""" We define our own challenge so that we can provide our own realm
58
and return a simple error message instead of rendering a template.
60
resp = HttpResponse("Authorization Required")
61
resp['WWW-Authenticate'] = 'Macaroon realm="%s"' % self.realm
62
resp.status_code = 401
65
def is_authenticated(self, request):
66
authorization = request.META.get('HTTP_AUTHORIZATION', None)
67
if not has_macaroon_bits(authorization):
68
# skip network request as macaroon headers are missing
70
data = web_services.verify_acl(request)
71
account = data and data.get('account', {}) or {}
72
verified = account.get('verified', False)
76
# get or create user account
77
user, _ = update_or_create_user_from_sso(
78
account.get('openid'), account=account)
80
# inject user into request