From a9c8b10d07f910b01af32e21afd34e8b6c6ebf6f Mon Sep 17 00:00:00 2001 From: "James E. Blair" Date: Sun, 23 Aug 2009 14:19:52 -0700 Subject: Addd OpenID controllers to support being an OpenID provider. --- quoins/blog_controllers.py | 8 -- quoins/controllers.py | 32 +++---- quoins/openid_controllers.py | 186 ++++++++++++++++++++++++++++++++++++++ quoins/templates/oid_confirm.html | 72 +++++++++++++++ quoins/utils.py | 17 ++++ 5 files changed, 286 insertions(+), 29 deletions(-) delete mode 100644 quoins/blog_controllers.py create mode 100644 quoins/openid_controllers.py create mode 100644 quoins/templates/oid_confirm.html create mode 100644 quoins/utils.py diff --git a/quoins/blog_controllers.py b/quoins/blog_controllers.py deleted file mode 100644 index 40c27ae..0000000 --- a/quoins/blog_controllers.py +++ /dev/null @@ -1,8 +0,0 @@ -from tg import TGController, tmpl_context -from tg import expose, flash, require, url, request, redirect - -class BlogController(TGController): - @expose(template="genshi:quoinstemplates.test1") - def test(self): - return dict() - diff --git a/quoins/controllers.py b/quoins/controllers.py index 6aac7a2..541d90b 100644 --- a/quoins/controllers.py +++ b/quoins/controllers.py @@ -41,31 +41,18 @@ import openid.consumer.consumer import openid.server.server import openid.extensions.sreg from openid.store.sqlstore import MySQLStore -import MySQLdb -import sqlalchemy.engine.url import types import xmlrpclib, sys, re from linkback import LinkBackHandler, PingBackURI, TrackBackURI import base64 import urllib +import openid_controllers +from utils import get_oid_connection def b64encode(x): return base64.encodestring(x)[:-1] -def get_oid_connection(config=None): - if config is None: - config = tg.config - backupuri = config.get('sqlalchemy.url') - uri = config.get('openid.store', backupuri) - u = sqlalchemy.engine.url.make_url(uri) - pw = u.password or '' - conn = MySQLdb.connect (host = u.host, - user = u.username, - passwd = pw, - db = u.database) - return conn - def fix_url(url): parts = urlsplit(url) if not parts[0]: @@ -335,6 +322,15 @@ class BlogController(TGController): feed = Feed() pingback = Pingback() + def __init__(self, *args, **kw): + self.path = kw.pop('path', '/') + self.post_paginate = kw.pop('paginate', 5) + get_name_from_id = kw.pop('get_name_from_id', lambda x: x) + self.openid = openid_controllers.OpenIDController(path=self.path+'/openid/', + get_name_from_id = get_name_from_id) + self.feed.blog_controller = self + super(BlogController, self).__init__(*args, **kw) + def url(self, obj=None): if obj is None: u = tg.url(self.path) @@ -428,12 +424,6 @@ Comment: t = Thread(target=send_email, args=(msg, fromaddr, toaddr)) t.start() - def __init__(self, *args, **kw): - self.path = kw.pop('path', '/') - self.post_paginate = kw.pop('paginate', 5) - self.feed.blog_controller = self - super(BlogController, self).__init__(*args, **kw) - @expose(template="genshi:quoinstemplates.index") def index(self, start=0): pylons.response.headers['X-XRDS-Location']=self.absolute_url('/yadis') diff --git a/quoins/openid_controllers.py b/quoins/openid_controllers.py new file mode 100644 index 0000000..9491195 --- /dev/null +++ b/quoins/openid_controllers.py @@ -0,0 +1,186 @@ +# Quoins - A TurboGears blogging system. +# Copyright (C) 2008-2009 James E. Blair +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from tg import controllers, expose, flash, require, validate, request, redirect, session +from tg import TGController +import tg +import pylons +from repoze.what import predicates +import openid.consumer.consumer +import openid.server.server +import openid.extensions.sreg +from openid.store.sqlstore import MySQLStore +import MySQLdb +import sqlalchemy.engine.url +import types +import os.path + +from utils import get_oid_connection + +class DecideController(object): + def __init__(self, oid_controller): + self.oid_controller = oid_controller + + @expose(template="genshi:quoinstemplates.oid_confirm") + @require(predicates.not_anonymous()) + def index(self, **kw): + oid_request = request.environ['oid_request'] + self.oid_controller._validate_identity(oid_request.identity) + + sreg_req = openid.extensions.sreg.SRegRequest.fromOpenIDRequest(oid_request) + user = request.identity['user'] + + sr_required = [self.oid_controller.getSRegValue(user,x) for x in sreg_req.required] + sr_optional = [self.oid_controller.getSRegValue(user,x) for x in sreg_req.optional] + + session['oid_request']=oid_request + session.save() + + return dict(openid = self.oid_controller, + oid_request = oid_request, + required = sr_required, + optional = sr_optional, + ) + +class ResponseController(object): + def __init__(self, oid_controller): + self.oid_controller = oid_controller + + @expose() + def index(self, **kw): + webresponse = request.environ['oid_webresponse'] + + pylons.response.status = webresponse.code + pylons.response.headers.update(webresponse.headers) + return webresponse.body + + +class OpenIDController(TGController): + sreg_friendly = { + 'nickname': 'Nickname', + 'email': 'Email address', + 'fullname': 'Full name', + 'dob': 'Date of birth', + 'gender': 'Gender', + 'postcode': 'Postal code', + 'country': 'Country', + 'language': 'Language', + 'timezone': 'Time zone', + } + + def url(self, obj=None): + if obj is None: + u = tg.url(self.path) + elif isinstance(obj, basestring): + if obj.startswith('/'): obj = obj[1:] + u = tg.url(os.path.join(self.path, obj)) + return u + + def absolute_url(self, obj): + port = tg.config.get('server.webport', '') + if port: + port = ':'+port + return 'http://%s%s%s'%(tg.config.get('server.webhost'), port, + self.url(obj)) + + def __init__(self, *args, **kw): + self.get_name_from_id = kw.pop('get_name_from_id', lambda x: x) + self.path = kw.pop('path', '/') + super(OpenIDController, self).__init__(*args, **kw) + + def getSRegValue(self, user, field): + val = None + if field=='nickname': val = user.user_name + if field=='fullname': val = user.display_name + if field=='email': val = user.email_address + return (field, self.sreg_friendly[field], val) + + def isAuthorized(self, id, trust_root): + if not request.identity: return False + name = self.get_name_from_id(id) + if request.identity['user'].user_name == name: + # check to see if the user has set preferences for this trust + # root, and return True. also, sreg. XXX + return False + return False + + def _validate_identity(self, id): + name = self.get_name_from_id(id) + if request.identity['user'].user_name != name: + raise Exception("Not your ID") + + @expose() + def finish(self, **kw): + oid_request=session['oid_request'] + self._validate_identity(oid_request.identity) + + sreg_req = openid.extensions.sreg.SRegRequest.fromOpenIDRequest(oid_request) + user = request.identity['user'] + + store = MySQLStore(get_oid_connection()) + oserver = openid.server.server.Server(store, + self.absolute_url('/openid/server')) + + data = {} + for field in sreg_req.required+sreg_req.optional: + if kw.has_key(field): + data[field]=self.getSRegValue(user, field)[2] + + sreg_resp = openid.extensions.sreg.SRegResponse.extractResponse(sreg_req, data) + + if kw.get('continue', None): + response = oid_request.answer(True) + response.addExtension(sreg_resp) + else: + response = oid_request.answer(False) + + webresponse = oserver.encodeResponse(response) + + pylons.response.status = webresponse.code + pylons.response.headers.update(webresponse.headers) + + del session['oid_request'] + session.save() + return webresponse.body + + @expose() + def lookup(self, *remainder): + store = MySQLStore(get_oid_connection()) + oserver = openid.server.server.Server(store, + self.absolute_url('/server')) + oid_request = oserver.decodeRequest(request.params) + request.environ['oid_request']=oid_request + + if oid_request.mode in ['checkid_immediate', 'checkid_setup']: + if hasattr(oid_request, 'returnToVerified'): + if not (oid_request.trustRootValid() + and oid_request.returnToVerified()): + raise Exception("Not trusted") + else: + if not (oid_request.trustRootValid()): + raise Exception("Not trusted") + if self.isAuthorized(oid_request.identity, oid_request.trust_root): + response = oid_request.answer(True) + elif oid_request.immediate: + response = oid_request.answer(False) + else: + return (DecideController(self), ()) + else: + response = oserver.handleRequest(oid_request) + + webresponse = oserver.encodeResponse(response) + request.environ['oid_webresponse']=webresponse + return (ResponseController(self), ()) diff --git a/quoins/templates/oid_confirm.html b/quoins/templates/oid_confirm.html new file mode 100644 index 0000000..4fcf1de --- /dev/null +++ b/quoins/templates/oid_confirm.html @@ -0,0 +1,72 @@ + + + + + + + + ${blog.title} + + + + +
+
+ +
+ +

+ The site ${oid_request.trust_root} + wants to verify that you are identified by ${request.identity['user']} + and has requested the following information: +

+ + +
+

Required information

+
    +
  • + +
  • +
+
+
+

Optional information

+
    +
  • + +
  • +
+
+ + + +

+ +   + +

+ +
+ +
+
+ + + diff --git a/quoins/utils.py b/quoins/utils.py new file mode 100644 index 0000000..2382a71 --- /dev/null +++ b/quoins/utils.py @@ -0,0 +1,17 @@ +import tg +import sqlalchemy.engine.url +import MySQLdb + +def get_oid_connection(config=None): + if config is None: + config = tg.config + backupuri = config.get('sqlalchemy.url') + uri = config.get('openid.store', backupuri) + u = sqlalchemy.engine.url.make_url(uri) + pw = u.password or '' + conn = MySQLdb.connect (host = u.host, + user = u.username, + passwd = pw, + db = u.database) + return conn + -- cgit v1.2.3