From c8284b2bdc846d9769cdf6865ff40ca37ab9661e Mon Sep 17 00:00:00 2001 From: termie Date: Tue, 26 Nov 2013 16:54:44 -0800 Subject: [PATCH] add a basic api based on flask-restless not really a final solution, but it can probably get pretty far --- refstack/admin.py | 2 +- refstack/api.py | 68 ++++++++++++++++++++++++++++++++++++++++++ refstack/app.py | 7 ++++- refstack/extensions.py | 3 ++ refstack/models.py | 14 +++++++++ refstack/web.py | 6 ++-- requirements.txt | 1 + 7 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 refstack/api.py diff --git a/refstack/admin.py b/refstack/admin.py index 79955e89..d756a65e 100644 --- a/refstack/admin.py +++ b/refstack/admin.py @@ -21,7 +21,7 @@ def init_app(app): admin.init_app(app) -def configure_admin(): +def configure_admin(app): admin.add_view(SecureView(models.Cloud, db.session)) admin.add_view(SecureView(models.User, db.session)) admin.add_view(SecureView(models.Vendor, db.session)) diff --git a/refstack/api.py b/refstack/api.py new file mode 100644 index 00000000..14e675f4 --- /dev/null +++ b/refstack/api.py @@ -0,0 +1,68 @@ +"""Basic API code. + +This is using Flask-Restless at the moment because it is super simple, +but probably should be moved to a more versatile framework like +Flask-Restful later on. +""" + +import flask +from flask.ext import restless + +from refstack import models +from refstack.extensions import api + + +def init_app(app, *args, **kw): + api.init_app(app, *args, **kw) + + +def access_control(**kw): + if not flask.g.user: + raise _not_authorized() + + if not flask.g.user.su: + return _not_authorized() + + # That's it, we're defaulting to superuser only access + # until we flesh this out further + + +ALL_METHODS = {'GET_SINGLE': [access_control], + 'GET_MANY': [access_control], + 'PUT_SINGLE': [access_control], + 'PUT_MANY': [access_control], + 'POST': [access_control], + 'DELETE': [access_control]} + + +def configure_api(app): + cloud_api = api.create_api_blueprint(models.Cloud, + preprocessors=ALL_METHODS) + cloud_api.before_request(authenticate) + app.register_blueprint(cloud_api) + + +def _not_authorized(): + return restless.ProcessingException(message='Not Authorized', + status_code=401) + + + + +def authenticate(): + # If we're already authenticated, we can ignore this + if flask.g.user: + return + + # Otherwise check headers + openid = flask.request.headers.get('X-AUTH-OPENID') + if openid: + # In debug mode accept anything + if flask.current_app.debug and False: + flask.g.user = models.User.query.filter_by(openid=openid).first() + return + + apikey = flask.request.headers.get('X-AUTH-APIKEY') + apikey_ref = models.ApiKey.query.filter_by(key=apikey) + if apikey_ref['openid'] == openid: + flask.g.user = apikey_ref.user diff --git a/refstack/app.py b/refstack/app.py index f15a7615..b1570ae7 100644 --- a/refstack/app.py +++ b/refstack/app.py @@ -15,6 +15,7 @@ from .config import DefaultConfig #from .admin import admin #from .extensions import db, mail, cache, login_manager, oid from refstack import admin +from refstack import api from .extensions import db from .extensions import oid @@ -94,7 +95,11 @@ def configure_extensions(app): # flask-admin admin.init_app(app) - admin.configure_admin() + admin.configure_admin(app) + + # flask-restless + api.init_app(app, flask_sqlalchemy_db=db) + api.configure_api(app) ## flask-mail #mail.init_app(app) diff --git a/refstack/extensions.py b/refstack/extensions.py index 6b36240f..5d528c23 100644 --- a/refstack/extensions.py +++ b/refstack/extensions.py @@ -5,6 +5,9 @@ from flask.ext.admin import Admin admin = Admin() +from flask.ext.restless import APIManager +api = APIManager() + from flask.ext.sqlalchemy import SQLAlchemy db = SQLAlchemy() diff --git a/refstack/models.py b/refstack/models.py index 79bf662d..4a83fe66 100755 --- a/refstack/models.py +++ b/refstack/models.py @@ -41,6 +41,20 @@ class User(db.Model): def __str__(self): return self.name + +class ApiKey(db.Model): + __tablename__ = 'apikey' + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(60)) + key = db.Column(db.String(200)) + openid = db.Column(db.String(200)) + timestamp = db.Column(db.DateTime, default=datetime.now) + + user_id = db.Column(db.Integer, db.ForeignKey('user.id')) + user = db.relationship('User', + backref=db.backref('apikeys', lazy='dynamic')) + + """ Note: The vendor list will be pre-populated from the sponsoring company database. TODO: better define the vendor object and its relationship with user diff --git a/refstack/web.py b/refstack/web.py index ed0bb46e..a7ae49c1 100755 --- a/refstack/web.py +++ b/refstack/web.py @@ -31,6 +31,8 @@ from flask_mail import Mail from refstack import app as base_app from refstack.extensions import db from refstack.extensions import oid +from refstack import api +from refstack.models import ApiKey from refstack.models import Cloud from refstack.models import Test from refstack.models import TestResults @@ -50,9 +52,9 @@ mail = Mail(app) @app.before_request def before_request(): """Runs before the request itself.""" - g.user = None + flask.g.user = None if 'openid' in session: - g.user = User.query.filter_by(openid=session['openid']).first() + flask.g.user = User.query.filter_by(openid=session['openid']).first() @app.route('/', methods=['POST', 'GET']) diff --git a/requirements.txt b/requirements.txt index e7a97700..cd32b05b 100755 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,7 @@ Flask-Principal==0.3.5 Flask-SQLAlchemy==1.0 Flask-Security==1.6.3 Flask-WTF==0.8.3 +Flask-Restless=0.12.0 SQLAlchemy==0.8.3 WTForms==1.0.4 Werkzeug==0.8.3