WIP
This commit is contained in:
parent
d0777e2b85
commit
b02d3db684
41 changed files with 426 additions and 647 deletions
18
realms/modules/auth/forms.py
Normal file
18
realms/modules/auth/forms.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
from wtforms import Form, StringField, PasswordField, validators
|
||||
|
||||
|
||||
class RegistrationForm(Form):
|
||||
username = StringField('Username', [validators.Length(min=4, max=25)])
|
||||
email = StringField('Email Address', [validators.Length(min=6, max=35)])
|
||||
password = PasswordField('Password', [
|
||||
validators.DataRequired(),
|
||||
validators.EqualTo('confirm', message='Passwords must match')
|
||||
])
|
||||
confirm = PasswordField('Repeat Password')
|
||||
|
||||
|
||||
class LoginForm(Form):
|
||||
email = StringField('Email', [validators.DataRequired])
|
||||
password = PasswordField('Password', [validators.DataRequired])
|
||||
|
||||
|
110
realms/modules/auth/models.py
Normal file
110
realms/modules/auth/models.py
Normal file
|
@ -0,0 +1,110 @@
|
|||
from flask.ext.login import UserMixin, logout_user, login_user
|
||||
from realms import config, login_manager
|
||||
from realms.lib.services import db
|
||||
from itsdangerous import URLSafeSerializer, BadSignature
|
||||
from hashlib import sha256
|
||||
import json
|
||||
import bcrypt
|
||||
|
||||
FIELD_MAP = dict(
|
||||
u='username',
|
||||
e='email',
|
||||
p='password',
|
||||
nv='not_verified',
|
||||
a='admin',
|
||||
b='banned')
|
||||
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return User.get(user_id)
|
||||
|
||||
|
||||
@login_manager.token_loader
|
||||
def load_token(token):
|
||||
# Load unsafe because payload is needed for sig
|
||||
sig_okay, payload = URLSafeSerializer(None).load_unsafe(token)
|
||||
|
||||
if not payload:
|
||||
return False
|
||||
|
||||
# User key *could* be stored in payload to avoid user lookup in db
|
||||
user = User.get(payload.get('id'))
|
||||
|
||||
if not user:
|
||||
return False
|
||||
|
||||
try:
|
||||
if User.signer(sha256(user.password).hexdigest()).loads(token):
|
||||
return user
|
||||
else:
|
||||
return False
|
||||
except BadSignature:
|
||||
return False
|
||||
|
||||
|
||||
class User(UserMixin):
|
||||
|
||||
username = None
|
||||
email = None
|
||||
password = None
|
||||
|
||||
def __init__(self, email, data=None):
|
||||
self.id = email
|
||||
for k, v in data.items():
|
||||
setattr(self, FIELD_MAP.get(k, k), v)
|
||||
|
||||
def get_auth_token(self):
|
||||
key = sha256(self.password).hexdigest()
|
||||
return User.signer(key).dumps(dict(id=self.username))
|
||||
|
||||
@staticmethod
|
||||
def create(username, email, password):
|
||||
User.set(email, dict(u=username, e=email, p=User.hash(password), nv=1))
|
||||
|
||||
@staticmethod
|
||||
def signer(salt):
|
||||
"""
|
||||
Signed with app secret salted with sha256 of password hash of user (client secret)
|
||||
"""
|
||||
return URLSafeSerializer(config.SECRET + salt)
|
||||
|
||||
@staticmethod
|
||||
def set(email, data):
|
||||
db.set('u:%s' % email, json.dumps(data, separators=(',', ':')))
|
||||
|
||||
@staticmethod
|
||||
def get(email):
|
||||
data = db.get('u:%s', email)
|
||||
|
||||
try:
|
||||
data = json.loads(data)
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
if data:
|
||||
return User(email, data)
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def auth(email, password):
|
||||
user = User.get(email)
|
||||
|
||||
if not user:
|
||||
return False
|
||||
|
||||
if bcrypt.checkpw(password, user.password):
|
||||
login_user(user, remember=True)
|
||||
return user
|
||||
else:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def hash(password):
|
||||
return bcrypt.hashpw(password, bcrypt.gensalt(log_rounds=12))
|
||||
|
||||
@classmethod
|
||||
def logout(cls):
|
||||
logout_user()
|
||||
|
|
@ -1,35 +1,36 @@
|
|||
from flask import render_template, redirect, request, url_for, flash, Blueprint
|
||||
from realms import redirect_url
|
||||
from realms.models import User
|
||||
from flask import g, render_template, request, redirect, Blueprint, flash, url_for
|
||||
from realms.modules.auth.models import User
|
||||
from realms.modules.auth.forms import LoginForm, RegistrationForm
|
||||
from realms import config
|
||||
|
||||
blueprint = Blueprint('auth', __name__)
|
||||
blueprint = Blueprint('auth', __name__, url_prefix=config.RELATIVE_PATH)
|
||||
|
||||
|
||||
@blueprint.route("/logout/")
|
||||
def logout():
|
||||
@blueprint.route("/logout")
|
||||
def logout_page():
|
||||
User.logout()
|
||||
return redirect(url_for('root'))
|
||||
flash("You are now logged out")
|
||||
return redirect(url_for(config.ROOT_ENDPOINT))
|
||||
|
||||
|
||||
@blueprint.route("/register/", methods=['GET', 'POST'])
|
||||
def register():
|
||||
if request.method == 'POST':
|
||||
if User.register(request.form.get('username'), request.form.get('email'), request.form.get('password')):
|
||||
return redirect(url_for('root'))
|
||||
else:
|
||||
# Login failed
|
||||
return redirect(url_for('.register'))
|
||||
else:
|
||||
return render_template('auth/register.html')
|
||||
|
||||
|
||||
@blueprint.route("/login/", methods=['GET', 'POST'])
|
||||
@blueprint.route("/login")
|
||||
def login():
|
||||
if request.method == 'POST':
|
||||
if request.method == "POST":
|
||||
form = RegistrationForm()
|
||||
|
||||
# TODO
|
||||
if not form.validate():
|
||||
flash('Form invalid')
|
||||
return redirect(url_for('auth.login'))
|
||||
|
||||
if User.auth(request.form['email'], request.form['password']):
|
||||
return redirect(redirect_url(referrer=url_for('root')))
|
||||
else:
|
||||
flash("Email or Password invalid")
|
||||
return redirect(url_for(".login"))
|
||||
return redirect(request.args.get("next") or url_for(config.ROOT_ENDPOINT))
|
||||
|
||||
return render_template("auth/login.html")
|
||||
|
||||
@blueprint.route("/register")
|
||||
def register():
|
||||
if request.method == "POST":
|
||||
return redirect(request.args.get("next") or url_for(config.ROOT_ENDPOINT))
|
||||
else:
|
||||
return render_template('auth/login.html')
|
||||
return render_template("auth/register.html")
|
|
@ -1,8 +1,8 @@
|
|||
from realms.lib.assets import register
|
||||
|
||||
register(
|
||||
'editor',
|
||||
'js/ace/ace.js',
|
||||
'js/ace/mode-markdown.js',
|
||||
'vendor/keymaster/keymaster.js',
|
||||
'js/dillinger.js'
|
||||
)
|
||||
'js/dillinger.js')
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
from flask import g, render_template, request, redirect, Blueprint, flash, url_for
|
||||
from flask.ext.login import login_required
|
||||
from realms.lib.util import to_canonical, remove_ext
|
||||
from realms import config
|
||||
|
||||
blueprint = Blueprint('wiki', __name__)
|
||||
blueprint = Blueprint('wiki', __name__, url_prefix=config.RELATIVE_PATH)
|
||||
|
||||
|
||||
@blueprint.route("/wiki/_commit/<sha>/<name>")
|
||||
@blueprint.route("/_commit/<sha>/<name>")
|
||||
def commit(name, sha):
|
||||
cname = to_canonical(name)
|
||||
|
||||
|
@ -16,13 +16,13 @@ def commit(name, sha):
|
|||
return redirect(url_for('wiki.create', name=cname))
|
||||
|
||||
|
||||
@blueprint.route("/wiki/_compare/<name>/<regex('[^.]+'):fsha><regex('\.{2,3}'):dots><regex('.+'):lsha>")
|
||||
@blueprint.route("/_compare/<name>/<regex('[^.]+'):fsha><regex('\.{2,3}'):dots><regex('.+'):lsha>")
|
||||
def compare(name, fsha, dots, lsha):
|
||||
diff = g.current_wiki.compare(name, fsha, lsha)
|
||||
return render_template('wiki/compare.html', name=name, diff=diff, old=fsha, new=lsha)
|
||||
|
||||
|
||||
@blueprint.route("/wiki/_revert", methods=['POST'])
|
||||
@blueprint.route("/_revert", methods=['POST'])
|
||||
def revert():
|
||||
if request.method == 'POST':
|
||||
name = request.form.get('name')
|
||||
|
@ -32,14 +32,15 @@ def revert():
|
|||
username=g.current_user.get('username'))
|
||||
flash('Page reverted', 'success')
|
||||
return redirect(url_for('wiki.page', name=cname))
|
||||
|
||||
@blueprint.route("/wiki/_history/<name>")
|
||||
|
||||
|
||||
@blueprint.route("/_history/<name>")
|
||||
def history(name):
|
||||
history = g.current_wiki.get_history(name)
|
||||
return render_template('wiki/history.html', name=name, history=history, wiki_home=url_for('wiki.page'))
|
||||
|
||||
|
||||
@blueprint.route("/wiki/_edit/<name>", methods=['GET', 'POST'])
|
||||
@blueprint.route("/_edit/<name>", methods=['GET', 'POST'])
|
||||
def edit(name):
|
||||
data = g.current_wiki.get_page(name)
|
||||
cname = to_canonical(name)
|
||||
|
@ -56,19 +57,19 @@ def edit(name):
|
|||
if data:
|
||||
name = remove_ext(data['name'])
|
||||
content = data['data']
|
||||
g.assets.append('editor')
|
||||
return render_template('wiki/edit.html', name=name, content=content)
|
||||
else:
|
||||
return redirect(url_for('wiki.create', name=cname))
|
||||
|
||||
|
||||
@blueprint.route("/wiki/_delete/<name>", methods=['POST'])
|
||||
@login_required
|
||||
@blueprint.route("/_delete/<name>", methods=['POST'])
|
||||
def delete(name):
|
||||
pass
|
||||
|
||||
|
||||
@blueprint.route("/wiki/_create/", defaults={'name': None}, methods=['GET', 'POST'])
|
||||
@blueprint.route("/wiki/_create/<name>", methods=['GET', 'POST'])
|
||||
@blueprint.route("/_create/", defaults={'name': None}, methods=['GET', 'POST'])
|
||||
@blueprint.route("/_create/<name>", methods=['GET', 'POST'])
|
||||
def create(name):
|
||||
if request.method == 'POST':
|
||||
g.current_wiki.write_page(request.form['name'],
|
||||
|
@ -82,11 +83,12 @@ def create(name):
|
|||
# Page exists, edit instead
|
||||
return redirect(url_for('wiki.edit', name=cname))
|
||||
|
||||
g.assets.append('editor')
|
||||
return render_template('wiki/edit.html', name=cname, content="")
|
||||
|
||||
|
||||
@blueprint.route("/wiki", defaults={'name': 'home'})
|
||||
@blueprint.route("/wiki/<name>")
|
||||
@blueprint.route("/", defaults={'name': 'home'})
|
||||
@blueprint.route("/<name>")
|
||||
def page(name):
|
||||
cname = to_canonical(name)
|
||||
if cname != name:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue