Adding Users model to alembic, and the openid templates.

This commit is contained in:
Joshua McKenty 2013-07-03 13:48:21 -07:00
parent de82c74a29
commit f170d96ea0
10 changed files with 291 additions and 10 deletions

View File

@ -0,0 +1,52 @@
"""Adding Cloud and User models
Revision ID: 40d4c6d389ec
Revises: 2e26571834ea
Create Date: 2013-07-02 15:02:46.951119
"""
# revision identifiers, used by Alembic.
revision = '40d4c6d389ec'
down_revision = '2e26571834ea'
from alembic import op
import sqlalchemy as sa
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.create_table('user',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=60), nullable=True),
sa.Column('email', sa.String(length=200), nullable=True),
sa.Column('openid', sa.String(length=200), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('openid')
)
op.create_table('cloud',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('vendor_id', sa.Integer(), nullable=True),
sa.Column('endpoint', sa.String(length=120), nullable=True),
sa.Column('test_user', sa.String(length=80), nullable=True),
sa.Column('test_key', sa.String(length=80), nullable=True),
sa.Column('admin_endpoint', sa.String(length=120), nullable=True),
sa.Column('admin_user', sa.String(length=80), nullable=True),
sa.Column('admin_key', sa.String(length=80), nullable=True),
sa.ForeignKeyConstraint(['vendor_id'], ['vendor.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('admin_endpoint'),
sa.UniqueConstraint('admin_key'),
sa.UniqueConstraint('admin_user'),
sa.UniqueConstraint('endpoint'),
sa.UniqueConstraint('test_key'),
sa.UniqueConstraint('test_user')
)
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_table('cloud')
op.drop_table('user')
### end Alembic commands ###

BIN
refstack/static/openid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

View File

@ -0,0 +1,45 @@
body {
font-family: 'Georgia', serif;
font-size: 16px;
margin: 30px;
padding: 0;
}
a {
color: #335E79;
}
p.message {
color: #335E79;
padding: 10px;
background: #CADEEB;
}
p.error {
color: #783232;
padding: 10px;
background: #EBCACA;
}
input {
font-family: 'Georgia', serif;
font-size: 16px;
border: 1px solid black;
color: #335E79;
padding: 2px;
}
input[type="submit"] {
background: #CADEEB;
color: #335E79;
border-color: #335E79;
}
input[name="openid"] {
background: url(openid.png) 4px no-repeat;
padding-left: 24px;
}
h1, h2 {
font-weight: normal;
}

View File

@ -0,0 +1,22 @@
{% extends "layout.html" %}
{% block title %}Create Profile{% endblock %}
{% block body %}
<h2>Create Profile</h2>
<p>
Hey! This is the first time you signed in on this website. In
order to proceed we need some extra information from you:
<form action="" method=post>
<dl>
<dt>Name:
<dd><input type=text name=name size=30 value="{{ request.values.name }}">
<dt>E-Mail:
<dd><input type=text name=email size=30 value="{{ request.values.email }}">
</dl>
<p>
<input type=submit value="Create profile">
<input type=hidden name=next value="{{ next }}">
</form>
<p>
If you don't want to proceed, you can <a href="{{ url_for('logout')
}}">sign out</a> again.
{% endblock %}

View File

@ -0,0 +1,16 @@
{% extends "layout.html" %}
{% block title %}Edit Profile{% endblock %}
{% block body %}
<h2>Edit Profile</h2>
<form action="" method=post>
<dl>
<dt>Name:
<dd><input type=text name=name size=30 value="{{ form.name }}">
<dt>E-Mail
<dd><input type=text name=email size=30 value="{{ form.email }}">
</dl>
<p>
<input type=submit value="Update profile">
<input type=submit name=delete value="Delete">
</form>
{% endblock %}

View File

@ -1,11 +1,6 @@
<!doctype html>
<html>
<head>
<title>RefStack: What is it?</title>
<link rel="stylesheet" href="/static/toast.css">
</head>
<body>
{% extends "layout.html" %}
{% block title %}Welcome{% endblock %}
{% block body %}
<div class="container">
<div class="grid">
<div class="unit span-grid">
@ -33,5 +28,4 @@
</div>
</div>
</div>
</body>
</html>
{% endblock %}

View File

@ -0,0 +1,19 @@
<!doctype html>
<title>{% block title %}Welcome{% endblock %} | RefStack</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static',
filename='style.css') }}">
<link rel="stylesheet" type="text/css" href="/static/toast.css">
<h1>RefStack with OpenID login</h1>
<ul class=navigation>
<li><a href="{{ url_for('index') }}">overview</a>
{% if g.user %}
<li><a href="{{ url_for('edit_profile') }}">profile</a>
<li><a href="{{ url_for('logout') }}">sign out [{{ g.user.name }}]</a>
{% else %}
<li><a href="{{ url_for('login') }}">sign in</a>
{% endif %}
</ul>
{% for message in get_flashed_messages() %}
<p class=message>{{ message }}
{% endfor %}
{% block body %}{% endblock %}

View File

@ -0,0 +1,13 @@
{% extends "layout.html" %}
{% block title %}Sign in{% endblock %}
{% block body %}
<h2>Sign in</h2>
<form action="" method=post>
{% if error %}<p class=error><strong>Error:</strong> {{ error }}</p>{% endif %}
<p>
OpenID:
<input type=text name=openid size=30>
<input type=submit value="Sign in">
<input type=hidden name=next value="{{ next }}">
</form>
{% endblock %}

View File

@ -8,6 +8,7 @@ import random
import sqlite3
import sys
from flask import Flask, flash, request, redirect, url_for, render_template, g, session
from flask_openid import OpenID
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.admin import Admin, BaseView, expose
from flask.ext.admin.contrib.sqlamodel import ModelView
@ -44,6 +45,9 @@ app.config['MAIL_USERNAME'] = 'postmaster@hastwoparents.com'
app.config['MAIL_PASSWORD'] = '0sx00qlvqbo3'
mail = Mail(app)
# setup flask-openid
oid = OpenID(app)
db = SQLAlchemy(app)
@ -77,8 +81,29 @@ class Cloud(db.Model):
return '<Cloud %r>' % self.endpoint
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(60))
email = db.Column(db.String(200))
openid = db.Column(db.String(200), unique=True)
def __init__(self, name, email, openid):
self.name = name
self.email = email
self.openid = openid
admin = Admin(app)
admin.add_view(ModelView(Vendor, db.session))
admin.add_view(ModelView(Cloud, db.session))
admin.add_view(ModelView(User, db.session))
@app.before_request
def before_request():
g.user = None
if 'openid' in session:
g.user = User.query.filter_by(openid=session['openid']).first()
@app.route('/', methods=['POST','GET'])
@ -87,6 +112,100 @@ def index():
return render_template('index.html', vendors = vendors)
@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.query.filter_by(openid=resp.identity_url).first()
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')
db.session.add(User(name, email, session['openid']))
db.session.commit()
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:
db.session.delete(g.user)
db.session.commit()
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']
db.session.commit()
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())
if __name__ == '__main__':
app.logger.setLevel('DEBUG')
port = int(os.environ.get('PORT', 5000))

View File

@ -3,6 +3,7 @@ twisted
flask
Flask-SQLAlchemy
flask-admin
flask-openid
werkzeug==0.8.3
flask==0.9
Flask-Login==0.1.3