from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render_to_response from models import * OPENID_FILESTORE = '/tmp/openid-filestore' from openid.store.filestore import FileOpenIDStore from openid.server.server import Server from idproxynet.idproxy.views import yhash_from_cookie from idproxynet import nonceprotect import datetime, pickle def openid_server(req): """ This view is the actual OpenID server - running at the URL pointed to by the tag. """ server = Server(FileOpenIDStore(OPENID_FILESTORE)) # 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: return HttpResponse("This is an OpenID server") if orequest.mode in ("checkid_immediate", "checkid_setup"): if openid_is_authorized(req, orequest.identity, orequest.trust_root): oresponse = orequest.answer(True) elif orequest.immediate: oresponse = orequest.answer(False, 'http://idproxy.net/openid/server/') else: return openid_decide_page(req, orequest) else: oresponse = server.handleRequest(orequest) webresponse = server.encodeResponse(oresponse) return django_response(webresponse) def django_response(webresponse): "Convert a webresponse from the OpenID library in to a Django HttpResponse" response = HttpResponse(webresponse.body) response.status_code = webresponse.code for key, value in webresponse.headers.items(): response[key] = value return response def pending_request(req, id): """ This page allows users to continue a pending request - i.e. one that was stashed away because they weren't authenticated with the site. """ # Allows user to retry an OpenID response stashed in PendingRequest table yhash = yhash_from_cookie(req) if not yhash: return error_page(req, "You are not signed in") try: yahoouser = YahooUser.objects.get(yahoo_hash = yhash) except YahooUser.DoesNotExist: return error_page(req, "You are signed in as an invalid user") try: pendingrequest = PendingRequest.objects.get( id = id, openid__user = yahoouser, ) except PendingRequest.DoesNotExist: return error_page(req, "Pending request does not exist") orequest = pendingrequest.unpickled() # If they already trust the trust_root, log them straight in after deleting # the pending request if openid_is_authorized(req, orequest.identity, orequest.trust_root): pendingrequest.delete() oresponse = orequest.answer(True) server = Server(FileOpenIDStore(OPENID_FILESTORE)) webresponse = server.encodeResponse(oresponse) return django_response(webresponse) # Show them the decide page return openid_decide_page(req, orequest, pendingrequest.id) def openid_decide_page(req, orequest, pending_id = None): """ The page that asks the user if they really want to sign in to the site, and lets them add the consumer to their trusted whitelist. """ # If user is logged in, ask if they want to trust this trust_root # If they are NOT logged in, show the landing page yhash = yhash_from_cookie(req) if not yhash: return landing_page(req, orequest) try: yahoouser = YahooUser.objects.get(yahoo_hash = yhash) except YahooUser.DoesNotExist: return error_page(req, "You are signed in as an invalid user") # Check that the user owns the requested identity subdomain = orequest.identity.replace('http://', '').replace( '.idproxy.net/', '') try: yahoouser.openid_set.get(subdomain = subdomain) except OpenId.DoesNotExist: return error_page(req, "You are signed in to idproxy.net but do " + \ "not own the requested OpenID") # They are logged in - ask if they want to trust this root req.session['orequest'] = orequest return render_to_response('decide.html', { 'title': 'Trust this site?', 'djnonce': nonceprotect.new_nonce_for_path_and_yhash('/decide/', yhash), 'trust_root': orequest.trust_root, 'identity': orequest.identity, 'yahoouser': yahoouser, 'pending_id': pending_id, }) import pprint from cgi import escape return HttpResponse( ("This is the page where you decide on identity %s page %s" % ( orequest.identity, orequest.trust_root )) + escape(pprint.pformat(orequest.__dict__.items())) ) def error_page(req, msg): return render_to_response('error.html', { 'title': 'Error', 'msg': msg, }) def openid_decide_post(req): """ The user has submitted the openid_decide_page """ assert req.POST, "POST required for this page" orequest = req.session.get('orequest', None) assert orequest, "Should have orequest stashed in their session" yhash = yhash_from_cookie(req) if not yhash: return error_page(req, "You are not signed in") try: yahoouser = YahooUser.objects.get(yahoo_hash = yhash) except YahooUser.DoesNotExist: return error_page(req, "You are signed in as an invalid user") try: nonceprotect.validate_nonce_against_path_and_yhash( req.POST.get('djnonce', ''), req.path, yhash ) except nonceprotect.BadNonce: return error_page(req, "Your form has expired; hit back and try again") # Ensure that auth_user owns the requested domain try: subdomain = orequest.identity.replace( 'http://', '').replace('.idproxy.net/', '') openid = yahoouser.openid_set.get(subdomain=subdomain) except OpenId.DoesNotExist: return error_page(req, "You do not own that OpenID") # If there was an associated pending request, delete it if req.POST.get('pending_id', None): PendingRequest.objects.get( id = req.POST.get('pending_id'), openid = openid ).delete() if req.POST.get('cancel', None): # Cancel their request del req.session['orequest'] return HttpResponseRedirect(orequest.getCancelURL()) if req.POST.get('always', None): # Save this to their trust roots openid.trustedroot_set.create( trust_root = orequest.trust_root, created = datetime.datetime.now() ) # Redirect and say yes oresponse = orequest.answer(True) server = Server(FileOpenIDStore(OPENID_FILESTORE)) webresponse = server.encodeResponse(oresponse) # Add this to their history OpenIdHistory.objects.create( openid = openid, trust_root = orequest.trust_root ) return django_response(webresponse) def landing_page(req, orequest): """ The page shown when the user attempts to sign in somewhere using OpenID but is not authenticated with the site. For idproxy.net, a message telling them to log in manually is displayed. """ # stash details so when they have logged in they can complete the auth subdomain = orequest.identity.replace('http://', '').replace( '.idproxy.net/', '') try: openid = OpenId.objects.get(subdomain = subdomain) except OpenId.DoesNotExist: return error_page(req, 'That OpenID does not exist') openid.pendingrequest_set.create( trust_root = orequest.trust_root, orequest = pickle.dumps(orequest) ) return render_to_response('landing.html', { 'title': 'You need to sign in', }) def openid_cancel(request): "Called when the user cancels a pending OpenID auth request" if request.session.get('AuthorizationInfo', None): del request.session['AuthorizationInfo'] return HttpResponseRedirect(request.GET.get('n', '/')) import urlparse def openid_is_authorized(req, identity_url, trust_root): """ Check that they own the given identity URL, and that the trust_root is in their whitelist of trusted sites. """ domain = urlparse.urlparse(identity_url)[1] subdomain = domain.split('.')[0] yhash = yhash_from_cookie(req) if not yhash: return False try: yahoouser = YahooUser.objects.get(yahoo_hash = yhash) except YahooUser.DoesNotExist: return False # Ensure that auth_user owns the requested domain try: openid = yahoouser.openid_set.get(subdomain=subdomain) except OpenId.DoesNotExist: return False if openid.trustedroot_set.filter( trust_root = trust_root ).count() < 1: # They don't trust this root yet return False # Add this to their history OpenIdHistory.objects.create(openid=openid, trust_root=str(trust_root)) return True