commit
377916670a
@ -38,8 +38,8 @@ fileConfig(config.config_file_name)
|
|||||||
# from myapp import mymodel
|
# from myapp import mymodel
|
||||||
# target_metadata = mymodel.Base.metadata
|
# target_metadata = mymodel.Base.metadata
|
||||||
# target_metadata = None
|
# target_metadata = None
|
||||||
from refstack.models import *
|
from refstack.models import db
|
||||||
target_metadata = Base.metadata
|
target_metadata = db.metadata
|
||||||
|
|
||||||
# other values from the config, defined by the needs of env.py,
|
# other values from the config, defined by the needs of env.py,
|
||||||
# can be acquired:
|
# can be acquired:
|
||||||
|
31
alembic/versions/449461dbc725_add_apikey.py
Normal file
31
alembic/versions/449461dbc725_add_apikey.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 449461dbc725
|
||||||
|
Revises: 59e15d864941
|
||||||
|
Create Date: 2013-11-26 16:57:16.062788
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '449461dbc725'
|
||||||
|
down_revision = '59e15d864941'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.create_table('apikey',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(length=60), nullable=True),
|
||||||
|
sa.Column('key', sa.String(length=200), nullable=True),
|
||||||
|
sa.Column('openid', sa.String(length=200), nullable=True),
|
||||||
|
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('timestamp', sa.DateTime(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_Table('apikey')
|
28
refstack/admin.py
Normal file
28
refstack/admin.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
import flask
|
||||||
|
from flask.ext.admin.contrib import sqla
|
||||||
|
|
||||||
|
from refstack import models
|
||||||
|
|
||||||
|
# Global admin object
|
||||||
|
from .extensions import admin
|
||||||
|
from .extensions import db
|
||||||
|
|
||||||
|
|
||||||
|
class SecureView(sqla.ModelView):
|
||||||
|
def is_accessible(self):
|
||||||
|
# let us look at the admin if we're in debug mode
|
||||||
|
if flask.current_app.debug:
|
||||||
|
return True
|
||||||
|
return flask.g.user.su is not False
|
||||||
|
|
||||||
|
|
||||||
|
def init_app(app):
|
||||||
|
admin.init_app(app)
|
||||||
|
|
||||||
|
|
||||||
|
def configure_admin(app):
|
||||||
|
admin.add_view(SecureView(models.ApiKey, db.session))
|
||||||
|
admin.add_view(SecureView(models.Cloud, db.session))
|
||||||
|
admin.add_view(SecureView(models.User, db.session))
|
||||||
|
admin.add_view(SecureView(models.Vendor, db.session))
|
68
refstack/api.py
Normal file
68
refstack/api.py
Normal file
@ -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
|
@ -14,7 +14,11 @@ from .config import DefaultConfig
|
|||||||
#from .api import api
|
#from .api import api
|
||||||
#from .admin import admin
|
#from .admin import admin
|
||||||
#from .extensions import db, mail, cache, login_manager, oid
|
#from .extensions import db, mail, cache, login_manager, oid
|
||||||
from .extensions import db, mail, login_manager, oid
|
from refstack import admin
|
||||||
|
from refstack import api
|
||||||
|
from .extensions import db
|
||||||
|
from .extensions import oid
|
||||||
|
|
||||||
|
|
||||||
from refstack import utils
|
from refstack import utils
|
||||||
|
|
||||||
@ -89,6 +93,14 @@ def configure_extensions(app):
|
|||||||
# flask-sqlalchemy
|
# flask-sqlalchemy
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
|
|
||||||
|
# flask-admin
|
||||||
|
admin.init_app(app)
|
||||||
|
admin.configure_admin(app)
|
||||||
|
|
||||||
|
# flask-restless
|
||||||
|
api.init_app(app, flask_sqlalchemy_db=db)
|
||||||
|
api.configure_api(app)
|
||||||
|
|
||||||
## flask-mail
|
## flask-mail
|
||||||
#mail.init_app(app)
|
#mail.init_app(app)
|
||||||
|
|
||||||
@ -112,8 +124,8 @@ def configure_extensions(app):
|
|||||||
# return User.query.get(id)
|
# return User.query.get(id)
|
||||||
#login_manager.setup_app(app)
|
#login_manager.setup_app(app)
|
||||||
|
|
||||||
## flask-openid
|
# flask-openid
|
||||||
#oid.init_app(app)
|
oid.init_app(app)
|
||||||
|
|
||||||
|
|
||||||
def configure_blueprints(app, blueprints):
|
def configure_blueprints(app, blueprints):
|
||||||
|
@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
# This file based on MIT licensed code at: https://github.com/imwilsonxu/fbone
|
# This file based on MIT licensed code at: https://github.com/imwilsonxu/fbone
|
||||||
|
|
||||||
|
from flask.ext.admin import Admin
|
||||||
|
admin = Admin()
|
||||||
|
|
||||||
|
from flask.ext.restless import APIManager
|
||||||
|
api = APIManager()
|
||||||
|
|
||||||
from flask.ext.sqlalchemy import SQLAlchemy
|
from flask.ext.sqlalchemy import SQLAlchemy
|
||||||
db = SQLAlchemy()
|
db = SQLAlchemy()
|
||||||
|
|
||||||
|
@ -41,6 +41,20 @@ class User(db.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
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.
|
Note: The vendor list will be pre-populated from the sponsoring company database.
|
||||||
TODO: better define the vendor object and its relationship with user
|
TODO: better define the vendor object and its relationship with user
|
||||||
|
@ -5,19 +5,19 @@
|
|||||||
<script src="{{ url_for('static', filename='jquery-1.10.1.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='jquery-1.10.1.min.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='bootstrap/js/bootstrap.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='bootstrap/js/bootstrap.min.js') }}"></script>
|
||||||
<!-- refstack specific js-->
|
<!-- refstack specific js-->
|
||||||
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='bootstrap/css/bootstrap.min.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='bootstrap/css/bootstrap.min.css') }}">
|
||||||
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" />
|
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" />
|
||||||
<link rel="stylesheet" type="text/css" href="/static/toast.css">
|
<link rel="stylesheet" type="text/css" href="/static/toast.css">
|
||||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='refstack.css') }}">
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='refstack.css') }}">
|
||||||
<!-- compiled and minified bootstrap JavaScript -->
|
<!-- compiled and minified bootstrap JavaScript -->
|
||||||
|
|
||||||
{% block head_css %}{% endblock %}
|
{% block head_css %}{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<a href="/" class="logo">
|
<a href="/" class="logo">
|
||||||
<span class="glyphicon glyphicon-check"></span>
|
<span class="glyphicon glyphicon-check"></span>
|
||||||
Refstack
|
Refstack
|
||||||
@ -32,7 +32,7 @@
|
|||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<span class="glyphicon glyphicon-cog"></span>
|
<span class="glyphicon glyphicon-cog"></span>
|
||||||
<a data-toggle="dropdown" href="#">
|
<a data-toggle="dropdown" href="#">
|
||||||
|
|
||||||
Admin
|
Admin
|
||||||
<span class="caret"></span>
|
<span class="caret"></span>
|
||||||
</a>
|
</a>
|
||||||
@ -46,7 +46,7 @@
|
|||||||
<li >
|
<li >
|
||||||
<a href="/admin/userview/">Users</a>
|
<a href="/admin/userview/">Users</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
@ -96,4 +96,4 @@
|
|||||||
{% block tail_js %}{% endblock %}
|
{% block tail_js %}{% endblock %}
|
||||||
<script src="{{ url_for('static', filename='refstack.js') }}"></script>
|
<script src="{{ url_for('static', filename='refstack.js') }}"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -29,8 +29,16 @@ from wtforms import Form, BooleanField, TextField, \
|
|||||||
from flask_mail import Mail
|
from flask_mail import Mail
|
||||||
|
|
||||||
from refstack import app as base_app
|
from refstack import app as base_app
|
||||||
from refstack import utils
|
from refstack.extensions import db
|
||||||
from refstack.models import *
|
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
|
||||||
|
from refstack.models import TestStatus
|
||||||
|
from refstack.models import User
|
||||||
|
from refstack.models import Vendor
|
||||||
|
|
||||||
|
|
||||||
# TODO(termie): transition all the routes below to blueprints
|
# TODO(termie): transition all the routes below to blueprints
|
||||||
@ -39,27 +47,14 @@ from refstack.models import *
|
|||||||
app = base_app.create_app()
|
app = base_app.create_app()
|
||||||
|
|
||||||
mail = Mail(app)
|
mail = Mail(app)
|
||||||
# setup flask-openid
|
|
||||||
oid = OpenID(app)
|
|
||||||
admin = Admin(app, base_template='admin/master.html')
|
|
||||||
|
|
||||||
|
|
||||||
class SecureView(ModelView):
|
|
||||||
def is_accessible(self):
|
|
||||||
return g.user.su is not False
|
|
||||||
|
|
||||||
|
|
||||||
admin.add_view(SecureView(Vendor, db))
|
|
||||||
admin.add_view(SecureView(Cloud, db))
|
|
||||||
admin.add_view(SecureView(User, db))
|
|
||||||
|
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
def before_request():
|
def before_request():
|
||||||
"""Runs before the request itself."""
|
"""Runs before the request itself."""
|
||||||
g.user = None
|
flask.g.user = None
|
||||||
if 'openid' in session:
|
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'])
|
@app.route('/', methods=['POST', 'GET'])
|
||||||
@ -122,8 +117,8 @@ def create_profile():
|
|||||||
flash(u'Error: you have to enter a valid email address')
|
flash(u'Error: you have to enter a valid email address')
|
||||||
else:
|
else:
|
||||||
flash(u'Profile successfully created')
|
flash(u'Profile successfully created')
|
||||||
db.add(User(name, email, session['openid']))
|
db.session.add(User(name, email, session['openid']))
|
||||||
db.commit()
|
db.session.commit()
|
||||||
return redirect(oid.get_next_url())
|
return redirect(oid.get_next_url())
|
||||||
return render_template(
|
return render_template(
|
||||||
'create_profile.html', next_url=oid.get_next_url())
|
'create_profile.html', next_url=oid.get_next_url())
|
||||||
@ -139,8 +134,8 @@ def delete_cloud(cloud_id):
|
|||||||
elif not c.user_id == g.user.id:
|
elif not c.user_id == g.user.id:
|
||||||
flash(u"This isn't your cloud!")
|
flash(u"This isn't your cloud!")
|
||||||
else:
|
else:
|
||||||
db.delete(c)
|
db.session.delete(c)
|
||||||
db.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return redirect('/')
|
return redirect('/')
|
||||||
|
|
||||||
@ -180,7 +175,7 @@ def edit_cloud(cloud_id):
|
|||||||
c.admin_user = request.form['admin_user']
|
c.admin_user = request.form['admin_user']
|
||||||
c.admin_key = request.form['admin_key']
|
c.admin_key = request.form['admin_key']
|
||||||
|
|
||||||
db.commit()
|
db.session.commit()
|
||||||
|
|
||||||
flash(u'Cloud Saved!')
|
flash(u'Cloud Saved!')
|
||||||
return redirect('/')
|
return redirect('/')
|
||||||
@ -230,8 +225,8 @@ def create_cloud():
|
|||||||
c.admin_user = request.form['admin_user']
|
c.admin_user = request.form['admin_user']
|
||||||
c.admin_key = request.form['admin_key']
|
c.admin_key = request.form['admin_key']
|
||||||
|
|
||||||
db.add(c)
|
db.session.add(c)
|
||||||
db.commit()
|
db.session.commit()
|
||||||
return redirect('/')
|
return redirect('/')
|
||||||
|
|
||||||
return render_template('create_cloud.html', next_url='/')
|
return render_template('create_cloud.html', next_url='/')
|
||||||
@ -245,8 +240,8 @@ def edit_profile():
|
|||||||
form = dict(name=g.user.name, email=g.user.email)
|
form = dict(name=g.user.name, email=g.user.email)
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
if 'delete' in request.form:
|
if 'delete' in request.form:
|
||||||
db.delete(g.user)
|
db.session.delete(g.user)
|
||||||
db.commit()
|
db.session.commit()
|
||||||
session['openid'] = None
|
session['openid'] = None
|
||||||
flash(u'Profile deleted')
|
flash(u'Profile deleted')
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
@ -260,7 +255,7 @@ def edit_profile():
|
|||||||
flash(u'Profile successfully created')
|
flash(u'Profile successfully created')
|
||||||
g.user.name = form['name']
|
g.user.name = form['name']
|
||||||
g.user.email = form['email']
|
g.user.email = form['email']
|
||||||
db.commit()
|
db.session.commit()
|
||||||
return redirect(url_for('edit_profile'))
|
return redirect(url_for('edit_profile'))
|
||||||
return render_template('edit_profile.html', form=form)
|
return render_template('edit_profile.html', form=form)
|
||||||
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
Flask==0.9
|
Flask==0.10.1
|
||||||
Flask-Admin==1.0.6
|
Flask-Admin==1.0.7
|
||||||
Flask-Login==0.1.3
|
Flask-Login==0.1.3
|
||||||
Flask-Mail==0.8.2
|
Flask-Mail==0.8.2
|
||||||
Flask-OpenID==1.1.1
|
Flask-OpenID==1.1.1
|
||||||
Flask-Principal==0.3.5
|
Flask-Principal==0.3.5
|
||||||
Flask-SQLAlchemy==0.16
|
Flask-SQLAlchemy==1.0
|
||||||
Flask-Security==1.6.3
|
Flask-Security==1.6.3
|
||||||
Flask-WTF==0.8.3
|
Flask-WTF==0.8.3
|
||||||
SQLAlchemy==0.8.1
|
Flask-Restless=0.12.0
|
||||||
|
SQLAlchemy==0.8.3
|
||||||
WTForms==1.0.4
|
WTForms==1.0.4
|
||||||
Werkzeug==0.8.3
|
Werkzeug==0.8.3
|
||||||
alembic==0.5.0
|
alembic==0.5.0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user