From c7f435056c797ca936df5b9952fb96a43bc708c6 Mon Sep 17 00:00:00 2001 From: Ryan Casey Date: Fri, 7 Dec 2012 12:43:26 -0800 Subject: [PATCH 1/6] Added support for a MongoDB backend. More or less a 1:1 port. Could stand to be optimized, most likely. --- examples/mongo_demoprovider/__init__.py | 35 +++ examples/mongo_demoprovider/login.py | 114 ++++++++ examples/mongo_demoprovider/models.py | 110 +++++++ examples/mongo_demoprovider/provider.py | 275 ++++++++++++++++++ examples/mongo_demoprovider/static/openid.png | Bin 0 -> 433 bytes examples/mongo_demoprovider/static/style.css | 4 + .../templates/authorize.html | 4 + .../mongo_demoprovider/templates/client.html | 21 ++ .../templates/create_profile.html | 22 ++ .../templates/edit_profile.html | 14 + .../mongo_demoprovider/templates/index.html | 22 ++ .../mongo_demoprovider/templates/layout.html | 39 +++ .../mongo_demoprovider/templates/login.html | 12 + .../templates/register.html | 34 +++ examples/mongo_demoprovider/utils.py | 14 + 15 files changed, 720 insertions(+) create mode 100644 examples/mongo_demoprovider/__init__.py create mode 100644 examples/mongo_demoprovider/login.py create mode 100644 examples/mongo_demoprovider/models.py create mode 100644 examples/mongo_demoprovider/provider.py create mode 100644 examples/mongo_demoprovider/static/openid.png create mode 100644 examples/mongo_demoprovider/static/style.css create mode 100644 examples/mongo_demoprovider/templates/authorize.html create mode 100644 examples/mongo_demoprovider/templates/client.html create mode 100644 examples/mongo_demoprovider/templates/create_profile.html create mode 100644 examples/mongo_demoprovider/templates/edit_profile.html create mode 100644 examples/mongo_demoprovider/templates/index.html create mode 100644 examples/mongo_demoprovider/templates/layout.html create mode 100644 examples/mongo_demoprovider/templates/login.html create mode 100644 examples/mongo_demoprovider/templates/register.html create mode 100644 examples/mongo_demoprovider/utils.py diff --git a/examples/mongo_demoprovider/__init__.py b/examples/mongo_demoprovider/__init__.py new file mode 100644 index 0000000..a528725 --- /dev/null +++ b/examples/mongo_demoprovider/__init__.py @@ -0,0 +1,35 @@ +from flask import Flask, request +from provider import ExampleProvider +from models import AccessToken, ResourceOwner as User + +app = Flask(__name__) +app.config.update( + DATABASE_URI="", + SECRET_KEY="debugging key" +) + +provider = ExampleProvider(app) + +# Imported to setup views +import login + +@app.route('/callback') +def callback(): + return str(request.__dict__) + +@app.route("/protected") +@provider.require_oauth() +def protected_view(): + token = request.oauth.resource_owner_key + access_token = AccessToken.get_collection().find_one({'token':token}) + user = User.find_one({'_id':access_token['resource_owner_id']}) + return user['name'] + + +@app.route("/protected_realm") +@provider.require_oauth(realm="secret") +def protected_realm_view(): + token = request.oauth.resource_owner_key + access_token = AccessToken.get_collection().find_one({'token':token}) + user = User.find_one({'_id':access_token['resource_owner_id']}) + return user['email'] diff --git a/examples/mongo_demoprovider/login.py b/examples/mongo_demoprovider/login.py new file mode 100644 index 0000000..58edce8 --- /dev/null +++ b/examples/mongo_demoprovider/login.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +from demoprovider import app +from models import ResourceOwner as User +from flask import g, session, render_template, request, redirect, flash +from flask import abort, url_for +from flask.ext.openid import OpenID + +# setup flask-openid +oid = OpenID(app) + + +@app.before_request +def before_request(): + g.user = None + if 'openid' in session: + user_dict = User.find_one({'openid':session['openid']}) + g.user = User() + g.user.update(user_dict) + + +@app.route('/') +def index(): + return render_template('index.html') + + +@app.route('/login', methods=['GET', 'POST']) +@oid.loginhandler +def login(): + """Does the login via OpenID. Has to call into `oid.try_login` + to start the OpenID machinery. + """ + # if we are already logged in, go back to were we came from + if g.user is not None: + return redirect(oid.get_next_url()) + if request.method == 'POST': + openid = request.form.get('openid') + if openid: + return oid.try_login(openid, ask_for=['email', 'fullname', + 'nickname']) + return render_template('login.html', next=oid.get_next_url(), + error=oid.fetch_error()) + + +@oid.after_login +def create_or_login(resp): + """This is called when login with OpenID succeeded and it's not + necessary to figure out if this is the users's first login or not. + This function has to redirect otherwise the user will be presented + with a terrible URL which we certainly don't want. + """ + session['openid'] = resp.identity_url + user = User.get_collection().find_one({'openid':resp.identity_url}) + if user is not None: + flash(u'Successfully signed in') + g.user = user + return redirect(oid.get_next_url()) + return redirect(url_for('create_profile', next=oid.get_next_url(), + name=resp.fullname or resp.nickname, + email=resp.email)) + + +@app.route('/create-profile', methods=['GET', 'POST']) +def create_profile(): + """If this is the user's first login, the create_or_login function + will redirect here so that the user can set up his profile. + """ + if g.user is not None or 'openid' not in session: + return redirect(url_for('index')) + if request.method == 'POST': + name = request.form['name'] + email = request.form['email'] + if not name: + flash(u'Error: you have to provide a name') + elif '@' not in email: + flash(u'Error: you have to enter a valid email address') + else: + flash(u'Profile successfully created') + User.get_collection().insert(User(name, email, session['openid'])) + return redirect(oid.get_next_url()) + return render_template('create_profile.html', next_url=oid.get_next_url()) + + +@app.route('/profile', methods=['GET', 'POST']) +def edit_profile(): + """Updates a profile""" + if g.user is None: + abort(401) + form = dict(name=g.user.name, email=g.user.email) + if request.method == 'POST': + if 'delete' in request.form: + User.get_collection().remove(g.user) + session['openid'] = None + flash(u'Profile deleted') + return redirect(url_for('index')) + form['name'] = request.form['name'] + form['email'] = request.form['email'] + if not form['name']: + flash(u'Error: you have to provide a name') + elif '@' not in form['email']: + flash(u'Error: you have to enter a valid email address') + else: + flash(u'Profile successfully created') + g.user.name = form['name'] + g.user.email = form['email'] + uid = User.get_collection().save(g.user) + return redirect(url_for('edit_profile')) + return render_template('edit_profile.html', form=form) + + +@app.route('/logout') +def logout(): + session.pop('openid', None) + flash(u'You have been signed out') + return redirect(oid.get_next_url()) diff --git a/examples/mongo_demoprovider/models.py b/examples/mongo_demoprovider/models.py new file mode 100644 index 0000000..b168589 --- /dev/null +++ b/examples/mongo_demoprovider/models.py @@ -0,0 +1,110 @@ +import pymongo + + +def get_connection(): + return pymongo.MongoClient().demo_oauth_provider + + +class Model(dict): + @classmethod + def get_collection(cls): + conn = get_connection() + return conn[cls.table] + + @classmethod + def find_one(cls, attrs): + return cls.get_collection().find_one(attrs) + + @classmethod + def insert(cls, obj): + return cls.get_collection().insert(obj) + + @classmethod + def save(cls, obj): + return cls.get_collection().save(obj) + + def __getattr__(self, attr): + return self[attr] + + def __setattr__(self, attr, value): + self[attr] = value + + + +class ResourceOwner(Model): + table = "users" + + def __init__(self, name="", email="", openid=""): + self.name = name + self.email = email + self.openid = openid + self.request_tokens = [] + self.access_tokens = [] + self.client_ids = [] + + def __repr__(self): + return "" % (self.name, self.email) + + +class Client(Model): + table = "clients" + + def __init__(self, client_key, name, description, secret=None, pubkey=None): + self.client_key = client_key + self.name = name + self.description = description + self.secret = secret + self.pubkey = pubkey + self.request_tokens = [] + self.access_tokens = [] + self.callbacks = [] + self.resource_owner_id = "" + + def __repr__(self): + return "" % (self.name, self.id) + + +class Nonce(Model): + table = "nonces" + + def __init__(self, nonce, timestamp): + self.nonce = nonce + self.timestamp = timestamp + self.client_id = "" + self.request_token_id = "" + self.access_token_id = "" + + def __repr__(self): + return "" % (self.nonce, self.timestamp, self.client, self.resource_owner) + + +class RequestToken(Model): + table = "requestTokens" + + def __init__(self, token, callback, secret=None, verifier=None, realm=None): + self.token = token + self.secret = secret + self.verifier = verifier + self.realm = realm + self.callback = callback + self.client_id = "" + self.resource_owner_id = "" + + + def __repr__(self): + return "" % (self.token, self.client, self.resource_owner) + + +class AccessToken(Model): + table = "accessTokens" + + def __init__(self, token, secret=None, verifier=None, realm=None): + self.token = token + self.secret = secret + self.verifier = verifier + self.realm = realm + self.client_id = "" + self.resource_owner_id = "" + + def __repr__(self): + return "" % (self.token, self.client, self.resource_owner) diff --git a/examples/mongo_demoprovider/provider.py b/examples/mongo_demoprovider/provider.py new file mode 100644 index 0000000..fc16a30 --- /dev/null +++ b/examples/mongo_demoprovider/provider.py @@ -0,0 +1,275 @@ +from flask import request, render_template, g +from flask.ext.oauthprovider import OAuthProvider +from bson.objectid import ObjectId +from models import ResourceOwner as User, Client, Nonce +from models import RequestToken, AccessToken +from utils import require_openid + + +class ExampleProvider(OAuthProvider): + + @property + def enforce_ssl(self): + return False + + @property + def realms(self): + return [u"secret", u"trolling"] + + @property + def nonce_length(self): + return 20, 40 + + @require_openid + def authorize(self): + if request.method == u"POST": + token = request.form.get("oauth_token") + return self.authorized(token) + else: + # TODO: Authenticate client + token = request.args.get(u"oauth_token") + return render_template(u"authorize.html", token=token) + + @require_openid + def register(self): + if request.method == u'POST': + client_key = self.generate_client_key() + secret = self.generate_client_secret() + # TODO: input sanitisation? + name = request.form.get(u"name") + description = request.form.get(u"description") + callback = request.form.get(u"callback") + pubkey = request.form.get(u"pubkey") + # TODO: redirect? + # TODO: pubkey upload + # TODO: csrf + info = { + u"client_key": client_key, + u"name": name, + u"description": description, + u"secret": secret, + u"pubkey": pubkey + } + client = Client(**info) + client['callback'].append(callback) + client['resource_owner_id'] = g.user['_id'] + client_id = Client.insert(client) + g.user.client_ids.append(client_id) + User.get_collection().save(g.user) + return render_template(u"client.html", **info) + else: + clients = Client.get_collection().find({'_id': {'$in': + [ObjectId(oid) for oid in g.user.client_ids]}}) + return render_template(u"register.html", clients=clients) + + + def validate_timestamp_and_nonce(self, client_key, timestamp, nonce, + request_token=None, access_token=None): + + token = True + req_token = True + client = Client.find_one({'client_key':client_key}) + + if client: + nonce = Nonce.find_one({'nonce':nonce, 'timestamp':timestamp, + 'client_id':client['_id']}) + + if nonce: + if request_token: + req_token = RequestToken.find_one( + {'_id':nonce['request_token_id'], 'token':request_token}) + + if access_token: + token = RequestToken.find_one( + {'_id':nonce['request_token_id'], 'token':access_token}) + + return token and req_token and nonce != None + + def validate_redirect_uri(self, client_key, redirect_uri=None): + client = Client.find_one({'client_key':client_key}) + + return client != None and ( + len(client['callback']) == 1 and redirect_uri is None + or redirect_uri in (x for x in client['callbacks'])) + + + def validate_client_key(self, client_key): + return ( + Client.find_one({'client_key':client_key}) != None) + + + def validate_requested_realm(self, client_key, realm): + return True + + + def validate_realm(self, client_key, access_token, uri=None, required_realm=None): + + if not required_realm: + return True + + # insert other check, ie on uri here + + client = Client.find_one({'client_key':client_key}) + + if client: + token = AccessToken.find_one( + {'token':access_token, 'client_id': client['_id']}) + + if token: + return token['realm'] in required_realm + + return False + + @property + def dummy_client(self): + return u'dummy_client' + + @property + def dummy_resource_owner(self): + return u'dummy_resource_owner' + + def validate_request_token(self, client_key, resource_owner_key): + # TODO: make client_key optional + token = None + + if client_key: + client = Client.find_one({'client_key':client_key}) + + if client: + token = RequestToken.find_one( + {'token':access_token, 'client_id': client['_id']}) + + else: + token = RequestToken.find_one( + {'token':resource_owner_key}) + + return token != None + + + def validate_access_token(self, client_key, resource_owner_key): + + token = None + client = Client.find_one({'client_key':client_key}) + + if client: + token = AccessToken.find_one( + {'token':resource_owner_key, 'client_id': client['_id']}) + + return token != None + + + def validate_verifier(self, client_key, resource_owner_key, verifier): + token = None + client = Client.find_one({'client_key':client_key}) + + if client: + token = RequestToken.find_one( + {'token':resource_owner_key, + 'client_id': client['_id'], + 'verifier':verifier}) + + return token != None + + + def get_callback(self, request_token): + return RequestToken.find_one( + {'token':request_token})['callback'] + + + def get_realm(self, client_key, request_token): + client = Client.find_one({'client_key':client_key}) + + if client: + return RequestToken.find_one( + {'token':request_token, 'client_id': client['_id']})['realm'] + else: + return None + + + def get_client_secret(self, client_key): + return Client.find_one( + {'client_key':client_key}).secret + + + def get_rsa_key(self, client_key): + return Client.find_one( + {'client_key':client_key}).pubkey + + def get_request_token_secret(self, client_key, resource_owner_key): + client = Client.find_one({'client_key':client_key}) + + if client: + token = RequestToken.find_one( + {'token':resource_owner_key, + 'client_id': client['_id']}) + + if token: + return token.secret + + return None + + + def get_access_token_secret(self, client_key, resource_owner_key): + client = Client.find_one({'client_key':client_key}) + + if client: + token = AccessToken.find_one( + {'token':resource_owner_key, + 'client_id': client['_id']}) + + if token: + return token.secret + + return None + + def save_request_token(self, client_key, request_token, callback, + realm=None, secret=None): + client = Client.find_one({'client_key':client_key}) + + if client: + token = RequestToken( + request_token, callback, secret=secret, realm=realm) + token.client_id = client['_id'] + + RequestToken.insert(token) + + def save_access_token(self, client_key, access_token, request_token, + realm=None, secret=None): + client = Client.find_one({'client_key':client_key}) + + if client: + token = AccessToken(access_token, secret=secret, realm=realm) + token.client_id = client['_id'] + + req_token = RequestToken.find_one({'token':request_token}) + + if req_token: + token['resource_owner_id'] = req_token['resource_owner_id'] + token['realm'] = req_token['realm'] + + AccessToken.insert(token) + + def save_timestamp_and_nonce(self, client_key, timestamp, nonce, + request_token=None, access_token=None): + + client = Client.find_one({'client_key':client_key}) + + if client: + nonce = Nonce(nonce, timestamp) + nonce.client_id = client['_id'] + + if request_token: + req_token = RequestToken.find_one({'token':request_token}) + nonce.request_token_id = req_token['_id'] + + if access_token: + token = AccessToken.find_one({'token':access_token}) + nonce.access_token_id = token['_id'] + + Nonce.insert(nonce) + + def save_verifier(self, request_token, verifier): + token = RequestToken.find_one({'token':request_token}) + token['verifier'] = verifier + token['resource_owner_id'] = g.user['_id'] + RequestToken.get_collection().save(token) diff --git a/examples/mongo_demoprovider/static/openid.png b/examples/mongo_demoprovider/static/openid.png new file mode 100644 index 0000000000000000000000000000000000000000..ce7954ab12080e1cfb0583669a58267bea008a44 GIT binary patch literal 433 zcmV;i0Z#sjP)DL1dJy@3D#0X|7Y zK~y-)wbMUH)^QL9@X!15!y(?mMM2P4p@xE{I+t9cA<2T^g}0^6DYpnD0w*VNtlcRZ zqPZNxAflj-7X_<9aESg4Qc-~~QK-vr5W&}8!-Io6?)yC4-E&`#+O{VRZBLrouU zd>!vFekcM=pw{U@^?ygK>`pSXz(rifnd6Jor+e3*)qd8`ZLVFpyNVVT@i-#>Waf!t z7r2RejNk{BiuSi&oGjYkKjQF~8cv)sk21Hd98k$YA6TgE+yQK5?aW&2SsvO9u^ zc!QfI$%D+C%gp|6IuH@}Z~@D>jZc~R^4H+#7}|L9Plr@W8n}eB_2+ZH`&J*_sVrDO bvfsJ`%6MX|2>A&`00000NkvXXu0mjf^=ZFi literal 0 HcmV?d00001 diff --git a/examples/mongo_demoprovider/static/style.css b/examples/mongo_demoprovider/static/style.css new file mode 100644 index 0000000..c8fd8ef --- /dev/null +++ b/examples/mongo_demoprovider/static/style.css @@ -0,0 +1,4 @@ +body { + margin-top: 20px; + font-family: Cantarell, Calibri, sans; +} diff --git a/examples/mongo_demoprovider/templates/authorize.html b/examples/mongo_demoprovider/templates/authorize.html new file mode 100644 index 0000000..0c5ccab --- /dev/null +++ b/examples/mongo_demoprovider/templates/authorize.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/examples/mongo_demoprovider/templates/client.html b/examples/mongo_demoprovider/templates/client.html new file mode 100644 index 0000000..40bb9ec --- /dev/null +++ b/examples/mongo_demoprovider/templates/client.html @@ -0,0 +1,21 @@ +{% extends "layout.html" %} +{% block body %} + + + + + + + + + + + + + + + + + +
Client key {{ client_key }}
Client secret {{ secret }}
Application title {{ name }}
Application description {{ description }}
+{% endblock %} diff --git a/examples/mongo_demoprovider/templates/create_profile.html b/examples/mongo_demoprovider/templates/create_profile.html new file mode 100644 index 0000000..00030fb --- /dev/null +++ b/examples/mongo_demoprovider/templates/create_profile.html @@ -0,0 +1,22 @@ +{% extends "layout.html" %} +{% block title %}Create Profile{% endblock %} +{% block body %} +

Create Profile

+

+ Hey! This is the first time you signed in on this website. In + order to proceed we need some extra information from you: +

+
+
Name: +
+
E-Mail: +
+
+

+ + +

+

+ If you don't want to proceed, you can sign out again. +{% endblock %} diff --git a/examples/mongo_demoprovider/templates/edit_profile.html b/examples/mongo_demoprovider/templates/edit_profile.html new file mode 100644 index 0000000..3a8eb43 --- /dev/null +++ b/examples/mongo_demoprovider/templates/edit_profile.html @@ -0,0 +1,14 @@ +{% extends "layout.html" %} +{% block title %}Edit Profile{% endblock %} +{% block body %} +

Edit Profile

+
+ + + + +
+ + +
+{% endblock %} diff --git a/examples/mongo_demoprovider/templates/index.html b/examples/mongo_demoprovider/templates/index.html new file mode 100644 index 0000000..88b7466 --- /dev/null +++ b/examples/mongo_demoprovider/templates/index.html @@ -0,0 +1,22 @@ +{% extends "layout.html" %} +{% block body %} +

Overview

+
+{% if not g.user %} +

First off, this example uses OpenID (Flask-OpenID) so sign in using your preferred OpenID provider.

+{% endif %} + +

This example is a basic but functional service with an API. Through the + API third party clients can access user data, which in our case will simply + be the username and email. These resources (email, name) are considered + protected resources and as such requests to the API for these are protected + with OAuth. +

+ +

All clients who wish to use the API will need to register an obtain a client_key as well as a client_secret. These credentials can then be used from another example application, this time a client, found in client.py. +

+ +

After setting up the client you may access its start page . Upon access the client will obtain a request token, redirect you to authorize access, obtain a request token, and finally using that token to fetch and display your email address. +

+ +{% endblock %} diff --git a/examples/mongo_demoprovider/templates/layout.html b/examples/mongo_demoprovider/templates/layout.html new file mode 100644 index 0000000..3ba8121 --- /dev/null +++ b/examples/mongo_demoprovider/templates/layout.html @@ -0,0 +1,39 @@ + + +{% block title %}Welcome{% endblock %} | Flask OAuth Provider Example + + + + +
+

Flask OAuth Provider Example

+
+ +{% for message in get_flashed_messages() %} +
+

{{ message }} +

+{% endfor %} +{% block body %}{% endblock %} +
+ diff --git a/examples/mongo_demoprovider/templates/login.html b/examples/mongo_demoprovider/templates/login.html new file mode 100644 index 0000000..1ba11b0 --- /dev/null +++ b/examples/mongo_demoprovider/templates/login.html @@ -0,0 +1,12 @@ +{% extends "layout.html" %} +{% block title %}Sign in{% endblock %} +{% block body %} +
+ {% if error %}

Error: {{ error }}

{% endif %} + + +
+ + +
+{% endblock %} diff --git a/examples/mongo_demoprovider/templates/register.html b/examples/mongo_demoprovider/templates/register.html new file mode 100644 index 0000000..02fe695 --- /dev/null +++ b/examples/mongo_demoprovider/templates/register.html @@ -0,0 +1,34 @@ +{% extends "layout.html" %} +{% block body %} +{% if clients %} +

Registered clients

+
+ + + + + + + + + + {% for client in clients %} + + + + + + + {% endfor %} +
Title Client key Client secret Public key
{{ client.name }} {{ client.client_key }} {{ client.secret }} {{ client.pubkey }}
+{% endif %} +

Register new client

+
+ + + + +
+ +
+{% endblock %} diff --git a/examples/mongo_demoprovider/utils.py b/examples/mongo_demoprovider/utils.py new file mode 100644 index 0000000..618331d --- /dev/null +++ b/examples/mongo_demoprovider/utils.py @@ -0,0 +1,14 @@ +from functools import wraps +from flask import g, url_for, request, redirect + + +def require_openid(f): + """Require user to be logged in.""" + @wraps(f) + def decorator(*args, **kwargs): + if g.user is None: + next_url = url_for("login") + "?next=" + request.url + return redirect(next_url) + else: + return f(*args, **kwargs) + return decorator From 3ad3d969c05309ae1d8da05c86c9f0a61248164a Mon Sep 17 00:00:00 2001 From: Ryan Casey Date: Fri, 7 Dec 2012 13:33:45 -0800 Subject: [PATCH 2/6] Fixed a couple errors caused by moving the MongoDB example to mongo_demoprovider and created a runserver_mongo.py for that example. --- examples/init_db.py | 2 +- examples/mongo_demoprovider/login.py | 2 +- examples/runserver_mongo.py | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 examples/runserver_mongo.py diff --git a/examples/init_db.py b/examples/init_db.py index e9557e9..edee707 100644 --- a/examples/init_db.py +++ b/examples/init_db.py @@ -1,2 +1,2 @@ -from demoprovider.models import init_db +from mongo_demoprovider.models import init_db init_db() diff --git a/examples/mongo_demoprovider/login.py b/examples/mongo_demoprovider/login.py index 58edce8..95e771c 100644 --- a/examples/mongo_demoprovider/login.py +++ b/examples/mongo_demoprovider/login.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from demoprovider import app +from mongo_demoprovider import app from models import ResourceOwner as User from flask import g, session, render_template, request, redirect, flash from flask import abort, url_for diff --git a/examples/runserver_mongo.py b/examples/runserver_mongo.py new file mode 100644 index 0000000..c5c0e3f --- /dev/null +++ b/examples/runserver_mongo.py @@ -0,0 +1,2 @@ +from mongo_demoprovider import app +app.run(debug=True) From ebec926945e1fc1e9b08247de4213330ea2ceb10 Mon Sep 17 00:00:00 2001 From: Ryan Casey Date: Mon, 10 Dec 2012 12:20:10 -0800 Subject: [PATCH 3/6] Fixed a couple bugs. Hitting the page with open_id in the session and no associated ResourceOwner no longer breaks things. Also fixed 'callback' reference that should have been 'callbacks.' --- examples/mongo_demoprovider/login.py | 6 ++++-- examples/mongo_demoprovider/provider.py | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/mongo_demoprovider/login.py b/examples/mongo_demoprovider/login.py index 95e771c..bdb2851 100644 --- a/examples/mongo_demoprovider/login.py +++ b/examples/mongo_demoprovider/login.py @@ -14,8 +14,10 @@ def before_request(): g.user = None if 'openid' in session: user_dict = User.find_one({'openid':session['openid']}) - g.user = User() - g.user.update(user_dict) + + if user_dict: + g.user = User() + g.user.update(user_dict) @app.route('/') diff --git a/examples/mongo_demoprovider/provider.py b/examples/mongo_demoprovider/provider.py index fc16a30..5210d5a 100644 --- a/examples/mongo_demoprovider/provider.py +++ b/examples/mongo_demoprovider/provider.py @@ -51,7 +51,7 @@ def register(self): u"pubkey": pubkey } client = Client(**info) - client['callback'].append(callback) + client['callbacks'].append(callback) client['resource_owner_id'] = g.user['_id'] client_id = Client.insert(client) g.user.client_ids.append(client_id) @@ -89,7 +89,7 @@ def validate_redirect_uri(self, client_key, redirect_uri=None): client = Client.find_one({'client_key':client_key}) return client != None and ( - len(client['callback']) == 1 and redirect_uri is None + len(client['callbacks']) == 1 and redirect_uri is None or redirect_uri in (x for x in client['callbacks'])) From c80bec827f1ae8b558effc0cfdc554bbc60feabb Mon Sep 17 00:00:00 2001 From: Ryan Casey Date: Sat, 15 Dec 2012 01:33:53 -0800 Subject: [PATCH 4/6] Made the code a little more robust and fixed two incorrect accessors. --- examples/mongo_demoprovider/provider.py | 40 +++++++++++++++++-------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/examples/mongo_demoprovider/provider.py b/examples/mongo_demoprovider/provider.py index 5210d5a..e653083 100644 --- a/examples/mongo_demoprovider/provider.py +++ b/examples/mongo_demoprovider/provider.py @@ -172,28 +172,44 @@ def validate_verifier(self, client_key, resource_owner_key, verifier): def get_callback(self, request_token): - return RequestToken.find_one( - {'token':request_token})['callback'] + token = RequestToken.find_one( + {'token':request_token}) + + if token: + return token.get('callback') + else: + return None def get_realm(self, client_key, request_token): client = Client.find_one({'client_key':client_key}) if client: - return RequestToken.find_one( - {'token':request_token, 'client_id': client['_id']})['realm'] - else: - return None + token = RequestToken.find_one( + {'token':request_token, 'client_id': client['_id']}) + + if token: + return token.get('realm') + + return None def get_client_secret(self, client_key): - return Client.find_one( - {'client_key':client_key}).secret + client = Client.find_one({'client_key':client_key}) + + if client: + return client.get('secret') + else: + return None def get_rsa_key(self, client_key): - return Client.find_one( - {'client_key':client_key}).pubkey + client = Client.find_one({'client_key':client_key}) + + if client: + return client.get('pubkey') + else: + return None def get_request_token_secret(self, client_key, resource_owner_key): client = Client.find_one({'client_key':client_key}) @@ -204,7 +220,7 @@ def get_request_token_secret(self, client_key, resource_owner_key): 'client_id': client['_id']}) if token: - return token.secret + return token.get('secret') return None @@ -218,7 +234,7 @@ def get_access_token_secret(self, client_key, resource_owner_key): 'client_id': client['_id']}) if token: - return token.secret + return token.get('secret') return None From 8e47884d0b9e266fbdec74f7036176647be6645b Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 17 Dec 2012 14:33:30 -0800 Subject: [PATCH 5/6] Reverted the init_db reference. --- examples/init_db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/init_db.py b/examples/init_db.py index edee707..e9557e9 100644 --- a/examples/init_db.py +++ b/examples/init_db.py @@ -1,2 +1,2 @@ -from mongo_demoprovider.models import init_db +from demoprovider.models import init_db init_db() From bfbf722870fb98d52bed5248c87209f5d7ebfb27 Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 17 Dec 2012 14:37:36 -0800 Subject: [PATCH 6/6] Update README.rst --- README.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.rst b/README.rst index c0c09d0..e77d80e 100644 --- a/README.rst +++ b/README.rst @@ -51,6 +51,27 @@ After installing you can run the demo application:: (venv)$ python flask-oauthprovider/examples/runserver.py +MongoDB Example +------- + +This repo also includes a fully working, MongoDB / pymongo +backed OAuth provider in the `/examples`_ folder. + +Before running the demo you need to install a few dependencies (virtualenv is +highly recommended).:: + + $ virtualenv venv + $ source venv/bin/activate + (venv)$ git clone https://github.com/idan/oauthlib.git + (venv)$ python oauthlib/setup.py install + (venv)$ git clone https://github.com/ib-lundgren/flask-oauthprovider.git + (venv)$ python flask-oauthprovider/setup.py install + (venv)$ pip install flask-openid pymongo + +After installing you can run the demo application:: + + (venv)$ python flask-oauthprovider/examples/runserver_mongo.py + Usage -----