Prepare Ipsilon patch for taiga POST
This commit is contained in:
parent
a9bffaf2e3
commit
56d363bbd5
2 changed files with 275 additions and 0 deletions
270
roles/ipsilon/files/openid_auth.py
Normal file
270
roles/ipsilon/files/openid_auth.py
Normal file
|
@ -0,0 +1,270 @@
|
|||
# Copyright (C) 2014 Ipsilon project Contributors, for license see COPYING
|
||||
|
||||
from ipsilon.providers.common import ProviderPageBase
|
||||
from ipsilon.providers.common import AuthenticationError, InvalidRequest
|
||||
from ipsilon.providers.openid.meta import XRDSHandler, UserXRDSHandler
|
||||
from ipsilon.providers.openid.meta import IDHandler
|
||||
from ipsilon.util.policy import Policy
|
||||
from ipsilon.util.trans import Transaction
|
||||
from ipsilon.util.user import UserSession
|
||||
|
||||
from openid.server.server import ProtocolError, EncodingError
|
||||
|
||||
import cherrypy
|
||||
import time
|
||||
import json
|
||||
|
||||
|
||||
class AuthenticateRequest(ProviderPageBase):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AuthenticateRequest, self).__init__(*args, **kwargs)
|
||||
self.stage = 'init'
|
||||
self.trans = None
|
||||
|
||||
def _preop(self, *args, **kwargs):
|
||||
try:
|
||||
# generate a new id or get current one
|
||||
self.trans = Transaction('openid', **kwargs)
|
||||
if self.trans.cookie.value != self.trans.provider:
|
||||
self.debug('Invalid transaction, %s != %s' % (
|
||||
self.trans.cookie.value, self.trans.provider))
|
||||
except Exception, e: # pylint: disable=broad-except
|
||||
raise cherrypy.HTTPRedirect('https://id.stg.fedoraproject.org/')
|
||||
self.debug('Transaction initialization failed: %s' % repr(e))
|
||||
raise cherrypy.HTTPError(400, 'Invalid transaction id')
|
||||
|
||||
def pre_GET(self, *args, **kwargs):
|
||||
self._preop(*args, **kwargs)
|
||||
|
||||
def pre_POST(self, *args, **kwargs):
|
||||
self._preop(*args, **kwargs)
|
||||
|
||||
def _get_form(self, *args):
|
||||
form = None
|
||||
if args is not None:
|
||||
first = args[0] if len(args) > 0 else None
|
||||
second = first[0] if len(first) > 0 else None
|
||||
if isinstance(second, dict):
|
||||
form = second.get('form', None)
|
||||
return form
|
||||
|
||||
def auth(self, *args, **kwargs):
|
||||
request = None
|
||||
form = self._get_form(args)
|
||||
try:
|
||||
request = self._parse_request(**kwargs)
|
||||
return self._openid_checks(request, form, **kwargs)
|
||||
except InvalidRequest, e:
|
||||
raise cherrypy.HTTPError(e.code, e.msg)
|
||||
except AuthenticationError, e:
|
||||
if request is None:
|
||||
raise cherrypy.HTTPError(e.code, e.msg)
|
||||
return self._respond(request.answer(False))
|
||||
|
||||
# get attributes, and apply policy mapping and filtering
|
||||
def _source_attributes(self, session):
|
||||
policy = Policy(self.cfg.default_attribute_mapping,
|
||||
self.cfg.default_allowed_attributes)
|
||||
userattrs = session.get_user_attrs()
|
||||
mappedattrs, _ = policy.map_attributes(userattrs)
|
||||
attributes = policy.filter_attributes(mappedattrs)
|
||||
self.debug('Filterd attributes: %s' % repr(attributes))
|
||||
return attributes
|
||||
|
||||
def _parse_request(self, **kwargs):
|
||||
request = None
|
||||
try:
|
||||
request = self.cfg.server.decodeRequest(kwargs)
|
||||
except ProtocolError, openid_error:
|
||||
self.debug('ProtocolError: %s' % openid_error)
|
||||
raise InvalidRequest('Invalid OpenID request', 400)
|
||||
|
||||
if request is None:
|
||||
self.debug('No request')
|
||||
raise cherrypy.HTTPRedirect(self.basepath)
|
||||
|
||||
return request
|
||||
|
||||
def _openid_checks(self, request, form, **kwargs):
|
||||
us = UserSession()
|
||||
user = us.get_user()
|
||||
immediate = False
|
||||
|
||||
self.debug('Mode: %s Stage: %s User: %s' % (
|
||||
kwargs['openid.mode'], self.stage, user.name))
|
||||
if kwargs.get('openid.mode', None) == 'checkid_setup':
|
||||
if user.is_anonymous:
|
||||
if self.stage == 'init':
|
||||
returl = '%s/openid/Continue?%s' % (
|
||||
self.basepath, self.trans.get_GET_arg())
|
||||
data = {'openid_stage': 'auth',
|
||||
'openid_request': json.dumps(kwargs),
|
||||
'login_return': returl,
|
||||
'login_target': request.trust_root}
|
||||
self.trans.store(data)
|
||||
redirect = '%s/login?%s' % (self.basepath,
|
||||
self.trans.get_GET_arg())
|
||||
self.debug('Redirecting: %s' % redirect)
|
||||
raise cherrypy.HTTPRedirect(redirect)
|
||||
else:
|
||||
raise AuthenticationError("unknown user", 401)
|
||||
|
||||
elif kwargs.get('openid.mode', None) == 'checkid_immediate':
|
||||
# This is immediate, so we need to assert or fail
|
||||
if user.is_anonymous:
|
||||
return self._respond(request.answer(False))
|
||||
|
||||
immediate = True
|
||||
|
||||
else:
|
||||
return self._respond(self.cfg.server.handleRequest(request))
|
||||
|
||||
# check if this is discovery or needs identity matching checks
|
||||
if not request.idSelect():
|
||||
idurl = self.cfg.identity_url_template % {'username': user.name}
|
||||
if request.identity != idurl:
|
||||
raise AuthenticationError("User ID mismatch!", 401)
|
||||
|
||||
# check if the relying party is trusted
|
||||
if request.trust_root in self.cfg.untrusted_roots:
|
||||
raise AuthenticationError("Untrusted Relying party", 401)
|
||||
|
||||
# if the party is explicitly whitelisted just respond
|
||||
if request.trust_root in self.cfg.trusted_roots:
|
||||
return self._respond(self._response(request, us))
|
||||
|
||||
allowroot = 'allow-%s' % request.trust_root
|
||||
|
||||
try:
|
||||
userdata = user.load_plugin_data(self.cfg.name)
|
||||
expiry = int(userdata[allowroot])
|
||||
except Exception, e: # pylint: disable=broad-except
|
||||
self.debug(e)
|
||||
expiry = 0
|
||||
if expiry > int(time.time()):
|
||||
self.debug("User has unexpired previous authorization")
|
||||
return self._respond(self._response(request, us))
|
||||
|
||||
if immediate:
|
||||
raise AuthenticationError("No consent for immediate", 401)
|
||||
|
||||
if self.stage == 'consent':
|
||||
if form is None:
|
||||
raise AuthenticationError("Unintelligible consent", 401)
|
||||
allow = form.get('decided_allow', False)
|
||||
if not allow:
|
||||
raise AuthenticationError("User declined", 401)
|
||||
try:
|
||||
days = int(form.get('remember_for_days', '0'))
|
||||
if days < 0 or days > 7:
|
||||
raise
|
||||
userdata = {allowroot: str(int(time.time()) + (days*86400))}
|
||||
user.save_plugin_data(self.cfg.name, userdata)
|
||||
except Exception, e: # pylint: disable=broad-except
|
||||
self.debug(e)
|
||||
days = 0
|
||||
|
||||
# all done we consent!
|
||||
return self._respond(self._response(request, us))
|
||||
|
||||
else:
|
||||
data = {'openid_stage': 'consent',
|
||||
'openid_request': json.dumps(kwargs)}
|
||||
self.trans.store(data)
|
||||
|
||||
# Add extension data to this dictionary
|
||||
ad = {
|
||||
"Trust Root": request.trust_root,
|
||||
}
|
||||
userattrs = self._source_attributes(us)
|
||||
for n, e in self.cfg.extensions.available().items():
|
||||
data = e.get_display_data(request, userattrs)
|
||||
self.debug('%s returned %s' % (n, repr(data)))
|
||||
for key, value in data.items():
|
||||
ad[self.cfg.mapping.display_name(key)] = value
|
||||
|
||||
context = {
|
||||
"title": 'Consent',
|
||||
"action": '%s/openid/Consent' % (self.basepath),
|
||||
"trustroot": request.trust_root,
|
||||
"username": user.name,
|
||||
"authz_details": ad,
|
||||
}
|
||||
context.update(dict((self.trans.get_POST_tuple(),)))
|
||||
return self._template('openid/consent_form.html', **context)
|
||||
|
||||
def _response(self, request, session):
|
||||
user = session.get_user()
|
||||
identity_url = self.cfg.identity_url_template % {'username': user.name}
|
||||
response = request.answer(
|
||||
True,
|
||||
identity=identity_url,
|
||||
claimed_id=identity_url
|
||||
)
|
||||
userattrs = self._source_attributes(session)
|
||||
for _, e in self.cfg.extensions.available().items():
|
||||
resp = e.get_response(request, userattrs)
|
||||
if resp is not None:
|
||||
response.addExtension(resp)
|
||||
return response
|
||||
|
||||
def _respond(self, response):
|
||||
try:
|
||||
self.debug('Response: %s' % response)
|
||||
webresponse = self.cfg.server.encodeResponse(response)
|
||||
cherrypy.response.headers.update(webresponse.headers)
|
||||
cherrypy.response.status = webresponse.code
|
||||
return webresponse.body
|
||||
except EncodingError, encoding_error:
|
||||
self.debug('Unable to respond because: %s' % encoding_error)
|
||||
cherrypy.response.headers = {
|
||||
'Content-Type': 'text/plain; charset=UTF-8'
|
||||
}
|
||||
cherrypy.response.status = 400
|
||||
return encoding_error.response.encodeToKVForm()
|
||||
|
||||
|
||||
class Continue(AuthenticateRequest):
|
||||
|
||||
def GET(self, *args, **kwargs):
|
||||
transdata = self.trans.retrieve()
|
||||
self.stage = transdata.get('openid_stage', None)
|
||||
openid_request = transdata.get('openid_request', None)
|
||||
if self.stage is None or openid_request is None:
|
||||
raise AuthenticationError("unknown state", 400)
|
||||
|
||||
kwargs = json.loads(openid_request)
|
||||
return self.auth(**kwargs)
|
||||
|
||||
|
||||
class Consent(AuthenticateRequest):
|
||||
|
||||
def POST(self, *args, **kwargs):
|
||||
transdata = self.trans.retrieve()
|
||||
self.stage = transdata.get('openid_stage', None)
|
||||
openid_request = transdata.get('openid_request', None)
|
||||
if self.stage is None or openid_request is None:
|
||||
raise AuthenticationError("unknown state", 400)
|
||||
|
||||
args = ({'form': kwargs},)
|
||||
kwargs = json.loads(openid_request)
|
||||
return self.auth(*args, **kwargs)
|
||||
|
||||
|
||||
class OpenID(AuthenticateRequest):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(OpenID, self).__init__(*args, **kwargs)
|
||||
self.XRDS = XRDSHandler(*args, **kwargs)
|
||||
self.yadis = UserXRDSHandler(*args, **kwargs)
|
||||
self.id = IDHandler(*args, **kwargs)
|
||||
self.Continue = Continue(*args, **kwargs)
|
||||
self.Consent = Consent(*args, **kwargs)
|
||||
self.trans = None
|
||||
|
||||
def GET(self, *args, **kwargs):
|
||||
return self.auth(**kwargs)
|
||||
|
||||
def POST(self, *args, **kwargs):
|
||||
return self.auth(**kwargs)
|
|
@ -24,6 +24,11 @@
|
|||
dest=/usr/lib/python2.7/site-packages/ipsilon/providers/openid/extensions/api.py
|
||||
owner=root group=root mode=0644
|
||||
|
||||
- name: Apply hotfix for taiga to get POST results
|
||||
copy: src=openid_auth.py
|
||||
dest=/usr/lib/python2.7/site-packages/ipsilon/providers/openid/auth.py
|
||||
owner=root group=root mode=0644
|
||||
|
||||
- name: copy ipsilon templates
|
||||
copy: src=templates/
|
||||
dest=/usr/share/ipsilon/templates-fedora
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue