first pass, non-working
This commit is contained in:
parent
459a9c2c59
commit
3c2f4a0445
17 changed files with 295 additions and 121 deletions
0
realms/modules/auth/local/__init__.py
Normal file
0
realms/modules/auth/local/__init__.py
Normal file
37
realms/modules/auth/local/commands.py
Normal file
37
realms/modules/auth/local/commands.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
import click
|
||||
from realms.lib.util import random_string
|
||||
from realms.modules.auth.local.models import User
|
||||
from realms.lib.util import green, red, yellow
|
||||
from realms import flask_cli
|
||||
|
||||
|
||||
@flask_cli.group(short_help="Auth Module")
|
||||
def cli():
|
||||
pass
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.argument('username')
|
||||
@click.argument('email')
|
||||
@click.option('--password', help='Leave blank for random password')
|
||||
def create_user(username, email, password):
|
||||
""" Create a new user
|
||||
"""
|
||||
show_pass = not password
|
||||
|
||||
if not password:
|
||||
password = random_string(12)
|
||||
|
||||
if User.get_by_username(username):
|
||||
red("Username %s already exists" % username)
|
||||
return
|
||||
|
||||
if User.get_by_email(email):
|
||||
red("Email %s already exists" % email)
|
||||
return
|
||||
|
||||
User.create(username, email, password)
|
||||
green("User %s created" % username)
|
||||
|
||||
if show_pass:
|
||||
yellow("Password: %s" % password)
|
17
realms/modules/auth/local/forms.py
Normal file
17
realms/modules/auth/local/forms.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
from flask_wtf import Form
|
||||
from wtforms import 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()])
|
8
realms/modules/auth/local/hooks.py
Normal file
8
realms/modules/auth/local/hooks.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from flask import current_app
|
||||
from flask_wtf import RecaptchaField
|
||||
from .forms import RegistrationForm
|
||||
|
||||
|
||||
def before_first_request():
|
||||
if current_app.config['RECAPTCHA_ENABLE']:
|
||||
setattr(RegistrationForm, 'recaptcha', RecaptchaField("You Human?"))
|
107
realms/modules/auth/local/models.py
Normal file
107
realms/modules/auth/local/models.py
Normal file
|
@ -0,0 +1,107 @@
|
|||
from flask import current_app, render_template
|
||||
from flask.ext.login import logout_user, login_user
|
||||
from realms import login_manager, db
|
||||
from realms.lib.model import Model
|
||||
from ..models import BaseUser
|
||||
from .forms import LoginForm
|
||||
from itsdangerous import URLSafeSerializer, BadSignature
|
||||
from hashlib import sha256
|
||||
import bcrypt
|
||||
|
||||
|
||||
@login_manager.token_loader
|
||||
def load_token(token):
|
||||
# Load unsafe because payload is needed for sig
|
||||
sig_okay, payload = URLSafeSerializer(current_app.config['SECRET_KEY']).loads_unsafe(token)
|
||||
|
||||
if not payload:
|
||||
return None
|
||||
|
||||
# User key *could* be stored in payload to avoid user lookup in db
|
||||
user = User.get_by_id(payload.get('id'))
|
||||
|
||||
if not user:
|
||||
return None
|
||||
|
||||
try:
|
||||
if BaseUser.signer(sha256(user.password).hexdigest()).loads(token):
|
||||
return user
|
||||
else:
|
||||
return None
|
||||
except BadSignature:
|
||||
return None
|
||||
|
||||
|
||||
class User(Model, BaseUser):
|
||||
__tablename__ = 'users'
|
||||
type = 'local'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(128), unique=True)
|
||||
email = db.Column(db.String(128), unique=True)
|
||||
password = db.Column(db.String(60))
|
||||
admin = False
|
||||
|
||||
hidden_fields = ['password']
|
||||
readonly_fields = ['email', 'password']
|
||||
|
||||
@property
|
||||
def auth_token_id(self):
|
||||
return self.password
|
||||
|
||||
@staticmethod
|
||||
def load_user(*args, **kwargs):
|
||||
return User.get_by_id(args[0])
|
||||
|
||||
@staticmethod
|
||||
def create(username, email, password):
|
||||
u = User()
|
||||
u.username = username
|
||||
u.email = email
|
||||
u.password = User.hash_password(password)
|
||||
u.save()
|
||||
|
||||
@staticmethod
|
||||
def get_by_username(username):
|
||||
return User.query().filter_by(username=username).first()
|
||||
|
||||
@staticmethod
|
||||
def get_by_email(email):
|
||||
return User.query().filter_by(email=email).first()
|
||||
|
||||
@staticmethod
|
||||
def signer(salt):
|
||||
return URLSafeSerializer(current_app.config['SECRET_KEY'] + salt)
|
||||
|
||||
@staticmethod
|
||||
def auth(email, password):
|
||||
user = User.get_by_email(email)
|
||||
|
||||
if not user:
|
||||
# User doesn't exist
|
||||
return False
|
||||
|
||||
if User.check_password(password, user.password):
|
||||
# Password is good, log in user
|
||||
login_user(user, remember=True)
|
||||
return user
|
||||
else:
|
||||
# Password check failed
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def hash_password(password):
|
||||
return bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt(12))
|
||||
|
||||
@staticmethod
|
||||
def check_password(password, hashed):
|
||||
return bcrypt.hashpw(password.encode('utf-8'), hashed.encode('utf-8')) == hashed
|
||||
|
||||
@classmethod
|
||||
def logout(cls):
|
||||
logout_user()
|
||||
|
||||
@staticmethod
|
||||
def login_form():
|
||||
form = LoginForm()
|
||||
return render_template('auth/local/login.html', form=form)
|
||||
|
51
realms/modules/auth/local/views.py
Normal file
51
realms/modules/auth/local/views.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
from flask import current_app, render_template, request, redirect, Blueprint, flash, url_for
|
||||
from realms.modules.auth.local.models import User
|
||||
from realms.modules.auth.local.forms import LoginForm, RegistrationForm
|
||||
|
||||
blueprint = Blueprint('auth.local', __name__)
|
||||
|
||||
|
||||
@blueprint.route("/login/local", methods=['POST'])
|
||||
def login():
|
||||
form = LoginForm()
|
||||
|
||||
if not form.validate():
|
||||
flash('Form invalid', 'warning')
|
||||
return redirect(url_for('auth.login'))
|
||||
|
||||
if User.auth(request.form['email'], request.form['password']):
|
||||
return redirect(request.args.get("next") or url_for(current_app.config['ROOT_ENDPOINT']))
|
||||
else:
|
||||
flash('Email or Password Incorrect', 'warning')
|
||||
return redirect(url_for('auth.login'))
|
||||
|
||||
|
||||
@blueprint.route("/register", methods=['GET', 'POST'])
|
||||
def register():
|
||||
|
||||
if not current_app.config['REGISTRATION_ENABLED']:
|
||||
flash("Registration is disabled")
|
||||
return redirect(url_for(current_app.config['ROOT_ENDPOINT']))
|
||||
|
||||
form = RegistrationForm()
|
||||
|
||||
if request.method == "POST":
|
||||
|
||||
if not form.validate():
|
||||
flash('Form invalid', 'warning')
|
||||
return redirect(url_for('auth.local.register'))
|
||||
|
||||
if User.get_by_username(request.form['username']):
|
||||
flash('Username is taken', 'warning')
|
||||
return redirect(url_for('auth.local.register'))
|
||||
|
||||
if User.get_by_email(request.form['email']):
|
||||
flash('Email is taken', 'warning')
|
||||
return redirect(url_for('auth.local.register'))
|
||||
|
||||
User.create(request.form['username'], request.form['email'], request.form['password'])
|
||||
User.auth(request.form['email'], request.form['password'])
|
||||
|
||||
return redirect(request.args.get("next") or url_for(current_app.config['ROOT_ENDPOINT']))
|
||||
|
||||
return render_template("auth/register.html", form=form)
|
Loading…
Add table
Add a link
Reference in a new issue