Merge pull request #2 from jarretraim/master

Authentication, Administration & Models
This commit is contained in:
Jarret Raim 2013-02-18 04:29:00 -08:00
commit 22abe99eae
7 changed files with 223 additions and 27 deletions

View File

@ -13,25 +13,67 @@
:license: Apache 2.0, see LICENSE for details
"""
import os
from flask import Flask, render_template
from flask import Flask, render_template, redirect, flash, request
from flask.ext.admin import Admin
from flask.ext.admin.contrib.sqlamodel import ModelView
from flask.ext import login, wtf
from flask.ext.login import login_user
from barbican_api import api
from database import db_session, init_db
from models import User
from models import User, Tenant, Key
app = Flask(__name__)
app.secret_key = '79f9823f1f0---DEVELOPMENT---c46cebdd1c8f3d0742e02'
app.register_blueprint(api)
admin = Admin(app, name="Barbican Admin")
admin.add_view(ModelView(User, db_session))
admin.add_view(ModelView(Tenant, db_session))
admin.add_view(ModelView(Key, db_session))
login_manager = login.LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
@app.route("/")
@login.login_required
def hello():
return "Hello world!"
return render_template("index.html")
@app.route('/users')
def users_list():
users = User.query.all()
return render_template('users.html', users=users)
#
# Login forms
#
class LoginForm(wtf.Form):
login = wtf.TextField(validators=[wtf.required()])
password = wtf.PasswordField(validators=[wtf.required()])
def validate_login(self, field):
user = self.get_user()
if user is None or user.password != self.password.data:
raise wtf.ValidationError('Invalid username or credentials.')
def get_user(self):
return User.query.filter_by(name=self.login.data).first()
@app.route("/login", methods=["GET", "POST"])
def login():
form = LoginForm(request.form)
if form.validate_on_submit():
user = form.get_user()
login_user(user)
flash('Logged in successfully.')
return redirect('/admin/')
return render_template("login.html", form=form)
@login_manager.user_loader
def load_user(user_id):
return User.query.get(user_id)
@app.teardown_request
@ -41,5 +83,6 @@ def shutdown_session(exception=None):
if __name__ == '__main__':
if not os.path.exists('/tmp/barbican.db'):
app.logger.info('No database detected at /tmp/barbican.db. Creating one and the admin user.')
init_db()
app.run(debug=True)

View File

@ -29,5 +29,5 @@ def init_db():
# you will have to import them first before calling init_db()
import models
Base.metadata.create_all(bind=engine)
db_session.add(models.User('admin', 'admin@localhost'))
db_session.add(models.User('admin', 'admin@localhost', 'Passw0rd'))
db_session.commit()

View File

@ -8,7 +8,10 @@
:copyright: (c) 2013 by Jarret Raim
:license: Apache 2.0, see LICENSE for details
"""
from sqlalchemy import Column, Integer, String
from uuid import uuid4
from sqlalchemy import Column, Integer, String, DateTime, Text
from sqlalchemy.orm import relationship, backref
from sqlalchemy.schema import ForeignKey
from database import Base
@ -17,10 +20,74 @@ class User(Base):
id = Column(Integer, primary_key=True)
name = Column(String(50), unique=True)
email = Column(String(120), unique=True)
password = Column(String(50))
def __init__(self, name=None, email=None):
def is_authenticated(self):
return True
def is_active(self):
return True
def is_anonymous(self):
return False
def get_id(self):
return self.id
def __init__(self, name=None, email=None, password=None):
self.name = name
self.email = email
self.password = password
def __repr__(self):
return '<User %r>' % self.name
class Tenant(Base):
__tablename__ = 'tenants'
id = Column(Integer, primary_key=True)
uuid = Column(String(36), unique=True)
#keys = relationship('Key', backref='tenant', lazy='dynamic')
def __init__(self, uuid=None):
if uuid is None:
self.uuid = str(uuid4())
def __repr__(self):
return '<Tenant %s>' % self.uuid
class Key(Base):
__tablename__ = 'keys'
id = Column(Integer, primary_key=True)
uuid = Column(String(36), unique=True)
filename = Column(String(128))
mime_type = Column(String(128))
expires = Column(DateTime)
secret = Column(Text)
tenant_id = Column(Integer, ForeignKey('tenants.id'))
tenant = relationship("Tenant", backref=backref('keys', order_by=id))
def __init__(self, uuid=None):
if uuid is None:
self.uuid = str(uuid4())
def __repr__(self):
return '<Key %s >' % self.uuid
class Policy(Base):
__tablename__ = 'policies'
id = Column(Integer, primary_key=True)
uuid = Column(String(36), unique=True)
tenant_id = Column(Integer, ForeignKey('tenants.id'))
tenant = relationship("Tenant", backref=backref('policies', order_by=id))
def __init__(self, uuid=None):
if uuid is None:
self.uuid = str(uuid4())
def __repr__(self):
return '<Policy %s >' % self.uuid

4
templates/index.html Normal file
View File

@ -0,0 +1,4 @@
{% extends 'layout.html' %}
{% block body %}
{% endblock %}

View File

@ -43,9 +43,9 @@
<a class="brand" href="#">Barbican</a>
<div class="nav-collapse collapse">
<ul class="nav">
<li class="active"><a href="#">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
<li><a href="/">Home</a></li>
<li><a href="/users">Users</a></li>
<li><a href="/tenants">Tenants</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>

96
templates/login.html Normal file
View File

@ -0,0 +1,96 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Sign in &middot; Twitter Bootstrap</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<!-- Le styles -->
<link href="/static/css/bootstrap.css" rel="stylesheet">
<style type="text/css">
body {
padding-top: 40px;
padding-bottom: 40px;
background-color: #f5f5f5;
}
.form-signin {
max-width: 300px;
padding: 19px 29px 29px;
margin: 0 auto 20px;
background-color: #fff;
border: 1px solid #e5e5e5;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05);
-moz-box-shadow: 0 1px 2px rgba(0,0,0,.05);
box-shadow: 0 1px 2px rgba(0,0,0,.05);
}
.form-signin .form-signin-heading,
.form-signin .checkbox {
margin-bottom: 10px;
}
.form-signin input[type="text"],
.form-signin input[type="password"] {
font-size: 16px;
height: auto;
margin-bottom: 15px;
padding: 7px 9px;
}
</style>
<link href="/static/css/bootstrap-responsive.css" rel="stylesheet">
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="/static/js/html5shiv.js"></script>
<![endif]-->
<!-- Fav and touch icons -->
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="/static/ico/apple-touch-icon-144-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="/static/ico/apple-touch-icon-114-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="/static/ico/apple-touch-icon-72-precomposed.png">
<link rel="apple-touch-icon-precomposed" href="/static/ico/apple-touch-icon-57-precomposed.png">
<link rel="shortcut icon" href="/static/ico/favicon.png">
</head>
<body>
<div class="container">
<form method="post" class="form-signin">
{{ form.hidden_tag() }}
<h2 class="form-signin-heading">Please sign in</h2>
{% if form.errors %}
<div class="alert alert-error alert-block">
<h4>Errors</h4>
<ul>
{% for field_name, field_errors in form.errors|dictsort if field_errors %}
{% for error in field_errors %}
<li>{{ form[field_name].name }}: {{ error }}</li>
{% endfor %}
{% endfor %}
</ul>
</div>
{% endif %}
{% for f in form if f.type != 'CSRFTokenField' %}
{{ f.label }}{{ f }}
{% endfor %}
<button class="btn btn-large btn-primary" type="submit">Sign in</button>
</form>
</div> <!-- /container -->
<!-- Le javascript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="/static/js/jquery-1.9.1.min.js"></script>
</body>
</html>

View File

@ -1,14 +0,0 @@
{% extends "layout.html" %}
{% block body %}
<div class="page-header">
<h1>Users</h1>
</div>
<ul>
{% for user in users %}
<li>{{ user.name }}</li>
{% else %}
<li><em>No users yet.</em></li>
{% endfor %}
</ul>
{% endblock %}