def openid_server(req): """ This view is the actual OpenID server - running at the URL pointed to by the <link rel="openid.server"> tag. """ host = get_base_uri(req) try: # if we have django_openid_auth in applications directory # then we can use DjangoOpenIDStore from django_openid_auth.store import DjangoOpenIDStore store = DjangoOpenIDStore() except: # otherwise use FileOpenIDStore OPENID_FILESTORE = '/tmp/openid-filestore' from openid.store.filestore import FileOpenIDStore store = FileOpenIDStore(OPENID_FILESTORE) server = Server(store, op_endpoint="%s%s" % (host, reverse('openid-provider-root'))) # Clear AuthorizationInfo session var, if it is set if req.session.get('AuthorizationInfo', None): del req.session['AuthorizationInfo'] querydict = dict(req.REQUEST.items()) orequest = server.decodeRequest(querydict) if not orequest: orequest = req.session.get('OPENID_REQUEST', None) if not orequest: # not request, render info page: return render_to_response('openid_provider/server.html', {'host': host,}, context_instance=RequestContext(req)) else: # remove session stored data: del req.session['OPENID_REQUEST'] if orequest.mode in ("checkid_immediate", "checkid_setup"): if not req.user.is_authenticated(): return landing_page(req, orequest) #openid = openid_is_authorized(req, orequest.identity, orequest.trust_root) #if openid is not None: oresponse = orequest.answer(True, identity="%s%s" % ( host, reverse('openid-provider-identity', args=[req.user.username]))) fullname = "%s %s" % (req.user.first_name, req.user.last_name) if fullname == " ": fullname = req.user.username if fullname.strip().find(" ") == -1: fullname = "%s %s" % (fullname, fullname) sreg_data = { 'fullname': fullname , 'email': req.user.email, 'nickname': req.user.username, } sreg_req = sreg.SRegRequest.fromOpenIDRequest(orequest) sreg_resp = sreg.SRegResponse.extractResponse(sreg_req, sreg_data) oresponse.addExtension(sreg_resp) #elif orequest.immediate: # raise Exception('checkid_immediate mode not supported') #else: # req.session['OPENID_REQUEST'] = orequest # return HttpResponseRedirect(reverse('openid-provider-decide')) else: oresponse = server.handleRequest(orequest) webresponse = server.encodeResponse(oresponse) return django_response(webresponse)
def display_response(request, openid_response): """ Display an OpenID response. Errors will be displayed directly to the user; successful responses and other protocol-level messages will be sent using the proper mechanism (i.e., direct response, redirection, etc.). """ s = Server(OpenIDStore(), request.build_absolute_uri(reverse(endpoint))) # Encode the response into something that is renderable. try: webresponse = s.encodeResponse(openid_response) except EncodingError, why: # If it couldn't be encoded, display an error. text = why.response.encodeToKVForm() return direct_to_template( request, 'endpoint.html', {'error': cgi.escape(text)})
def prep_response(request, orequest, oresponse, server=None): # Convert a webresponse from the OpenID library in to a Django HttpResponse if not server: server = Server(get_store(request), op_endpoint=request.build_absolute_uri( reverse('openid-provider-root'))) webresponse = server.encodeResponse(oresponse) if webresponse.code == 200 and orequest.mode in BROWSER_REQUEST_MODES: response = render_to_response('openid_provider/response.html', { 'body': webresponse.body, }, context_instance=RequestContext(request)) logger.debug('rendering browser response') else: response = HttpResponse(webresponse.body) response.status_code = webresponse.code for key, value in webresponse.headers.items(): response[key] = value logger.debug('rendering raw response') return response
def prep_response(request, orequest, oresponse, server=None): # Convert a webresponse from the OpenID library in to a Django HttpResponse if not server: server = Server(get_store(request), op_endpoint=request.build_absolute_uri( reverse('openid-provider-root'))) webresponse = server.encodeResponse(oresponse) if webresponse.code == 200 and orequest.mode in BROWSER_REQUEST_MODES: response = render_to_response('openid_provider/response.html', { 'body': webresponse.body, }, context_instance=RequestContext(request)) logger.debug('rendering browser response') else: response = HttpResponse(webresponse.body) response.status_code = webresponse.code for key, value in webresponse.headers.items(): response[key] = value logger.debug('rendering raw response') return response
def index(): oidstore = Web2pyStore(db) cache.ram('oid_cleanup', lambda: oidstore.cleanup(), time_expire=25200) #call cleanup approx. every 7h oserver = Server(oidstore, OID_SERVER) orequest = oserver.decodeRequest(request.vars) #if another controller asked to cancel this authentication request do so if session.oid_cancel: session.oid_cancel = None return redirect(orequest.getCancelURL()) user_is_logged_in = auth.is_logged_in() user_owns_id = user_is_logged_in and auth.user['nickname'] == orequest.identity.split('/').pop().lower() #lazy eval user_trusts_root = user_is_logged_in and _userTrustsRoot(orequest.trust_root) #lazy eval if orequest.mode in ['checkid_immediate', 'checkid_setup']: if user_is_logged_in and user_owns_id and user_trusts_root: resp = orequest.answer(True) elif orequest.immediate: resp = orequest.answer(False) else: #populate oid session with details as required by other controllers such as the url to #redirect to after logon or after approving data transfer to the relying party. session.oid_url = URL(r=request, args=request.args, vars=request.vars) #redirect user to authenticate herself or to authorise request if user_is_logged_in: session.oid_orequest = orequest session.oid_user_owns_id = user_owns_id return redirect(URL(r=request, c='auth', f='data')) else: return redirect(URL(r=request, c='auth', f='user', args=['login'])) else: #let the library handle it resp = oserver.handleRequest(orequest) oresp = oserver.encodeResponse(resp) if oresp.code==200: return oresp.body else: return redirect(oresp.headers['location'], oresp.code)
class OpenIDProvider(object): # OpenID service type URIs, listed in order of preference. The # ordering of this list affects yadis and XRI service discovery. openid_type_uris = [ #discover.OPENID_IDP_2_0_TYPE, discover.OPENID_2_0_TYPE, discover.OPENID_1_1_TYPE, discover.OPENID_1_0_TYPE, sreg.ns_uri, ] def __init__(self, request): self.request = request self.openid_server = Server(SFOpenIDStore(self.request.db_session), request.route_url('openid_provider')) userid = authenticated_userid(request) self.auth_userid = userid or request.session.get('auth_userid') if self.auth_userid: query = self.request.db_session.query(UserProfile) self.auth_user = query.get(self.auth_userid) else: self.auth_user = None def process(self, request_params): logger.debug('Processing openid request: %s', request_params) if request_params.get('openid.identity'): home_url = self.request.route_url('home') if request_params['openid.identity'] == request_params['openid.claimed_id'] and \ request_params['openid.identity'] == home_url: username = self.request.user and self.request.user.username or \ self.auth_userid if username: username = username.lower() user_url = self.request.route_url('user_profile', username=username) request_params['openid.identity'] = user_url request_params['openid.claimed_id'] = user_url try: openid_request = self.openid_server.decodeRequest(request_params) # MalformedReturnURL, UntrustedReturnURL, etc. except ProtocolError as e: logger.info( 'Unable to decode request: %s %s', e.__class__, e.openid_message ) return '' logger.debug('Decoded request: %s', openid_request) if openid_request is None: return '' if openid_request.mode in ["checkid_immediate", "checkid_setup"]: if self.request.user or self.auth_userid: openid_response = self.handleCheckIDRequest(openid_request) else: # If the user has not logged in yet, stash the OpenID # consuming site request (if there isn't one already) and # send them to the login view. The openid_tween will take # care of sending them back to the OpenID consuming site. rp_dict = dict(request_params.items()) self.request.session['openid_request'] = rp_dict self.request.session.save() return HTTPFound(location=self.request.route_url('login')) else: openid_response = self.openid_server.handleRequest(openid_request) logger.debug('Decoded response: %s', openid_response) encoded_response = self.openid_server.encodeResponse(openid_response) if 'location' in encoded_response.headers: response = HTTPFound(location=encoded_response.headers['location']) return response return encoded_response.body def get(self): return self.process(self.request.GET) def post(self): if self.request.method != "POST": return HTTPMethodNotAllowed() return self.process(self.request.POST) def approved(self, request, identifier=None): response = request.answer(True, identity=identifier) self.addSRegResponse(request, response) return response def handleCheckIDRequest(self, request): is_authorized = self.isAuthorized(request.identity, request.trust_root) if is_authorized: return self.approved(request, request.identity) else: return request.answer(False, identity=request.identity) def isAuthorized(self, identity_url, trust_root): if not self.auth_userid: return False self.request.registry.notify(CheckIDAuthorized(self.request, self.auth_user)) # TODO: prompt user for authorization return True def addSRegResponse(self, request, response): sreg_req = sreg.SRegRequest.fromOpenIDRequest(request) sreg_data = dict([ (fname, None) for fname in sreg.data_fields ]) sreg_data['fullname'] = '%s %s' % (self.auth_user.first_name, self.auth_user.last_name) sreg_data['nickname'] = self.auth_userid sreg_data['email'] = self.auth_user.email sreg_resp = sreg.SRegResponse.extractResponse(sreg_req, sreg_data) response.addExtension(sreg_resp) def identity(self): query = self.request.db_session.query(UserProfile) target_username = self.request.matchdict['username'] target_user = query.get(target_username) if target_user is None: raise HTTPNotFound() return { 'username': target_user.username.lower(), }
class StubOpenIDProvider(HTTPFetcher): def __init__(self, base_url): self.store = MemoryStore() self.identity_url = base_url + 'identity' self.localid_url = base_url + 'localid' self.endpoint_url = base_url + 'endpoint' self.server = Server(self.store, self.endpoint_url) self.last_request = None self.type_uris = ['http://specs.openid.net/auth/2.0/signon'] def fetch(self, url, body=None, headers=None): if url == self.identity_url: # Serve an XRDS document directly, pointing at our endpoint. type_uris = ['<Type>%s</Type>' % uri for uri in self.type_uris] return HTTPResponse( url, 200, {'content-type': 'application/xrds+xml'}, """\ <?xml version="1.0"?> <xrds:XRDS xmlns="xri://$xrd*($v*2.0)" xmlns:xrds="xri://$xrds"> <XRD> <Service priority="0"> %s <URI>%s</URI> <LocalID>%s</LocalID> </Service> </XRD> </xrds:XRDS> """ % ('\n'.join(type_uris), self.endpoint_url, self.localid_url)) elif url.startswith(self.endpoint_url): # Gather query parameters query = {} if '?' in url: query.update(cgi.parse_qsl(url.split('?', 1)[1])) if body is not None: query.update(cgi.parse_qsl(body)) self.last_request = self.server.decodeRequest(query) # The browser based requests should not be handled through # the fetcher interface. assert self.last_request.mode not in BROWSER_REQUEST_MODES response = self.server.handleRequest(self.last_request) webresponse = self.server.encodeResponse(response) return HTTPResponse(url, webresponse.code, webresponse.headers, webresponse.body) else: raise HTTPFetchingError('unknown URL %s' % url) def parseFormPost(self, content): """Parse an HTML form post to create an OpenID request.""" # Hack to make the javascript XML compliant ... content = content.replace('i < elements.length', 'i < elements.length') tree = ET.XML(content) form = tree.find('.//form') assert form is not None, 'No form in document' assert form.get('action') == self.endpoint_url, ( 'Form posts to %s instead of %s' % (form.get('action'), self.endpoint_url)) query = {} for input in form.findall('input'): if input.get('type') != 'hidden': continue query[input.get('name').encode('UTF-8')] = \ input.get('value').encode('UTF-8') self.last_request = self.server.decodeRequest(query) return self.last_request
def openid_server(req): """ This view is the actual OpenID server - running at the URL pointed to by the <link rel="openid.server"> tag. """ host = get_base_uri(req) try: # if we have django_openid_auth in applications directory # then we can use DjangoOpenIDStore from django_openid_auth.store import DjangoOpenIDStore store = DjangoOpenIDStore() except: # otherwise use FileOpenIDStore OPENID_FILESTORE = '/tmp/openid-filestore' from openid.store.filestore import FileOpenIDStore store = FileOpenIDStore(OPENID_FILESTORE) server = Server(store, op_endpoint="%s%s" % (host, reverse('openid-provider-root'))) # Clear AuthorizationInfo session var, if it is set if req.session.get('AuthorizationInfo', None): del req.session['AuthorizationInfo'] querydict = dict(req.REQUEST.items()) try: orequest = server.decodeRequest(querydict) except: L.exception("Request decode failed") orequest = None if not orequest: orequest = req.session.get('OPENID_REQUEST', None) if not orequest: # not request, render info page: return render_to_response('openid_provider/server.html', { 'host': host, }, context_instance=RequestContext(req)) else: # remove session stored data: del req.session['OPENID_REQUEST'] if orequest.mode in ("checkid_immediate", "checkid_setup"): if not req.user.is_authenticated(): return landing_page(req, orequest) openid = openid_is_authorized(req, orequest.identity, orequest.trust_root) if openid is not None: oresponse = orequest.answer( True, identity="%s%s" % (host, reverse('openid-provider-identity', args=[openid.openid]))) sreg_data = {'nickname': req.user.username} sreg_req = sreg.SRegRequest.fromOpenIDRequest(orequest) sreg_resp = sreg.SRegResponse.extractResponse(sreg_req, sreg_data) oresponse.addExtension(sreg_resp) elif orequest.immediate: raise Exception('checkid_immediate mode not supported') else: req.session['OPENID_REQUEST'] = orequest return HttpResponseRedirect(reverse('openid-provider-decide')) else: oresponse = server.handleRequest(orequest) webresponse = server.encodeResponse(oresponse) return django_response(webresponse)
def openid_server(request): """ This view is the actual OpenID server - running at the URL pointed to by the <link rel="openid.server"> tag. """ logger.debug('server request %s: %s', request.method, request.POST or request.GET) server = Server(get_store(request), op_endpoint=request.build_absolute_uri(reverse('openid-provider-root'))) if not request.is_secure(): # if request is not secure allow only encrypted association sessions server.negotiator = encrypted_negotiator # Clear AuthorizationInfo session var, if it is set if request.session.get('AuthorizationInfo', None): del request.session['AuthorizationInfo'] querydict = dict(request.POST.items()) orequest = server.decodeRequest(querydict) if not orequest: orequest = request.session.get('OPENID_REQUEST', None) if orequest: # remove session stored data: del request.session['OPENID_REQUEST'] else: # not request, render info page: data = { 'host': request.build_absolute_uri('/'), 'xrds_location': request.build_absolute_uri( reverse('openid-provider-xrds')), } # Return empty string return HttpResponse("", content_type="text/plain") if orequest.mode in BROWSER_REQUEST_MODES: if not request.user.is_authenticated: logger.debug('no local authentication, sending landing page') return landing_page(request, orequest) openid = openid_is_authorized(request, orequest.identity, orequest.trust_root) if openid is not None: id_url = request.build_absolute_uri( reverse('openid-provider-identity', args=[openid.openid])) oresponse = orequest.answer(True, identity=id_url) logger.debug('orequest.answer(True, identity="%s")', id_url) elif orequest.immediate: logger.debug('checkid_immediate mode not supported') raise Exception('checkid_immediate mode not supported') else: request.session['OPENID_REQUEST'] = orequest logger.debug('redirecting to decide page') return HttpResponseRedirect(reverse('openid-provider-decide')) else: oresponse = server.handleRequest(orequest) if request.user.is_authenticated: add_sreg_data(request, orequest, oresponse) if conf.AX_EXTENSION: add_ax_data(request, orequest, oresponse) # Convert a webresponse from the OpenID library in to a Django HttpResponse webresponse = server.encodeResponse(oresponse) if webresponse.code == 200 and orequest.mode in BROWSER_REQUEST_MODES: response = render(request, 'openid_provider/response.html', { 'body': webresponse.body, }) logger.debug('rendering browser response') else: response = HttpResponse(webresponse.body) response.status_code = webresponse.code for key, value in webresponse.headers.items(): response[key] = value logger.debug('rendering raw response') return response
class StubOpenIDProvider(HTTPFetcher): def __init__(self, base_url): self.store = MemoryStore() self.identity_url = base_url + 'identity' self.localid_url = base_url + 'localid' self.endpoint_url = base_url + 'endpoint' self.server = Server(self.store, self.endpoint_url) self.last_request = None self.type_uris = ['http://specs.openid.net/auth/2.0/signon'] def fetch(self, url, body=None, headers=None): if url == self.identity_url: # Serve an XRDS document directly, pointing at our endpoint. type_uris = ['<Type>%s</Type>' % uri for uri in self.type_uris] return HTTPResponse( url, 200, {'content-type': 'application/xrds+xml'}, """\ <?xml version="1.0"?> <xrds:XRDS xmlns="xri://$xrd*($v*2.0)" xmlns:xrds="xri://$xrds"> <XRD> <Service priority="0"> %s <URI>%s</URI> <LocalID>%s</LocalID> </Service> </XRD> </xrds:XRDS> """ % ('\n'.join(type_uris), self.endpoint_url, self.localid_url)) elif url.startswith(self.endpoint_url): # Gather query parameters query = {} if '?' in url: query.update(cgi.parse_qsl(url.split('?', 1)[1])) if body is not None: query.update(cgi.parse_qsl(body)) self.last_request = self.server.decodeRequest(query) # The browser based requests should not be handled through # the fetcher interface. assert self.last_request.mode not in BROWSER_REQUEST_MODES response = self.server.handleRequest(self.last_request) webresponse = self.server.encodeResponse(response) return HTTPResponse(url, webresponse.code, webresponse.headers, webresponse.body) else: raise HTTPFetchingError('unknown URL %s' % url) def parseFormPost(self, content): """Parse an HTML form post to create an OpenID request.""" # Hack to make the javascript XML compliant ... content = content.replace('i < elements.length', 'i < elements.length') tree = ET.XML(content) form = tree.find('.//form') assert form is not None, 'No form in document' assert form.get('action') == self.endpoint_url, ( 'Form posts to %s instead of %s' % (form.get('action'), self.endpoint_url)) query = {} for input in form.findall('input'): if input.get('type') != 'hidden': continue query[input.get('name').encode('UTF-8')] = \ input.get('value').encode('UTF-8') self.last_request = self.server.decodeRequest(query) return self.last_request
class OpenIDMixin: """A mixin with OpenID helper methods.""" openid_request = None def __init__(self, context, request): super(OpenIDMixin, self).__init__(context, request) self.server_url = get_server_url() self.openid_server = Server(openid_store, self.server_url) @property def user_identity_url(self): return ITestOpenIDPersistentIdentity(self.account).openid_identity_url def isIdentityOwner(self): """Return True if the user can authenticate as the given ID.""" assert self.account is not None, "user should be logged in by now." return (self.openid_request.idSelect() or self.openid_request.identity == self.user_identity_url) @cachedproperty def openid_parameters(self): """A dictionary of OpenID query parameters from request.""" query = {} for key, value in self.request.form.items(): if key.startswith('openid.'): # All OpenID query args are supposed to be ASCII. query[key.encode('US-ASCII')] = value.encode('US-ASCII') return query def getSession(self): """Get the session data container that stores the OpenID request.""" if IUnauthenticatedPrincipal.providedBy(self.request.principal): # A dance to assert that we want to break the rules about no # unauthenticated sessions. Only after this next line is it # safe to set session values. allowUnauthenticatedSession(self.request, duration=timedelta(minutes=60)) return ISession(self.request)[SESSION_PKG_KEY] def restoreRequestFromSession(self): """Get the OpenIDRequest from our session.""" session = self.getSession() cache = get_property_cache(self) try: cache.openid_parameters = session[OPENID_REQUEST_SESSION_KEY] except KeyError: raise UnexpectedFormData("No OpenID request in session") # Decode the request parameters and create the request object. self.openid_request = self.openid_server.decodeRequest( self.openid_parameters) assert zisinstance( self.openid_request, CheckIDRequest), ('Invalid OpenIDRequest in session') def saveRequestInSession(self): """Save the OpenIDRequest in our session.""" query = self.openid_parameters assert query.get('openid.mode') == 'checkid_setup', ( 'Can only serialise checkid_setup OpenID requests') session = self.getSession() # If this was meant for use in production we'd have to use a nonce # as the key when storing the openid request in the session, but as # it's meant to run only on development instances we can simplify # things a bit by storing the openid request using a well known key. session[OPENID_REQUEST_SESSION_KEY] = query def renderOpenIDResponse(self, openid_response): """Return a web-suitable response constructed from openid_response.""" webresponse = self.openid_server.encodeResponse(openid_response) response = self.request.response response.setStatus(webresponse.code) # encodeResponse doesn't generate a content-type, help it out if (webresponse.code == 200 and webresponse.body and openid_response.whichEncoding() == ENCODE_HTML_FORM): response.setHeader('content-type', 'text/html') for header, value in webresponse.headers.items(): response.setHeader(header, value) return webresponse.body def createPositiveResponse(self): """Create a positive assertion OpenIDResponse. This method should be called to create the response to successful checkid requests. If the trust root for the request is in openid_sreg_trustroots, then additional user information is included with the response. """ assert self.account is not None, ( 'Must be logged in for positive OpenID response') assert self.openid_request is not None, ( 'No OpenID request to respond to.') if not self.isIdentityOwner(): return self.createFailedResponse() if self.openid_request.idSelect(): response = self.openid_request.answer( True, identity=self.user_identity_url) else: response = self.openid_request.answer(True) person = IPerson(self.account) sreg_fields = dict(nickname=person.name, email=person.preferredemail.email, fullname=self.account.displayname) sreg_request = SRegRequest.fromOpenIDRequest(self.openid_request) sreg_response = SRegResponse.extractResponse(sreg_request, sreg_fields) response.addExtension(sreg_response) return response def createFailedResponse(self): """Create a failed assertion OpenIDResponse. This method should be called to create the response to unsuccessful checkid requests. """ assert self.openid_request is not None, ( 'No OpenID request to respond to.') response = self.openid_request.answer(False, self.server_url) return response
def openid_server(request): """ This view is the actual OpenID server - running at the URL pointed to by the <link rel="openid.server"> tag. """ logger.debug('server request %s: %s', request.method, request.POST or request.GET) server = Server(get_store(request), op_endpoint=request.build_absolute_uri( reverse('openid-provider-root'))) if not request.is_secure(): # if request is not secure allow only encrypted association sessions server.negotiator = encrypted_negotiator # Clear AuthorizationInfo session var, if it is set if request.session.get('AuthorizationInfo', None): del request.session['AuthorizationInfo'] querydict = dict(request.REQUEST.items()) orequest = server.decodeRequest(querydict) if not orequest: orequest = request.session.get('OPENID_REQUEST', None) if orequest: # remove session stored data: del request.session['OPENID_REQUEST'] else: # not request, render info page: data = { 'host': request.build_absolute_uri('/'), 'xrds_location': request.build_absolute_uri(reverse('openid-provider-xrds')), } logger.debug('invalid request, sending info: %s', data) return render_to_response('openid_provider/server.html', data, context_instance=RequestContext(request)) if orequest.mode in BROWSER_REQUEST_MODES: if not request.user.is_authenticated(): #return HttpResponse(orequest.return_to) logger.debug('no local authentication, sending landing page') return landing_page(request, orequest) openid = openid_is_authorized(request, orequest.identity, orequest.trust_root) if openid is not None: id_url = request.build_absolute_uri( reverse('openid-provider-identity', args=[openid.openid])) oresponse = orequest.answer(True, identity=id_url) logger.debug('orequest.answer(True, identity="%s")', id_url) elif orequest.immediate: logger.debug('checkid_immediate mode not supported') raise Exception('checkid_immediate mode not supported') else: request.session['OPENID_REQUEST'] = orequest logger.debug('redirecting to decide page') return HttpResponseRedirect(reverse('openid-provider-decide')) else: oresponse = server.handleRequest(orequest) if request.user.is_authenticated(): add_sreg_data(request, orequest, oresponse) if conf.AX_EXTENSION: add_ax_data(request, orequest, oresponse) # Convert a webresponse from the OpenID library in to a Django HttpResponse webresponse = server.encodeResponse(oresponse) if webresponse.code == 200 and orequest.mode in BROWSER_REQUEST_MODES: response = render_to_response('openid_provider/response.html', { 'body': webresponse.body, }, context_instance=RequestContext(request)) logger.debug('rendering browser response') else: response = HttpResponse(webresponse.body) response.status_code = webresponse.code for key, value in webresponse.headers.items(): response[key] = value logger.debug('rendering raw response') return response
class OpenIDMixin: """A mixin with OpenID helper methods.""" openid_request = None def __init__(self, context, request): super(OpenIDMixin, self).__init__(context, request) self.server_url = get_server_url() self.openid_server = Server(openid_store, self.server_url) @property def user_identity_url(self): return ITestOpenIDPersistentIdentity(self.account).openid_identity_url def isIdentityOwner(self): """Return True if the user can authenticate as the given ID.""" assert self.account is not None, "user should be logged in by now." return (self.openid_request.idSelect() or self.openid_request.identity == self.user_identity_url) @cachedproperty def openid_parameters(self): """A dictionary of OpenID query parameters from request.""" query = {} for key, value in self.request.form.items(): if key.startswith('openid.'): # All OpenID query args are supposed to be ASCII. query[key.encode('US-ASCII')] = value.encode('US-ASCII') return query def getSession(self): """Get the session data container that stores the OpenID request.""" if IUnauthenticatedPrincipal.providedBy(self.request.principal): # A dance to assert that we want to break the rules about no # unauthenticated sessions. Only after this next line is it # safe to set session values. allowUnauthenticatedSession( self.request, duration=timedelta(minutes=60)) return ISession(self.request)[SESSION_PKG_KEY] def restoreRequestFromSession(self): """Get the OpenIDRequest from our session.""" session = self.getSession() cache = get_property_cache(self) try: cache.openid_parameters = session[OPENID_REQUEST_SESSION_KEY] except KeyError: raise UnexpectedFormData("No OpenID request in session") # Decode the request parameters and create the request object. self.openid_request = self.openid_server.decodeRequest( self.openid_parameters) assert zisinstance(self.openid_request, CheckIDRequest), ( 'Invalid OpenIDRequest in session') def saveRequestInSession(self): """Save the OpenIDRequest in our session.""" query = self.openid_parameters assert query.get('openid.mode') == 'checkid_setup', ( 'Can only serialise checkid_setup OpenID requests') session = self.getSession() # If this was meant for use in production we'd have to use a nonce # as the key when storing the openid request in the session, but as # it's meant to run only on development instances we can simplify # things a bit by storing the openid request using a well known key. session[OPENID_REQUEST_SESSION_KEY] = query def renderOpenIDResponse(self, openid_response): """Return a web-suitable response constructed from openid_response.""" webresponse = self.openid_server.encodeResponse(openid_response) response = self.request.response response.setStatus(webresponse.code) # encodeResponse doesn't generate a content-type, help it out if (webresponse.code == 200 and webresponse.body and openid_response.whichEncoding() == ENCODE_HTML_FORM): response.setHeader('content-type', 'text/html') for header, value in webresponse.headers.items(): response.setHeader(header, value) return webresponse.body def createPositiveResponse(self): """Create a positive assertion OpenIDResponse. This method should be called to create the response to successful checkid requests. If the trust root for the request is in openid_sreg_trustroots, then additional user information is included with the response. """ assert self.account is not None, ( 'Must be logged in for positive OpenID response') assert self.openid_request is not None, ( 'No OpenID request to respond to.') if not self.isIdentityOwner(): return self.createFailedResponse() if self.openid_request.idSelect(): response = self.openid_request.answer( True, identity=self.user_identity_url) else: response = self.openid_request.answer(True) person = IPerson(self.account) sreg_fields = dict( nickname=person.name, email=person.preferredemail.email, fullname=self.account.displayname) sreg_request = SRegRequest.fromOpenIDRequest(self.openid_request) sreg_response = SRegResponse.extractResponse( sreg_request, sreg_fields) response.addExtension(sreg_response) return response def createFailedResponse(self): """Create a failed assertion OpenIDResponse. This method should be called to create the response to unsuccessful checkid requests. """ assert self.openid_request is not None, ( 'No OpenID request to respond to.') response = self.openid_request.answer(False, self.server_url) return response
class OpenIDProvider(object): # OpenID service type URIs, listed in order of preference. The # ordering of this list affects yadis and XRI service discovery. openid_type_uris = [ #discover.OPENID_IDP_2_0_TYPE, discover.OPENID_2_0_TYPE, discover.OPENID_1_1_TYPE, discover.OPENID_1_0_TYPE, sreg.ns_uri, ] def __init__(self, request): self.request = request self.openid_server = Server(SFOpenIDStore(self.request.db_session), request.route_url('openid_provider')) userid = authenticated_userid(request) self.auth_userid = userid or request.session.get('auth_userid') if self.auth_userid: query = self.request.db_session.query(UserProfile) self.auth_user = query.get(self.auth_userid) else: self.auth_user = None def process(self, request_params): logger.debug('Processing openid request: %s', request_params) if request_params.get('openid.identity'): home_url = self.request.route_url('home') if request_params['openid.identity'] == request_params['openid.claimed_id'] and \ request_params['openid.identity'] == home_url: username = self.request.user and self.request.user.username or \ self.auth_userid if username: username = username.lower() user_url = self.request.route_url('user_profile', username=username) request_params['openid.identity'] = user_url request_params['openid.claimed_id'] = user_url try: openid_request = self.openid_server.decodeRequest(request_params) # MalformedReturnURL, UntrustedReturnURL, etc. except ProtocolError as e: logger.info('Unable to decode request: %s %s', e.__class__, e.openid_message) return '' logger.debug('Decoded request: %s', openid_request) if openid_request is None: return '' if openid_request.mode in ["checkid_immediate", "checkid_setup"]: if self.request.user or self.auth_userid: openid_response = self.handleCheckIDRequest(openid_request) else: # If the user has not logged in yet, stash the OpenID # consuming site request (if there isn't one already) and # send them to the login view. The openid_tween will take # care of sending them back to the OpenID consuming site. rp_dict = dict(request_params.items()) self.request.session['openid_request'] = rp_dict self.request.session.save() return HTTPFound(location=self.request.route_url('login')) else: openid_response = self.openid_server.handleRequest(openid_request) logger.debug('Decoded response: %s', openid_response) encoded_response = self.openid_server.encodeResponse(openid_response) if 'location' in encoded_response.headers: response = HTTPFound(location=encoded_response.headers['location']) return response return encoded_response.body def get(self): return self.process(self.request.GET) def post(self): if self.request.method != "POST": return HTTPMethodNotAllowed() return self.process(self.request.POST) def approved(self, request, identifier=None): response = request.answer(True, identity=identifier) self.addSRegResponse(request, response) return response def handleCheckIDRequest(self, request): is_authorized = self.isAuthorized(request.identity, request.trust_root) if is_authorized: return self.approved(request, request.identity) else: return request.answer(False, identity=request.identity) def isAuthorized(self, identity_url, trust_root): if not self.auth_userid: return False self.request.registry.notify( CheckIDAuthorized(self.request, self.auth_user)) # TODO: prompt user for authorization return True def addSRegResponse(self, request, response): sreg_req = sreg.SRegRequest.fromOpenIDRequest(request) sreg_data = dict([(fname, None) for fname in sreg.data_fields]) sreg_data['fullname'] = '%s %s' % (self.auth_user.first_name, self.auth_user.last_name) sreg_data['nickname'] = self.auth_userid sreg_data['email'] = self.auth_user.email sreg_resp = sreg.SRegResponse.extractResponse(sreg_req, sreg_data) response.addExtension(sreg_resp) def identity(self): query = self.request.db_session.query(UserProfile) target_username = self.request.matchdict['username'] target_user = query.get(target_username) if target_user is None: raise HTTPNotFound() return { 'username': target_user.username.lower(), }