Browse Source

Big login update, share login cookies with account app

mistress
Daniel Muckerman 3 years ago
parent
commit
416c8d46d2
7 changed files with 155 additions and 52 deletions
  1. +3
    -1
      .gitignore
  2. +141
    -38
      app.py
  3. +3
    -1
      requirements.txt
  4. +2
    -1
      templates/card_list.j2
  5. +2
    -2
      templates/games.j2
  6. +2
    -2
      templates/index.j2
  7. +2
    -7
      templates/login.j2

+ 3
- 1
.gitignore View File

@ -16,4 +16,6 @@ dashboard-env/
.envrc .envrc
.DS_Store .DS_Store
.vscode/
.vscode/
users.db

+ 141
- 38
app.py View File

@ -1,8 +1,14 @@
import ldap as l import ldap as l
from ldap3 import Server, Connection, ALL, MODIFY_REPLACE from ldap3 import Server, Connection, ALL, MODIFY_REPLACE
from flask import Flask, g, request, session, redirect, url_for, render_template
from ldap3.core.exceptions import LDAPBindError
from flask import Flask, g, request, session, redirect, url_for, render_template, flash
from flask_bs4 import Bootstrap from flask_bs4 import Bootstrap
from flask_simpleldap import LDAP
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, login_manager, current_user, login_user, \
logout_user, login_required
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired
import yaml import yaml
import datetime as dt import datetime as dt
import pytz import pytz
@ -14,6 +20,10 @@ Bootstrap(app)
app.secret_key = 'asdf' app.secret_key = 'asdf'
app.debug = True app.debug = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['WTF_CSRF_SECRET_KEY'] = 'asdf'
# Base # Base
app.config['LDAP_REALM_NAME'] = 'OpenLDAP Authentication' app.config['LDAP_REALM_NAME'] = 'OpenLDAP Authentication'
app.config['LDAP_HOST'] = os.environ.get('LDAP_HOST') app.config['LDAP_HOST'] = os.environ.get('LDAP_HOST')
@ -26,7 +36,18 @@ app.config['LDAP_OBJECTS_DN'] = 'dn'
app.config['LDAP_OPENLDAP'] = True app.config['LDAP_OPENLDAP'] = True
app.config['LDAP_USER_OBJECT_FILTER'] = '(&(objectclass=posixAccount)(uid=%s))' app.config['LDAP_USER_OBJECT_FILTER'] = '(&(objectclass=posixAccount)(uid=%s))'
ldap = LDAP(app)
# Login cookies
app.config['REMEMBER_COOKIE_DOMAIN'] = os.environ.get('COOKIE_DOMAIN')
db = SQLAlchemy(app)
login_manager = LoginManager(app)
login_manager.init_app(app)
login_manager.login_view = 'login'
db.create_all()
eastern = pytz.timezone('US/Eastern') eastern = pytz.timezone('US/Eastern')
@ -61,14 +82,74 @@ for itm in yaml_data['games'].items():
server = Server(app.config['LDAP_HOST']) server = Server(app.config['LDAP_HOST'])
conn = Connection(server, app.config['LDAP_USERNAME'], app.config['LDAP_PASSWORD'], auto_bind=True) conn = Connection(server, app.config['LDAP_USERNAME'], app.config['LDAP_PASSWORD'], auto_bind=True)
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(100))
password = db.Column(db.String(128))
authenticated = db.Column(db.Boolean, default=False)
def __init__(self, username, password):
self.username = username
self.password = password
@staticmethod
def try_login(username, password):
conn.search(app.config['LDAP_BASE_DN'], app.config['LDAP_USER_OBJECT_FILTER'] % username, attributes=['*'])
if len(conn.entries) > 0:
Connection(app.config['LDAP_HOST'], conn.entries[0].entry_dn, password, auto_bind=True)
return
raise LDAPBindError
def is_authenticated(self):
return self.authenticated
def is_active(self):
return True
def is_anonymous(self):
return False
def get_id(self):
return self.id
def get_user_dict(self):
user = {'dn': '',
'firstName': '',
'lastName': '',
'email': '',
'userName': self.username,
}
conn.search(app.config['LDAP_BASE_DN'], app.config['LDAP_USER_OBJECT_FILTER'] % self.username, attributes=['*'])
user['dn'] = conn.entries[0].entry_dn
user['firstName'] = conn.entries[0].givenName.value
user['lastName'] = conn.entries[0].sn.value
user['email'] = conn.entries[0].mail.value
return user
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
password = PasswordField('Password', validators=[DataRequired()])
remember_me = BooleanField('Remember Me')
submit = SubmitField('Sign In')
@login_manager.user_loader
def load_user(id):
return User.query.get(int(id))
@app.before_request @app.before_request
def before_request():
g.user = None
if 'user_id' in session:
# This is where you'd query your database to get the user info.
g.user = {}
def get_current_user():
g.user = current_user
@app.route('/') @app.route('/')
@ -83,46 +164,63 @@ def index():
@app.route('/games') @app.route('/games')
def game():
if 'user_id' in session:
user_dict = ldap.get_object_details(session['user_id'])
user = {'dn': 'cn={},cn=usergroup,ou=users,dc=technicalincompetence,dc=club'.format(user_dict['cn'][0].decode('ascii')),
'firstName': user_dict['givenName'][0].decode('ascii'),
'lastName': user_dict['sn'][0].decode('ascii'),
'email': user_dict['mail'][0].decode('ascii'),
'userName': user_dict['uid'][0].decode('ascii'),
}
def game():
current_time = eastern.localize(dt.datetime.now()) current_time = eastern.localize(dt.datetime.now())
if final_countdown_data != None: if final_countdown_data != None:
if (final_time - current_time).days > -1: if (final_time - current_time).days > -1:
return render_template('final_countdown.j2', final_countdown = final_countdown_data) return render_template('final_countdown.j2', final_countdown = final_countdown_data)
if countdown_data != None: if countdown_data != None:
return render_template('games.j2', apps = games, search = search, account_url = account_url, description = game_description, countdown = countdown_data) return render_template('games.j2', apps = games, search = search, account_url = account_url, description = game_description, countdown = countdown_data)
if 'user_id' in session:
return render_template('games.j2', apps = games, search = search, account_url = account_url, description = game_description, user = user, game_list = generate_game_list())
if current_user.is_authenticated:
return render_template('games.j2', apps = games, search = search, account_url = account_url, description = game_description, user = current_user.get_user_dict(), game_list = generate_game_list())
return render_template('games.j2', apps = games, search = search, account_url = account_url, description = game_description) return render_template('games.j2', apps = games, search = search, account_url = account_url, description = game_description)
@app.route('/login', methods=['GET', 'POST']) @app.route('/login', methods=['GET', 'POST'])
def login(): def login():
if g.user:
return redirect(url_for('index'))
if request.method == 'POST':
user = request.form['user']
passwd = request.form['passwd']
test = ldap.bind_user(user, passwd)
if test is None or passwd == '':
return render_template('login.j2', error='Invalid credentials')
else:
session['user_id'] = request.form['user']
session['passwd'] = request.form['passwd']
return redirect('/games')
return render_template('login.j2')
@ldap.login_required
if current_user.is_authenticated:
flash('You are already logged in.')
return redirect(url_for('auth.home'))
form = LoginForm(request.form)
print(form)
print(request.method)
if request.method == 'POST' and form.validate():
username = request.form.get('username')
password = request.form.get('password')
print(username)
print(password)
try:
User.try_login(username, password)
except LDAPBindError:
flash(
'Invalid username or password. Please try again.',
'danger')
return render_template('login.j2', form=form)
user = User.query.filter(User.username == username).first()
print(user)
if user is None:
user = User(username, password)
db.session.add(user)
user.authenticated = True
db.session.commit()
login_user(user, remember=form.remember_me.data)
print('You have successfully logged in.')
return redirect('/games')
if form.errors:
flash(form.errors, 'danger')
return render_template('login.j2', form=form)
@app.route('/add', methods=['POST']) @app.route('/add', methods=['POST'])
@login_required
def add_game(): def add_game():
if request.method == 'POST': if request.method == 'POST':
game_title = request.form['game_title'] game_title = request.form['game_title']
@ -141,8 +239,8 @@ def add_game():
return 'Error' return 'Error'
@ldap.login_required
@app.route('/delete', methods=['POST']) @app.route('/delete', methods=['POST'])
@login_required
def delete_game(): def delete_game():
if request.method == 'POST': if request.method == 'POST':
game_id = request.form['game_id'] game_id = request.form['game_id']
@ -176,8 +274,13 @@ def generate_game_list():
@app.route('/logout') @app.route('/logout')
@login_required
def logout(): def logout():
session.pop('user_id', None)
user = current_user
user.authenticated = False
db.session.add(user)
db.session.commit()
logout_user()
return redirect(url_for('game')) return redirect(url_for('game'))

+ 3
- 1
requirements.txt View File

@ -5,4 +5,6 @@ pytz
Flask-Login==0.5.0 Flask-Login==0.5.0
Flask-SimpleLDAP==1.4.0 Flask-SimpleLDAP==1.4.0
python-ldap==3.2.0 python-ldap==3.2.0
ldap3==2.7
ldap3==2.8.1
Flask-SQLAlchemy==2.4.4
Flask-WTF==0.14.3

+ 2
- 1
templates/card_list.j2 View File

@ -2,7 +2,8 @@
<div class="card" id="game-list"> <div class="card" id="game-list">
<div class="card-body" style="padding-bottom: 0 !important;"> <div class="card-body" style="padding-bottom: 0 !important;">
<h5 class="card-title" style="margin-bottom: 1.25rem !important;">Games In Progress</h5> <h5 class="card-title" style="margin-bottom: 1.25rem !important;">Games In Progress</h5>
{% if user %}
{{ current_user }}
{% if current_user.is_authenticated %}
<table class="table table-borderless" id="game-table"> <table class="table table-borderless" id="game-table">
<thead> <thead>
<tr> <tr>

+ 2
- 2
templates/games.j2 View File

@ -64,8 +64,8 @@
{{ super () }} {{ super () }}
{% if countdown is defined %} {% if countdown is defined %}
<script> <script>
const deadline = 9;{{ countdown['timestamp'] }}';;
const tripName = 9;{{ countdown['name'] }}';;
const deadline = 4;{{ countdown['timestamp'] }}";;
const tripName = 4;{{ countdown['name'] }}";;
</script> </script>
<script src="{{url_for('.static', filename='clock.js')}}"></script> <script src="{{url_for('.static', filename='clock.js')}}"></script>
{% endif %} {% endif %}

+ 2
- 2
templates/index.j2 View File

@ -59,8 +59,8 @@
{{ super () }} {{ super () }}
{% if countdown is defined %} {% if countdown is defined %}
<script> <script>
const deadline = 9;{{ countdown['timestamp'] }}';;
const tripName = 9;{{ countdown['name'] }}';;
const deadline = 4;{{ countdown['timestamp'] }}";;
const tripName = 4;{{ countdown['name'] }}";;
</script> </script>
<script src="{{url_for('.static', filename='clock.js')}}"></script> <script src="{{url_for('.static', filename='clock.js')}}"></script>
{% endif %} {% endif %}

+ 2
- 7
templates/login.j2 View File

@ -1,3 +1,4 @@
{% import "bootstrap/wtf.html" as wtf %}
{% extends "bootstrap/base.html" %} {% extends "bootstrap/base.html" %}
{% block title %}Manage your Technical Incompetence account{% endblock %} {% block title %}Manage your Technical Incompetence account{% endblock %}
@ -26,12 +27,6 @@
</div> </div>
{% endif %} {% endif %}
<form action="" method="post">
<label for="exampleFormControlInput1">Username</label>
<input name="user" class="form-control"><br>
<label for="exampleFormControlInput2">Password</label>
<input type="password" name="passwd" class="form-control"><br>
<button type="submit" class="btn btn-primary">Log In</button>
</form>
{{wtf.quick_form(form, novalidate=True)}}
</div> </div>
{% endblock %} {% endblock %}

Loading…
Cancel
Save