first pass, non-working
This commit is contained in:
parent
459a9c2c59
commit
3c2f4a0445
|
@ -161,4 +161,4 @@ if ENV != "DEV":
|
||||||
ASSETS_DEBUG = False
|
ASSETS_DEBUG = False
|
||||||
SQLALCHEMY_ECHO = False
|
SQLALCHEMY_ECHO = False
|
||||||
|
|
||||||
MODULES = ['wiki', 'auth', 'search']
|
MODULES = ['wiki', 'search', 'auth', 'auth.local', 'auth.oauth']
|
||||||
|
|
0
realms/modules/auth/ldap/__init__.py
Normal file
0
realms/modules/auth/ldap/__init__.py
Normal file
0
realms/modules/auth/local/__init__.py
Normal file
0
realms/modules/auth/local/__init__.py
Normal file
|
@ -1,9 +1,10 @@
|
||||||
import click
|
import click
|
||||||
from realms.lib.util import random_string
|
from realms.lib.util import random_string
|
||||||
from realms.modules.auth.models import User
|
from realms.modules.auth.local.models import User
|
||||||
from realms.lib.util import green, red, yellow
|
from realms.lib.util import green, red, yellow
|
||||||
from realms import flask_cli
|
from realms import flask_cli
|
||||||
|
|
||||||
|
|
||||||
@flask_cli.group(short_help="Auth Module")
|
@flask_cli.group(short_help="Auth Module")
|
||||||
def cli():
|
def cli():
|
||||||
pass
|
pass
|
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)
|
|
@ -1,39 +1,41 @@
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from flask.ext.login import UserMixin, logout_user, login_user, AnonymousUserMixin
|
from flask.ext.login import UserMixin, logout_user, login_user, AnonymousUserMixin
|
||||||
from realms import login_manager, db
|
from realms import login_manager
|
||||||
from realms.lib.model import Model
|
|
||||||
from realms.lib.util import gravatar_url
|
from realms.lib.util import gravatar_url
|
||||||
from itsdangerous import URLSafeSerializer, BadSignature
|
from itsdangerous import URLSafeSerializer, BadSignature
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
import bcrypt
|
import bcrypt
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
|
||||||
@login_manager.user_loader
|
@login_manager.user_loader
|
||||||
def load_user(user_id):
|
def load_user(auth_id):
|
||||||
return User.get_by_id(user_id)
|
return Auth.load_user(auth_id)
|
||||||
|
|
||||||
|
auth_users = {}
|
||||||
|
|
||||||
|
|
||||||
@login_manager.token_loader
|
class Auth(object):
|
||||||
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:
|
@staticmethod
|
||||||
return None
|
def get_auth_user(auth_type):
|
||||||
|
print auth_type
|
||||||
|
mod = importlib.import_module('realms.modules.auth.%s.models' % auth_type)
|
||||||
|
return mod.User
|
||||||
|
|
||||||
# User key *could* be stored in payload to avoid user lookup in db
|
@staticmethod
|
||||||
user = User.get_by_id(payload.get('id'))
|
def load_user(auth_id):
|
||||||
|
print auth_id
|
||||||
|
auth_type, user_id = auth_id.split("/")
|
||||||
|
return Auth.get_auth_user(auth_type).load_user(user_id)
|
||||||
|
|
||||||
if not user:
|
@staticmethod
|
||||||
return None
|
def login_forms():
|
||||||
|
forms = []
|
||||||
try:
|
# TODO be dynamic
|
||||||
if User.signer(sha256(user.password).hexdigest()).loads(token):
|
for t in ['local']:
|
||||||
return user
|
forms.append(Auth.get_auth_user(t).login_form())
|
||||||
else:
|
return forms
|
||||||
return None
|
|
||||||
except BadSignature:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class AnonUser(AnonymousUserMixin):
|
class AnonUser(AnonymousUserMixin):
|
||||||
|
@ -42,40 +44,42 @@ class AnonUser(AnonymousUserMixin):
|
||||||
admin = False
|
admin = False
|
||||||
|
|
||||||
|
|
||||||
class User(Model, UserMixin):
|
class BaseUser(UserMixin):
|
||||||
__tablename__ = 'users'
|
id = None
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
email = None
|
||||||
username = db.Column(db.String(128), unique=True)
|
username = None
|
||||||
email = db.Column(db.String(128), unique=True)
|
type = 'base'
|
||||||
password = db.Column(db.String(60))
|
|
||||||
admin = False
|
|
||||||
|
|
||||||
hidden_fields = ['password']
|
def get_id(self):
|
||||||
readonly_fields = ['email', 'password']
|
return unicode("%s/%s" % (self.type, self.id))
|
||||||
|
|
||||||
def get_auth_token(self):
|
def get_auth_token(self):
|
||||||
key = sha256(self.password).hexdigest()
|
key = sha256(self.auth_token_id).hexdigest()
|
||||||
return User.signer(key).dumps(dict(id=self.id))
|
return BaseUser.signer(key).dumps(dict(id=self.id))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def auth_token_id(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def avatar(self):
|
def avatar(self):
|
||||||
return gravatar_url(self.email)
|
return gravatar_url(self.email)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create(username, email, password):
|
def load_user(*args, **kwargs):
|
||||||
u = User()
|
raise NotImplementedError
|
||||||
u.username = username
|
|
||||||
u.email = email
|
@staticmethod
|
||||||
u.password = User.hash_password(password)
|
def create(*args, **kwargs):
|
||||||
u.save()
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_by_username(username):
|
def get_by_username(username):
|
||||||
return User.query().filter_by(username=username).first()
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_by_email(email):
|
def get_by_email(email):
|
||||||
return User.query().filter_by(email=email).first()
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def signer(salt):
|
def signer(salt):
|
||||||
|
@ -83,19 +87,7 @@ class User(Model, UserMixin):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def auth(email, password):
|
def auth(email, password):
|
||||||
user = User.get_by_email(email)
|
raise NotImplementedError()
|
||||||
|
|
||||||
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
|
@staticmethod
|
||||||
def hash_password(password):
|
def hash_password(password):
|
||||||
|
@ -109,4 +101,8 @@ class User(Model, UserMixin):
|
||||||
def logout(cls):
|
def logout(cls):
|
||||||
logout_user()
|
logout_user()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def login_form():
|
||||||
|
pass
|
||||||
|
|
||||||
login_manager.anonymous_user = AnonUser
|
login_manager.anonymous_user = AnonUser
|
||||||
|
|
0
realms/modules/auth/oauth/__init__.py
Normal file
0
realms/modules/auth/oauth/__init__.py
Normal file
36
realms/modules/auth/oauth/models.py
Normal file
36
realms/modules/auth/oauth/models.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
from flask import render_template
|
||||||
|
from flask_oauthlib.client import OAuth
|
||||||
|
from realms import config
|
||||||
|
from ..models import BaseUser
|
||||||
|
|
||||||
|
oauth = OAuth()
|
||||||
|
|
||||||
|
|
||||||
|
class OAuthUser(BaseUser):
|
||||||
|
# OAuth remote app
|
||||||
|
app = None
|
||||||
|
|
||||||
|
|
||||||
|
class TwitterUser(OAuthUser):
|
||||||
|
|
||||||
|
app = oauth.remote_app(
|
||||||
|
'twitter',
|
||||||
|
base_url='https://api.twitter.com/1/',
|
||||||
|
request_token_url='https://api.twitter.com/oauth/request_token',
|
||||||
|
access_token_url='https://api.twitter.com/oauth/access_token',
|
||||||
|
authorize_url='https://api.twitter.com/oauth/authenticate',
|
||||||
|
consumer_key=config.TWITTER_KEY,
|
||||||
|
consumer_secret=config.TWITTER_SECRET)
|
||||||
|
|
||||||
|
def __init__(self, id_, username, email=None):
|
||||||
|
self.id = id_
|
||||||
|
self.username = username
|
||||||
|
self.email = email
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_user(*args, **kwargs):
|
||||||
|
return TwitterUser(args[0])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def login_form():
|
||||||
|
return render_template('auth/oauth/twitter.html')
|
30
realms/modules/auth/oauth/views.py
Normal file
30
realms/modules/auth/oauth/views.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
from flask import Blueprint, url_for, request, flash, redirect
|
||||||
|
from .models import TwitterUser
|
||||||
|
|
||||||
|
blueprint = Blueprint('auth.oauth', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
def oauth_failed(next_url):
|
||||||
|
flash(u'You denied the request to sign in.')
|
||||||
|
return redirect(next_url)
|
||||||
|
|
||||||
|
@blueprint.route("/login/twitter")
|
||||||
|
def login_twitter():
|
||||||
|
return TwitterUser.app.authorize(callback=url_for('twitter_callback',
|
||||||
|
next=request.args.get('next') or request.referrer or None))
|
||||||
|
|
||||||
|
@blueprint.route('/login/twitter/callback')
|
||||||
|
def twitter_callback():
|
||||||
|
next_url = request.args.get('next') or url_for('index')
|
||||||
|
resp = TwitterUser.app.authorized_response()
|
||||||
|
if resp is None:
|
||||||
|
return oauth_failed(next_url)
|
||||||
|
|
||||||
|
session['twitter_token'] = (
|
||||||
|
resp['oauth_token'],
|
||||||
|
resp['oauth_token_secret']
|
||||||
|
)
|
||||||
|
session['twitter_user'] = resp['screen_name']
|
||||||
|
|
||||||
|
flash('You were signed in as %s' % resp['screen_name'])
|
||||||
|
return redirect(next_url)
|
|
@ -1,72 +1,22 @@
|
||||||
from flask import current_app, render_template, request, redirect, Blueprint, flash, url_for
|
from flask import current_app, render_template, request, redirect, Blueprint, flash, url_for
|
||||||
from realms.modules.auth.models import User
|
from flask.ext.login import logout_user
|
||||||
from realms.modules.auth.forms import LoginForm, RegistrationForm
|
from realms.modules.auth.models import Auth
|
||||||
|
|
||||||
blueprint = Blueprint('auth', __name__)
|
blueprint = Blueprint('auth', __name__)
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/logout")
|
|
||||||
def logout_page():
|
|
||||||
User.logout()
|
|
||||||
flash("You are now logged out")
|
|
||||||
return redirect(url_for(current_app.config['ROOT_ENDPOINT']))
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/login", methods=['GET', 'POST'])
|
@blueprint.route("/login", methods=['GET', 'POST'])
|
||||||
def login():
|
def login():
|
||||||
form = LoginForm()
|
return render_template("auth/login.html", forms=Auth.login_forms())
|
||||||
|
|
||||||
if request.method == "POST":
|
|
||||||
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'))
|
|
||||||
|
|
||||||
return render_template("auth/login.html", form=form)
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/register", methods=['GET', 'POST'])
|
@blueprint.route("/logout")
|
||||||
def register():
|
def logout():
|
||||||
|
logout_user()
|
||||||
if not current_app.config['REGISTRATION_ENABLED']:
|
flash("You are now logged out")
|
||||||
flash("Registration is disabled")
|
|
||||||
return redirect(url_for(current_app.config['ROOT_ENDPOINT']))
|
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.register'))
|
|
||||||
|
|
||||||
if User.get_by_username(request.form['username']):
|
|
||||||
flash('Username is taken', 'warning')
|
|
||||||
return redirect(url_for('auth.register'))
|
|
||||||
|
|
||||||
if User.get_by_email(request.form['email']):
|
|
||||||
flash('Email is taken', 'warning')
|
|
||||||
return redirect(url_for('auth.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)
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/settings", methods=['GET', 'POST'])
|
@blueprint.route("/settings", methods=['GET', 'POST'])
|
||||||
def settings():
|
def settings():
|
||||||
return render_template("auth/settings.html")
|
return render_template("auth/settings.html")
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/logout")
|
|
||||||
def logout():
|
|
||||||
User.logout()
|
|
||||||
return redirect(url_for(current_app.config['ROOT_ENDPOINT']))
|
|
||||||
|
|
5
realms/templates/auth/local/login.html
Normal file
5
realms/templates/auth/local/login.html
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{% from 'macros.html' import render_form, render_field %}
|
||||||
|
{% call render_form(form, action_url=url_for('auth.local.login'), action_text='Login', btn_class='btn btn-primary') %}
|
||||||
|
{{ render_field(form.email, placeholder='Email', type='email', required=1) }}
|
||||||
|
{{ render_field(form.password, placeholder='Password', type='password', required=1) }}
|
||||||
|
{% endcall %}
|
|
@ -1,8 +1,6 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% from 'macros.html' import render_form, render_field %}
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{% call render_form(form, action_url=url_for('auth.login'), action_text='Login', btn_class='btn btn-primary') %}
|
{% for form in forms %}
|
||||||
{{ render_field(form.email, placeholder='Email', type='email', required=1) }}
|
{{ form|safe }}
|
||||||
{{ render_field(form.password, placeholder='Password', type='password', required=1) }}
|
{% endfor %}
|
||||||
{% endcall %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% from 'macros.html' import render_form, render_field %}
|
{% from 'macros.html' import render_form, render_field %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{% call render_form(form, action_url=url_for('auth.register'), action_text='Register', btn_class='btn btn-primary') %}
|
{% call render_form(form, action_url=url_for('auth.local.register'), action_text='Register', btn_class='btn btn-primary') %}
|
||||||
{{ render_field(form.username, placeholder='Username', type='username', **{"required": 1, "data-parsley-type": "alphanum"}) }}
|
{{ render_field(form.username, placeholder='Username', type='username', **{"required": 1, "data-parsley-type": "alphanum"}) }}
|
||||||
{{ render_field(form.email, placeholder='Email', type='email', required=1) }}
|
{{ render_field(form.email, placeholder='Email', type='email', required=1) }}
|
||||||
{{ render_field(form.password, placeholder='Password', type='password', **{"required": 1, "data-parsley-minlength": "6"}) }}
|
{{ render_field(form.password, placeholder='Password', type='password', **{"required": 1, "data-parsley-minlength": "6"}) }}
|
||||||
|
|
|
@ -74,7 +74,7 @@
|
||||||
{% else %}
|
{% else %}
|
||||||
<li><a href="{{ url_for('auth.login') }}"><i class="fa fa-user"></i> Login</a></li>
|
<li><a href="{{ url_for('auth.login') }}"><i class="fa fa-user"></i> Login</a></li>
|
||||||
{% if config.REGISTRATION_ENABLED %}
|
{% if config.REGISTRATION_ENABLED %}
|
||||||
<li><a href="{{ url_for('auth.register') }}"><i class="fa fa-users"></i> Register</a></li>
|
<li><a href="{{ url_for('auth.local.register') }}"><i class="fa fa-users"></i> Register</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
Loading…
Reference in a new issue