mirror of
https://github.com/aronwk-aaron/MSState-Library-ETD.git
synced 2026-01-24 14:28:25 -06:00
Login form implemented
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
from flask import Flask
|
||||
from flask_assets import Environment
|
||||
from webassets import Bundle
|
||||
|
||||
from app.forms import CustomUserManager
|
||||
from app.models import db, migrate, User
|
||||
from app.schemas import ma
|
||||
from flask_mail import Mail
|
||||
from flask_user import UserManager
|
||||
from flask_wtf.csrf import CSRFProtect
|
||||
|
||||
# Instantiate Flask extensions
|
||||
@@ -55,7 +56,7 @@ def register_extensions(app):
|
||||
ma.init_app(app)
|
||||
mail.init_app(app)
|
||||
csrf_protect.init_app(app)
|
||||
user_manager = UserManager(app, db, User)
|
||||
user_manager = CustomUserManager(app, db, User)
|
||||
@app.context_processor
|
||||
def context_processor():
|
||||
return dict(user_manager=user_manager)
|
||||
@@ -75,8 +76,8 @@ def register_blueprints(app):
|
||||
from .main import main_blueprint
|
||||
app.register_blueprint(main_blueprint)
|
||||
|
||||
from .auth import auth_blueprint
|
||||
app.register_blueprint(auth_blueprint)
|
||||
# from .auth import auth_blueprint
|
||||
# app.register_blueprint(auth_blueprint)
|
||||
|
||||
|
||||
def init_email_error_handler(app):
|
||||
|
||||
53
app/forms.py
Normal file
53
app/forms.py
Normal file
@@ -0,0 +1,53 @@
|
||||
from flask import current_app, flash
|
||||
from flask_user import UserManager
|
||||
from flask_user.forms import RegisterForm
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, HiddenField, PasswordField, BooleanField, SubmitField
|
||||
from wtforms.validators import DataRequired
|
||||
|
||||
from app import models
|
||||
|
||||
|
||||
class CustomUserManager(UserManager):
|
||||
|
||||
# noinspection PyAttributeOutsideInit
|
||||
def customize(self, app):
|
||||
self.LoginFormClass = CustomLoginForm
|
||||
|
||||
|
||||
class CustomLoginForm(FlaskForm):
|
||||
"""Login form"""
|
||||
next = HiddenField()
|
||||
reg_next = HiddenField()
|
||||
|
||||
netid = StringField('NetID', validators=[
|
||||
DataRequired('NetID is required'),
|
||||
])
|
||||
password = PasswordField('Password', validators=[
|
||||
DataRequired('Password is required'),
|
||||
])
|
||||
remember_me = BooleanField('Remember me')
|
||||
|
||||
submit = SubmitField('Sign in')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CustomLoginForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def validate(self):
|
||||
# grab the user manager
|
||||
user_manager = current_app.user_manager
|
||||
|
||||
# handle invalid fields
|
||||
if not super(CustomLoginForm, self).validate():
|
||||
return False
|
||||
|
||||
# find user by netid
|
||||
user = user_manager.db_manager.db_adapter.ifind_first_object(models.User, netid=self.netid.data)
|
||||
|
||||
# handle successful authentication
|
||||
if user and user_manager.verify_password(self.password.data, user.password):
|
||||
return True
|
||||
|
||||
# unsuccessful authentication
|
||||
flash('Invalid NetID or Password', 'error')
|
||||
return False
|
||||
42
app/templates/flask_user/_macros.html
Normal file
42
app/templates/flask_user/_macros.html
Normal file
@@ -0,0 +1,42 @@
|
||||
{% macro render_field(field, label=None, label_visible=true, right_url=None, right_label=None) -%}
|
||||
<div class="form-group {% if field.errors %}has-error{% endif %} {{ kwargs.pop('class_', '') }}">
|
||||
{% if field.type != 'HiddenField' and label_visible %}
|
||||
{% if not label %}{% set label=field.label.text %}{% endif %}
|
||||
<label for="{{ field.id }}" class="control-label">{{ label|safe }}</label>
|
||||
{% endif %}
|
||||
{{ field(class_='form-control', **kwargs) }}
|
||||
{% if field.errors %}
|
||||
{% for e in field.errors %}
|
||||
<p class="help-block">{{ e }}</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro render_checkbox_field(field, label=None) -%}
|
||||
{% if not label %}{% set label=field.label.text %}{% endif %}
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
{{ field(type='checkbox', **kwargs) }} {{ label }}
|
||||
</label>
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro render_radio_field(field) -%}
|
||||
{% for value, label, checked in field.iter_choices() %}
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" name="{{ field.id }}" id="{{ field.id }}" value="{{ value }}"{% if checked %} checked{% endif %}>
|
||||
{{ label }}
|
||||
</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro render_submit_field(field, label=None, tabindex=None) -%}
|
||||
{% if not label %}{% set label=field.label.text %}{% endif %}
|
||||
{#<button type="submit" class="form-control btn btn-default btn-primary">{{label}}</button>#}
|
||||
<input type="submit" class="btn btn-default btn-primary" value="{{label}}"
|
||||
{% if tabindex %}tabindex="{{ tabindex }}"{% endif %}
|
||||
>
|
||||
{%- endmacro %}
|
||||
1
app/templates/flask_user/_public_base.html
Normal file
1
app/templates/flask_user/_public_base.html
Normal file
@@ -0,0 +1 @@
|
||||
{% extends 'base.jinja2' %}
|
||||
17
app/templates/flask_user/change_password.html
Normal file
17
app/templates/flask_user/change_password.html
Normal file
@@ -0,0 +1,17 @@
|
||||
{% extends 'flask_user/_authorized_base.html' %}
|
||||
|
||||
{% block content %}
|
||||
{% from "flask_user/_macros.html" import render_field, render_submit_field %}
|
||||
<h1>{%trans%}Change password{%endtrans%}</h1>
|
||||
|
||||
<form action="" method="POST" class="form" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{{ render_field(form.old_password, tabindex=10) }}
|
||||
{{ render_field(form.new_password, tabindex=20) }}
|
||||
{% if user_manager.USER_REQUIRE_RETYPE_PASSWORD %}
|
||||
{{ render_field(form.retype_password, tabindex=30) }}
|
||||
{% endif %}
|
||||
{{ render_submit_field(form.submit, tabindex=90) }}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
14
app/templates/flask_user/change_username.html
Normal file
14
app/templates/flask_user/change_username.html
Normal file
@@ -0,0 +1,14 @@
|
||||
{% extends 'flask_user/_authorized_base.html' %}
|
||||
|
||||
{% block content %}
|
||||
{% from "flask_user/_macros.html" import render_field, render_submit_field %}
|
||||
<h1>{%trans%}Change username{%endtrans%}</h1>
|
||||
|
||||
<form action="" method="POST" class="form" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{{ render_field(form.new_username, tabindex=10) }}
|
||||
{{ render_field(form.old_password, tabindex=20) }}
|
||||
{{ render_submit_field(form.submit, tabindex=90) }}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
30
app/templates/flask_user/edit_user_profile.html
Normal file
30
app/templates/flask_user/edit_user_profile.html
Normal file
@@ -0,0 +1,30 @@
|
||||
{% extends 'flask_user/_authorized_base.html' %}
|
||||
|
||||
{% block content %}
|
||||
{% from "flask_user/_macros.html" import render_field, render_checkbox_field, render_submit_field %}
|
||||
<h1>{%trans%}User profile{%endtrans%}</h1>
|
||||
|
||||
<form action="" method="POST" class="form" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{% for field in form %}
|
||||
{% if not field.flags.hidden %}
|
||||
{% if field.type=='SubmitField' %}
|
||||
{{ render_submit_field(field, tabindex=loop.index*10) }}
|
||||
{% else %}
|
||||
{{ render_field(field, tabindex=loop.index*10) }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</form>
|
||||
<br/>
|
||||
|
||||
{% if not user_manager.USER_ENABLE_AUTH0 %}
|
||||
{% if user_manager.USER_ENABLE_CHANGE_USERNAME %}
|
||||
<p><a href="{{ url_for('user.change_username') }}">{%trans%}Change username{%endtrans%}</a></p>
|
||||
{% endif %}
|
||||
{% if user_manager.USER_ENABLE_CHANGE_PASSWORD %}
|
||||
<p><a href="{{ url_for('user.change_password') }}">{%trans%}Change password{%endtrans%}</a></p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
13
app/templates/flask_user/forgot_password.html
Normal file
13
app/templates/flask_user/forgot_password.html
Normal file
@@ -0,0 +1,13 @@
|
||||
{% extends 'flask_user/_public_base.html' %}
|
||||
|
||||
{% block content %}
|
||||
{% from "flask_user/_macros.html" import render_field, render_submit_field %}
|
||||
<h1>{%trans%}Forgot Password{%endtrans%}</h1>
|
||||
|
||||
<form action="" method="POST" class="form" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{{ render_field(form.email, tabindex=10) }}
|
||||
{{ render_submit_field(form.submit, tabindex=90) }}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
13
app/templates/flask_user/invite_user.html
Normal file
13
app/templates/flask_user/invite_user.html
Normal file
@@ -0,0 +1,13 @@
|
||||
{% extends 'flask_user/_authorized_base.html' %}
|
||||
|
||||
{% block content %}
|
||||
{% from "flask_user/_macros.html" import render_field, render_submit_field %}
|
||||
<h1>{%trans%}Invite User{%endtrans%}</h1>
|
||||
|
||||
<form action="" method="POST" class="form" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{{ render_field(form.email, tabindex=10) }}
|
||||
{{ render_submit_field(form.submit, tabindex=90) }}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
116
app/templates/flask_user/login.html
Normal file
116
app/templates/flask_user/login.html
Normal file
@@ -0,0 +1,116 @@
|
||||
{% extends 'flask_user/_public_base.html' %}
|
||||
|
||||
{% set navbar_shadow = True %}
|
||||
{% block title %}Login{% endblock %}
|
||||
{% block header %}{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ super() }}
|
||||
{# override the body and html css to force center #}
|
||||
<style>
|
||||
html, body {
|
||||
height: 100%;
|
||||
background-color: #777777;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% from "flask_user/_macros.html" import render_field, render_checkbox_field, render_submit_field %}
|
||||
|
||||
{% block content_before %}
|
||||
<div class="container-fluid h-100">
|
||||
<div class="row h-100 justify-content-center align-items-center">
|
||||
<div class="mx-auto" style="max-width: 30em;">
|
||||
|
||||
{# Splash card #}
|
||||
<div class="card shadow rounded border-0 h-100">
|
||||
<div class="card-header bg-primary pt-4 text-center text-white">
|
||||
<img class="img-fluid mb-3" src="http://lib.msstate.edu/_assets/img/2015-header-logo-msstate.png"
|
||||
alt=""/>
|
||||
<h5>Electronic Thesis and Dissertation System</h5>
|
||||
</div>
|
||||
|
||||
<div class="card-body d-flex flex-column m-2">
|
||||
|
||||
<form action="" method="POST" class="form" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<h3>
|
||||
Login
|
||||
</h3>
|
||||
|
||||
<hr class="mb-4"/>
|
||||
|
||||
{# Show any errors #}
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-warning" role="alert">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
|
||||
|
||||
{# NetID field #}
|
||||
{% set field = form.netid %}
|
||||
<div class="form-group {% if field.errors %}has-error{% endif %}">
|
||||
{# Label on left, "New here? Register." on right #}
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<label for="{{ field.id }}" class="font-weight-bold">{{ field.label.text }}</label>
|
||||
</div>
|
||||
<div class="col-6 text-right">
|
||||
{% if user_manager.USER_ENABLE_REGISTER and not user_manager.USER_REQUIRE_INVITATION %}
|
||||
<a href="{{ url_for('user.register') }}" class="text-primary" tabindex='190'>
|
||||
{%trans%}New here? Register.{%endtrans%}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{{ field(class_='form-control', tabindex=110) }}
|
||||
{% if field.errors %}
|
||||
{% for e in field.errors %}
|
||||
<p class="help-block">{{ e }}</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{# Password field #}
|
||||
{% set field = form.password %}
|
||||
<div class="form-group {% if field.errors %}has-error{% endif %}">
|
||||
{# Label on left, "Forgot your Password?" on right #}
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<label for="{{ field.id }}" class="font-weight-bold">{{ field.label.text }}</label>
|
||||
</div>
|
||||
<div class="col-6 text-right">
|
||||
{% if user_manager.USER_ENABLE_FORGOT_PASSWORD %}
|
||||
<a href="{{ url_for('user.forgot_password') }}" class="text-primary" tabindex='195'>
|
||||
{%trans%}Forgot your Password?{%endtrans%}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{{ field(class_='form-control', tabindex=120) }}
|
||||
{% if field.errors %}
|
||||
{% for e in field.errors %}
|
||||
<p class="help-block">{{ e }}</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{# Remember me #}
|
||||
{% if user_manager.USER_ENABLE_REMEMBER_ME %}
|
||||
{{ render_checkbox_field(login_form.remember_me, tabindex=130) }}
|
||||
{% endif %}
|
||||
|
||||
{# Submit button #}
|
||||
{{ render_submit_field(form.submit, tabindex=180) }}
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
39
app/templates/flask_user/manage_emails.html
Normal file
39
app/templates/flask_user/manage_emails.html
Normal file
@@ -0,0 +1,39 @@
|
||||
{% extends 'flask_user/_authorized_base.html' %}
|
||||
|
||||
{% block content %}
|
||||
{% from "flask_user/_macros.html" import render_field, render_submit_field %}
|
||||
<h1>{%trans%}Manage Emails{%endtrans%}</h1>
|
||||
|
||||
<table class="table">
|
||||
<tr><th>Email</th><th>Status</th><th>Actions</th></tr>
|
||||
{% for user_email in user_emails %}
|
||||
<tr>
|
||||
<td>{{ user_email.email }}</td>
|
||||
<td>
|
||||
{% if user_email.email_confirmed_at %}
|
||||
Confirmed
|
||||
{% else %}
|
||||
<a href="{{ url_for('user.email_action', id=user_email.id, action='confirm') }}">Confirm Email</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if user_email.is_primary %}
|
||||
<b>Primary email</b>
|
||||
{% else %}
|
||||
{% if user_email.email_confirmed_at %}
|
||||
<a href="{{ url_for('user.email_action', id=user_email.id, action='make-primary') }}">Make primary</a> |
|
||||
{% endif %}
|
||||
<a href="{{ url_for('user.email_action', id=user_email.id, action='delete') }}">Delete</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<form action="" method="POST" class="form" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{{ render_field(form.email) }}
|
||||
{{ render_submit_field(form.submit) }}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
46
app/templates/flask_user/register.html
Normal file
46
app/templates/flask_user/register.html
Normal file
@@ -0,0 +1,46 @@
|
||||
{% extends 'flask_user/_public_base.html' %}
|
||||
|
||||
{% block content %}
|
||||
{% from "flask_user/_macros.html" import render_field, render_submit_field %}
|
||||
<h1>{%trans%}Register{%endtrans%}</h1>
|
||||
|
||||
<form action="" method="POST" novalidate formnovalidate class="form" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
{# Username or Email #}
|
||||
{% set field = form.username if user_manager.USER_ENABLE_USERNAME else form.email %}
|
||||
<div class="form-group {% if field.errors %}has-error{% endif %}">
|
||||
{# Label on left, "Already registered? Sign in." on right #}
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<label for="{{ field.id }}" class="control-label">{{ field.label.text }}</label>
|
||||
</div>
|
||||
<div class="col-xs-6 text-right">
|
||||
{% if user_manager.USER_ENABLE_REGISTER %}
|
||||
<a href="{{ url_for('user.login') }}" tabindex='290'>
|
||||
{%trans%}Already registered? Sign in.{%endtrans%}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{{ field(class_='form-control', tabindex=210) }}
|
||||
{% if field.errors %}
|
||||
{% for e in field.errors %}
|
||||
<p class="help-block">{{ e }}</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if user_manager.USER_ENABLE_EMAIL and user_manager.USER_ENABLE_USERNAME %}
|
||||
{{ render_field(form.email, tabindex=220) }}
|
||||
{% endif %}
|
||||
|
||||
{{ render_field(form.password, tabindex=230) }}
|
||||
|
||||
{% if user_manager.USER_REQUIRE_RETYPE_PASSWORD %}
|
||||
{{ render_field(form.retype_password, tabindex=240) }}
|
||||
{% endif %}
|
||||
|
||||
{{ render_submit_field(form.submit, tabindex=280) }}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
13
app/templates/flask_user/resend_confirm_email.html
Normal file
13
app/templates/flask_user/resend_confirm_email.html
Normal file
@@ -0,0 +1,13 @@
|
||||
{% extends 'flask_user/_public_base.html' %}
|
||||
|
||||
{% block content %}
|
||||
{% from "flask_user/_macros.html" import render_field, render_submit_field %}
|
||||
<h1>{%trans%}Resend Confirmation Email{%endtrans%}</h1>
|
||||
|
||||
<form action="" method="POST" class="form" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{{ render_field(form.email, tabindex=10) }}
|
||||
{{ render_submit_field(form.submit, tabindex=90) }}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
16
app/templates/flask_user/reset_password.html
Normal file
16
app/templates/flask_user/reset_password.html
Normal file
@@ -0,0 +1,16 @@
|
||||
{% extends 'flask_user/_public_base.html' %}
|
||||
|
||||
{% block content %}
|
||||
{% from "flask_user/_macros.html" import render_field, render_submit_field %}
|
||||
<h1>{%trans%}Reset Password{%endtrans%}</h1>
|
||||
|
||||
<form action="" method="POST" class="form" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
{{ render_field(form.new_password, tabindex=10) }}
|
||||
{% if user_manager.USER_REQUIRE_RETYPE_PASSWORD %}
|
||||
{{ render_field(form.retype_password, tabindex=20) }}
|
||||
{% endif %}
|
||||
{{ render_submit_field(form.submit, tabindex=90) }}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
@@ -36,7 +36,7 @@
|
||||
</p>
|
||||
|
||||
{# TODO: link to CAS sign in #}
|
||||
<a href="{{ url_for("auth.login") }}" class="btn btn-primary btn-lg mt-auto align-self-center px-4">
|
||||
<a href="{{ url_for("user.login") }}" class="btn btn-primary btn-lg mt-auto align-self-center px-4">
|
||||
Sign in with CAS
|
||||
</a>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user