|
Pierre-Yves Chibon |
f1a2e7 |
# -*- coding: utf-8 -*-
|
|
Pierre-Yves Chibon |
f1a2e7 |
# Flask-FAS-OpenID - A Flask extension for authorizing users with FAS-OpenID
|
|
Pierre-Yves Chibon |
f1a2e7 |
#
|
|
Pierre-Yves Chibon |
f1a2e7 |
# Primary maintainer: Patrick Uiterwijk <puiterwijk@fedoraproject.org></puiterwijk@fedoraproject.org>
|
|
Pierre-Yves Chibon |
f1a2e7 |
#
|
|
Pierre-Yves Chibon |
f1a2e7 |
# Copyright (c) 2013, Patrick Uiterwijk
|
|
Pierre-Yves Chibon |
f1a2e7 |
# This file is part of python-fedora
|
|
Pierre-Yves Chibon |
f1a2e7 |
#
|
|
Pierre-Yves Chibon |
f1a2e7 |
# python-fedora is free software; you can redistribute it and/or
|
|
Pierre-Yves Chibon |
f1a2e7 |
# modify it under the terms of the GNU Lesser General Public
|
|
Pierre-Yves Chibon |
f1a2e7 |
# License as published by the Free Software Foundation; either
|
|
Pierre-Yves Chibon |
f1a2e7 |
# version 2.1 of the License, or (at your option) any later version.
|
|
Pierre-Yves Chibon |
f1a2e7 |
#
|
|
Pierre-Yves Chibon |
f1a2e7 |
# python-fedora is distributed in the hope that it will be useful,
|
|
Pierre-Yves Chibon |
f1a2e7 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Pierre-Yves Chibon |
f1a2e7 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Pierre-Yves Chibon |
f1a2e7 |
# Lesser General Public License for more details.
|
|
Pierre-Yves Chibon |
f1a2e7 |
#
|
|
Pierre-Yves Chibon |
f1a2e7 |
# You should have received a copy of the GNU Lesser General Public
|
|
Pierre-Yves Chibon |
f1a2e7 |
# License along with python-fedora; if not, see <http: licenses="" www.gnu.org=""></http:>
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
'''
|
|
Pierre-Yves Chibon |
f1a2e7 |
FAS-OpenID authentication plugin for the flask web framework
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
.. moduleauthor:: Patrick Uiterwijk <puiterwijk@fedoraproject.org></puiterwijk@fedoraproject.org>
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
..versionadded:: 0.3.33
|
|
Pierre-Yves Chibon |
f1a2e7 |
'''
|
|
Pierre-Yves Chibon |
f1a2e7 |
from functools import wraps
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
from bunch import Bunch
|
|
Pierre-Yves Chibon |
f1a2e7 |
import flask
|
|
Pierre-Yves Chibon |
f1a2e7 |
try:
|
|
Pierre-Yves Chibon |
f1a2e7 |
from flask import _app_ctx_stack as stack
|
|
Pierre-Yves Chibon |
944e0e |
except ImportError: # pragma: no cover
|
|
Pierre-Yves Chibon |
f1a2e7 |
from flask import _request_ctx_stack as stack
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
import openid
|
|
Pierre-Yves Chibon |
f1a2e7 |
from openid.consumer import consumer
|
|
Pierre-Yves Chibon |
f1a2e7 |
from openid.fetchers import setDefaultFetcher, Urllib2Fetcher
|
|
Pierre-Yves Chibon |
f1a2e7 |
from openid.extensions import pape, sreg
|
|
Pierre-Yves Chibon |
f1a2e7 |
from openid_cla import cla
|
|
Pierre-Yves Chibon |
f1a2e7 |
from openid_teams import teams
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
from fedora import __version__
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
944e0e |
class FASJSONEncoder(flask.json.JSONEncoder): # pragma: no cover
|
|
Pierre-Yves Chibon |
f1a2e7 |
""" Dedicated JSON encoder for the FAS openid information. """
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
def default(self, o):
|
|
Pierre-Yves Chibon |
f1a2e7 |
"""Implement this method in a subclass such that it returns a
|
|
Pierre-Yves Chibon |
f1a2e7 |
serializable object for ``o``, or calls the base implementation (to
|
|
Pierre-Yves Chibon |
f1a2e7 |
raise a ``TypeError``).
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
For example, to support arbitrary iterators, you could implement
|
|
Pierre-Yves Chibon |
f1a2e7 |
default like this::
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
def default(self, o):
|
|
Pierre-Yves Chibon |
f1a2e7 |
try:
|
|
Pierre-Yves Chibon |
f1a2e7 |
iterable = iter(o)
|
|
Pierre-Yves Chibon |
f1a2e7 |
except TypeError:
|
|
Pierre-Yves Chibon |
f1a2e7 |
pass
|
|
Pierre-Yves Chibon |
f1a2e7 |
else:
|
|
Pierre-Yves Chibon |
f1a2e7 |
return list(iterable)
|
|
Pierre-Yves Chibon |
f1a2e7 |
return JSONEncoder.default(self, o)
|
|
Pierre-Yves Chibon |
f1a2e7 |
"""
|
|
Pierre-Yves Chibon |
f1a2e7 |
if isinstance(o, (set, frozenset)):
|
|
Pierre-Yves Chibon |
f1a2e7 |
return list(o)
|
|
Pierre-Yves Chibon |
f1a2e7 |
return flask.json.JSONEncoder.default(self, o)
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
944e0e |
class FAS(object): # pragma: no cover
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
def __init__(self, app=None):
|
|
Pierre-Yves Chibon |
f1a2e7 |
self.postlogin_func = None
|
|
Pierre-Yves Chibon |
f1a2e7 |
self.app = app
|
|
Pierre-Yves Chibon |
f1a2e7 |
if self.app is not None:
|
|
Pierre-Yves Chibon |
f1a2e7 |
self._init_app(app)
|
|
Pierre-Yves Chibon |
f1a2e7 |
# json_encoder is only available from flask 0.10
|
|
Pierre-Yves Chibon |
f1a2e7 |
version = flask.__version__.split('.')
|
|
Pierre-Yves Chibon |
f1a2e7 |
assume_recent = False
|
|
Pierre-Yves Chibon |
f1a2e7 |
try:
|
|
Pierre-Yves Chibon |
f1a2e7 |
major = int(version[0])
|
|
Pierre-Yves Chibon |
f1a2e7 |
minor = int(version[1])
|
|
Pierre-Yves Chibon |
f1a2e7 |
except ValueError:
|
|
Pierre-Yves Chibon |
f1a2e7 |
# We'll assume we're using a recent enough flask as the packages
|
|
Pierre-Yves Chibon |
f1a2e7 |
# of old versions used sane version numbers.
|
|
Pierre-Yves Chibon |
f1a2e7 |
assume_recent = True
|
|
Pierre-Yves Chibon |
f1a2e7 |
if assume_recent or (major >= 0 and minor >= 10):
|
|
Pierre-Yves Chibon |
f1a2e7 |
self.app.json_encoder = FASJSONEncoder
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
def _init_app(self, app):
|
|
Pierre-Yves Chibon |
f1a2e7 |
app.config.setdefault('FAS_OPENID_ENDPOINT',
|
|
Pierre-Yves Chibon |
f1a2e7 |
'http://id.fedoraproject.org/')
|
|
Pierre-Yves Chibon |
f1a2e7 |
app.config.setdefault('FAS_OPENID_CHECK_CERT', True)
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
if not self.app.config['FAS_OPENID_CHECK_CERT']:
|
|
Pierre-Yves Chibon |
f1a2e7 |
setDefaultFetcher(Urllib2Fetcher())
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
@app.route('/_flask_fas_openid_handler/', methods=['GET', 'POST'])
|
|
Pierre-Yves Chibon |
f1a2e7 |
def flask_fas_openid_handler():
|
|
Pierre-Yves Chibon |
f1a2e7 |
return self._handle_openid_request()
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
app.before_request(self._check_session)
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
def postlogin(self, f):
|
|
Pierre-Yves Chibon |
f1a2e7 |
"""Marks a function as post login handler. This decorator calls your
|
|
Pierre-Yves Chibon |
f1a2e7 |
function after the login has been performed.
|
|
Pierre-Yves Chibon |
f1a2e7 |
"""
|
|
Pierre-Yves Chibon |
f1a2e7 |
self.postlogin_func = f
|
|
Pierre-Yves Chibon |
f1a2e7 |
return f
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
def _handle_openid_request(self):
|
|
Pierre-Yves Chibon |
f1a2e7 |
return_url = flask.session.get('FLASK_FAS_OPENID_RETURN_URL', None)
|
|
Pierre-Yves Chibon |
f1a2e7 |
cancel_url = flask.session.get('FLASK_FAS_OPENID_CANCEL_URL', None)
|
|
Pierre-Yves Chibon |
f1a2e7 |
base_url = self.normalize_url(flask.request.base_url)
|
|
Pierre-Yves Chibon |
f1a2e7 |
oidconsumer = consumer.Consumer(flask.session, None)
|
|
Pierre-Yves Chibon |
f1a2e7 |
info = oidconsumer.complete(flask.request.values, base_url)
|
|
Pierre-Yves Chibon |
f1a2e7 |
display_identifier = info.getDisplayIdentifier()
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
if info.status == consumer.FAILURE and display_identifier:
|
|
Pierre-Yves Chibon |
f1a2e7 |
return 'FAILURE. display_identifier: %s' % display_identifier
|
|
Pierre-Yves Chibon |
f1a2e7 |
elif info.status == consumer.CANCEL:
|
|
Pierre-Yves Chibon |
f1a2e7 |
if cancel_url:
|
|
Pierre-Yves Chibon |
f1a2e7 |
return flask.redirect(cancel_url)
|
|
Pierre-Yves Chibon |
f1a2e7 |
return 'OpenID request was cancelled'
|
|
Pierre-Yves Chibon |
f1a2e7 |
elif info.status == consumer.SUCCESS:
|
|
Pierre-Yves Chibon |
f1a2e7 |
sreg_resp = sreg.SRegResponse.fromSuccessResponse(info)
|
|
Pierre-Yves Chibon |
f1a2e7 |
pape_resp = pape.Response.fromSuccessResponse(info)
|
|
Pierre-Yves Chibon |
f1a2e7 |
teams_resp = teams.TeamsResponse.fromSuccessResponse(info)
|
|
Pierre-Yves Chibon |
f1a2e7 |
cla_resp = cla.CLAResponse.fromSuccessResponse(info)
|
|
Pierre-Yves Chibon |
f1a2e7 |
user = {'fullname': '', 'username': '', 'email': '',
|
|
Pierre-Yves Chibon |
f1a2e7 |
'timezone': '', 'cla_done': False, 'groups': []}
|
|
Pierre-Yves Chibon |
f1a2e7 |
if not sreg_resp:
|
|
Pierre-Yves Chibon |
f1a2e7 |
# If we have no basic info, be gone with them!
|
|
Pierre-Yves Chibon |
f1a2e7 |
return flask.redirect(cancel_url)
|
|
Pierre-Yves Chibon |
f1a2e7 |
user['username'] = sreg_resp.get('nickname')
|
|
Pierre-Yves Chibon |
f1a2e7 |
user['fullname'] = sreg_resp.get('fullname')
|
|
Pierre-Yves Chibon |
f1a2e7 |
user['email'] = sreg_resp.get('email')
|
|
Pierre-Yves Chibon |
f1a2e7 |
user['timezone'] = sreg_resp.get('timezone')
|
|
Pierre-Yves Chibon |
f1a2e7 |
if cla_resp:
|
|
Pierre-Yves Chibon |
f1a2e7 |
user['cla_done'] = cla.CLA_URI_FEDORA_DONE in cla_resp.clas
|
|
Pierre-Yves Chibon |
f1a2e7 |
if teams_resp:
|
|
Pierre-Yves Chibon |
f1a2e7 |
# The groups do not contain the cla_ groups
|
|
Pierre-Yves Chibon |
f1a2e7 |
user['groups'] = frozenset(teams_resp.teams)
|
|
Pierre-Yves Chibon |
f1a2e7 |
flask.session['FLASK_FAS_OPENID_USER'] = user
|
|
Pierre-Yves Chibon |
f1a2e7 |
flask.session.modified = True
|
|
Pierre-Yves Chibon |
f1a2e7 |
if self.postlogin_func is not None:
|
|
Pierre-Yves Chibon |
f1a2e7 |
self._check_session()
|
|
Pierre-Yves Chibon |
f1a2e7 |
return self.postlogin_func(return_url)
|
|
Pierre-Yves Chibon |
f1a2e7 |
else:
|
|
Pierre-Yves Chibon |
f1a2e7 |
return flask.redirect(return_url)
|
|
Pierre-Yves Chibon |
f1a2e7 |
else:
|
|
Pierre-Yves Chibon |
f1a2e7 |
return 'Strange state: %s' % info.status
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
def _check_session(self):
|
|
Pierre-Yves Chibon |
e04c77 |
if 'FLASK_FAS_OPENID_USER' not in flask.session \
|
|
Pierre-Yves Chibon |
f1a2e7 |
or flask.session['FLASK_FAS_OPENID_USER'] is None:
|
|
Pierre-Yves Chibon |
f1a2e7 |
flask.g.fas_user = None
|
|
Pierre-Yves Chibon |
f1a2e7 |
else:
|
|
Pierre-Yves Chibon |
f1a2e7 |
user = flask.session['FLASK_FAS_OPENID_USER']
|
|
Pierre-Yves Chibon |
f1a2e7 |
# Add approved_memberships to provide backwards compatibility
|
|
Pierre-Yves Chibon |
f1a2e7 |
# New applications should only use g.fas_user.groups
|
|
Pierre-Yves Chibon |
f1a2e7 |
user['approved_memberships'] = []
|
|
Pierre-Yves Chibon |
f1a2e7 |
for group in user['groups']:
|
|
Pierre-Yves Chibon |
f1a2e7 |
membership = dict()
|
|
Pierre-Yves Chibon |
f1a2e7 |
membership['name'] = group
|
|
Pierre-Yves Chibon |
f1a2e7 |
user['approved_memberships'].append(Bunch.fromDict(membership))
|
|
Pierre-Yves Chibon |
f1a2e7 |
flask.g.fas_user = Bunch.fromDict(user)
|
|
Pierre-Yves Chibon |
f1a2e7 |
flask.g.fas_user.groups = frozenset(flask.g.fas_user.groups)
|
|
Pierre-Yves Chibon |
f1a2e7 |
flask.g.fas_session_id = 0
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
def login(self, username=None, password=None, return_url=None,
|
|
Pierre-Yves Chibon |
f1a2e7 |
cancel_url=None, groups=['_FAS_ALL_GROUPS_']):
|
|
Pierre-Yves Chibon |
f1a2e7 |
"""Tries to log in a user.
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
Sets the user information on :attr:`flask.g.fas_user`.
|
|
Pierre-Yves Chibon |
f1a2e7 |
Will set 0 to :attr:`flask.g.fas_session_id, for compatibility
|
|
Pierre-Yves Chibon |
f1a2e7 |
with flask_fas.
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
:kwarg username: Not used, but accepted for compatibility with the
|
|
Pierre-Yves Chibon |
f1a2e7 |
flask_fas module
|
|
Pierre-Yves Chibon |
f1a2e7 |
:kwarg password: Not used, but accepted for compatibility with the
|
|
Pierre-Yves Chibon |
f1a2e7 |
flask_fas module
|
|
Pierre-Yves Chibon |
f1a2e7 |
:kwarg return_url: The URL to forward the user to after login
|
|
Pierre-Yves Chibon |
f1a2e7 |
:kwarg groups: A string or a list of group the user should belong
|
|
Pierre-Yves Chibon |
f1a2e7 |
to to be authentified.
|
|
Pierre-Yves Chibon |
f1a2e7 |
:returns: True if the user was succesfully authenticated.
|
|
Pierre-Yves Chibon |
f1a2e7 |
:raises: Might raise an redirect to the OpenID endpoint
|
|
Pierre-Yves Chibon |
f1a2e7 |
"""
|
|
Pierre-Yves Chibon |
f1a2e7 |
if return_url is None and return_func is None:
|
|
Pierre-Yves Chibon |
f1a2e7 |
return_url = flask.request.args.get('next', flask.request.url)
|
|
Pierre-Yves Chibon |
f1a2e7 |
session = {}
|
|
Pierre-Yves Chibon |
f1a2e7 |
oidconsumer = consumer.Consumer(session, None)
|
|
Pierre-Yves Chibon |
f1a2e7 |
try:
|
|
Pierre-Yves Chibon |
f1a2e7 |
request = oidconsumer.begin(self.app.config['FAS_OPENID_ENDPOINT'])
|
|
Pierre-Yves Chibon |
f1a2e7 |
except consumer.DiscoveryFailure, exc:
|
|
Pierre-Yves Chibon |
f1a2e7 |
# VERY strange, as this means it could not discover an OpenID
|
|
Pierre-Yves Chibon |
f1a2e7 |
# endpoint at FAS_OPENID_ENDPOINT
|
|
Pierre-Yves Chibon |
f1a2e7 |
return 'discoveryfailure'
|
|
Pierre-Yves Chibon |
f1a2e7 |
if request is None:
|
|
Pierre-Yves Chibon |
f1a2e7 |
# Also very strange, as this means the discovered OpenID
|
|
Pierre-Yves Chibon |
f1a2e7 |
# endpoint is no OpenID endpoint
|
|
Pierre-Yves Chibon |
f1a2e7 |
return 'no-request'
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
if isinstance(groups, basestring):
|
|
Pierre-Yves Chibon |
f1a2e7 |
groups = [groups]
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
request.addExtension(sreg.SRegRequest(
|
|
Pierre-Yves Chibon |
f1a2e7 |
required=['nickname', 'fullname', 'email', 'timezone']))
|
|
Pierre-Yves Chibon |
f1a2e7 |
request.addExtension(pape.Request([]))
|
|
Pierre-Yves Chibon |
f1a2e7 |
request.addExtension(teams.TeamsRequest(requested=groups))
|
|
Pierre-Yves Chibon |
f1a2e7 |
request.addExtension(cla.CLARequest(
|
|
Pierre-Yves Chibon |
f1a2e7 |
requested=[cla.CLA_URI_FEDORA_DONE]))
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
trust_root = self.normalize_url(flask.request.url_root)
|
|
Pierre-Yves Chibon |
f1a2e7 |
return_to = trust_root + '_flask_fas_openid_handler/'
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
flask.session['FLASK_FAS_OPENID_RETURN_URL'] = return_url
|
|
Pierre-Yves Chibon |
f1a2e7 |
flask.session['FLASK_FAS_OPENID_CANCEL_URL'] = cancel_url
|
|
Pierre-Yves Chibon |
f1a2e7 |
if request.shouldSendRedirect():
|
|
Pierre-Yves Chibon |
f1a2e7 |
redirect_url = request.redirectURL(
|
|
Pierre-Yves Chibon |
f1a2e7 |
trust_root, return_to, False)
|
|
Pierre-Yves Chibon |
f1a2e7 |
return flask.redirect(redirect_url)
|
|
Pierre-Yves Chibon |
f1a2e7 |
else:
|
|
Pierre-Yves Chibon |
f1a2e7 |
return request.htmlMarkup(
|
|
Pierre-Yves Chibon |
f1a2e7 |
trust_root, return_to,
|
|
Pierre-Yves Chibon |
f1a2e7 |
form_tag_attrs={'id': 'openid_message'}, immediate=False)
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
def logout(self):
|
|
Pierre-Yves Chibon |
f1a2e7 |
'''Logout the user associated with this session
|
|
Pierre-Yves Chibon |
f1a2e7 |
'''
|
|
Pierre-Yves Chibon |
f1a2e7 |
flask.session['FLASK_FAS_OPENID_USER'] = None
|
|
Pierre-Yves Chibon |
f1a2e7 |
flask.g.fas_session_id = None
|
|
Pierre-Yves Chibon |
f1a2e7 |
flask.g.fas_user = None
|
|
Pierre-Yves Chibon |
f1a2e7 |
flask.session.modified = True
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
def normalize_url(self, url):
|
|
Pierre-Yves Chibon |
f1a2e7 |
''' Replace the scheme prefix of a url with our preferred scheme.
|
|
Pierre-Yves Chibon |
f1a2e7 |
'''
|
|
Pierre-Yves Chibon |
f1a2e7 |
scheme = self.app.config['PREFERRED_URL_SCHEME']
|
|
Pierre-Yves Chibon |
f1a2e7 |
scheme_index = url.index('://')
|
|
Pierre-Yves Chibon |
f1a2e7 |
return scheme + url[scheme_index:]
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
# This is a decorator we can use with any HTTP method (except login, obviously)
|
|
Pierre-Yves Chibon |
f1a2e7 |
# to require a login.
|
|
Pierre-Yves Chibon |
f1a2e7 |
# If the user is not logged in, it will redirect them to the login form.
|
|
Pierre-Yves Chibon |
f1a2e7 |
# http://flask.pocoo.org/docs/patterns/viewdecorators/#login-required-decorator
|
|
Pierre-Yves Chibon |
944e0e |
def fas_login_required(function): # pragma: no cover
|
|
Pierre-Yves Chibon |
f1a2e7 |
""" Flask decorator to ensure that the user is logged in against FAS.
|
|
Pierre-Yves Chibon |
f1a2e7 |
To use this decorator you need to have a function named 'auth_login'.
|
|
Pierre-Yves Chibon |
f1a2e7 |
Without that function the redirect if the user is not logged in will not
|
|
Pierre-Yves Chibon |
f1a2e7 |
work.
|
|
Pierre-Yves Chibon |
f1a2e7 |
"""
|
|
Pierre-Yves Chibon |
f1a2e7 |
@wraps(function)
|
|
Pierre-Yves Chibon |
f1a2e7 |
def decorated_function(*args, **kwargs):
|
|
Pierre-Yves Chibon |
f1a2e7 |
if flask.g.fas_user is None:
|
|
Pierre-Yves Chibon |
f1a2e7 |
return flask.redirect(flask.url_for('auth_login',
|
|
Pierre-Yves Chibon |
f1a2e7 |
next=flask.request.url))
|
|
Pierre-Yves Chibon |
f1a2e7 |
return function(*args, **kwargs)
|
|
Pierre-Yves Chibon |
f1a2e7 |
return decorated_function
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
f1a2e7 |
|
|
Pierre-Yves Chibon |
944e0e |
def cla_plus_one_required(function): # pragma: no cover
|
|
Pierre-Yves Chibon |
f1a2e7 |
""" Flask decorator to retrict access to CLA+1.
|
|
Pierre-Yves Chibon |
f1a2e7 |
To use this decorator you need to have a function named 'auth_login'.
|
|
Pierre-Yves Chibon |
f1a2e7 |
Without that function the redirect if the user is not logged in will not
|
|
Pierre-Yves Chibon |
f1a2e7 |
work.
|
|
Pierre-Yves Chibon |
f1a2e7 |
"""
|
|
Pierre-Yves Chibon |
f1a2e7 |
@wraps(function)
|
|
Pierre-Yves Chibon |
f1a2e7 |
def decorated_function(*args, **kwargs):
|
|
Pierre-Yves Chibon |
f1a2e7 |
if flask.g.fas_user is None or not flask.g.fas_user.cla_done \
|
|
Pierre-Yves Chibon |
f1a2e7 |
or len(flask.g.fas_user.groups) < 1:
|
|
Pierre-Yves Chibon |
f1a2e7 |
# FAS-OpenID does not return cla_ groups
|
|
Pierre-Yves Chibon |
f1a2e7 |
return flask.redirect(flask.url_for('auth_login',
|
|
Pierre-Yves Chibon |
f1a2e7 |
next=flask.request.url))
|
|
Pierre-Yves Chibon |
f1a2e7 |
else:
|
|
Pierre-Yves Chibon |
f1a2e7 |
return function(*args, **kwargs)
|
|
Pierre-Yves Chibon |
f1a2e7 |
return decorated_function
|