Files
photoassistant/main.py
2021-06-27 14:26:51 +10:00

155 lines
5.8 KiB
Python

from flask import Flask, render_template, request, redirect, jsonify, url_for, render_template_string
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.exc import SQLAlchemyError
from flask_marshmallow import Marshmallow
from flask_bootstrap import Bootstrap
from wtforms import SubmitField, StringField, HiddenField, SelectField, IntegerField, TextAreaField, validators
from flask_wtf import FlaskForm
from status import st, Status
from shared import CreateSelect, CreateFoldersSelect, LocationIcon, DB_URL
# for ldap auth
from flask_ldap3_login import LDAP3LoginManager
from flask_login import LoginManager, login_user, login_required, UserMixin, current_user
from flask_ldap3_login.forms import LDAPLoginForm
import re
import socket
# pylint: disable=no-member
####################################### Flask App globals #######################################
PROD_HOST="pa_web"
hostname = socket.gethostname()
print( "Running on: {}".format( hostname) )
app = Flask(__name__)
### what is this value? I gather I should change it?
app.config['SQLALCHEMY_DATABASE_URI'] = DB_URL
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config.from_mapping( SECRET_KEY=b'\xd6\x04\xbdj\xfe\xed$c\x1e@\xad\x0f\x13,@G')
# ldap config vars: (the last one is required, or python ldap freaks out)
app.config['LDAP_HOST'] = 'mara.ddp.net'
app.config['LDAP_BASE_DN'] = 'dc=depaoli,dc=id,dc=au'
app.config['LDAP_USER_DN'] = 'ou=users'
app.config['LDAP_GROUP_DN'] = 'ou=groups'
app.config['LDAP_USER_RDN_ATTR'] = 'cn'
app.config['LDAP_USER_LOGIN_ATTR'] = 'uid'
app.config['LDAP_BIND_USER_DN'] = None
app.config['LDAP_BIND_USER_PASSWORD'] = None
app.config['LDAP_GROUP_OBJECT_FILTER'] = '(objectclass=posixGroup)'
db = SQLAlchemy(app)
ma = Marshmallow(app)
Bootstrap(app)
login_manager = LoginManager(app) # Setup a Flask-Login Manager
ldap_manager = LDAP3LoginManager(app) # Setup a LDAP3 Login Manager.
login_manager.login_view = "login" # default login route, failed with url_for, so hard-coded
################################# Now, import non-book classes ###################################
from settings import Settings
from files import Entry, GetJM_Message, ClearJM_Message
from person import Person
from refimg import Refimg
from job import Job, GetNumActiveJobs
from ai import aistats
from path import StoragePathNames
from user import PAUser
####################################### GLOBALS #######################################
# allow jinja2 to call these python functions directly
app.jinja_env.add_extension('jinja2.ext.loopcontrols')
app.jinja_env.globals['ClearStatus'] = st.ClearStatus
app.jinja_env.globals['GetAlert'] = st.GetAlert
app.jinja_env.globals['GetMessage'] = st.GetMessage
app.jinja_env.globals['GetNumActiveJobs'] = GetNumActiveJobs
app.jinja_env.globals['GetJM_Message'] = GetJM_Message
app.jinja_env.globals['ClearJM_Message'] = ClearJM_Message
app.jinja_env.globals['CreateSelect'] = CreateSelect
app.jinja_env.globals['CreateFoldersSelect'] = CreateFoldersSelect
app.jinja_env.globals['LocationIcon'] = LocationIcon
app.jinja_env.globals['StoragePathNames'] = StoragePathNames
# Declare an Object Model for the user, and make it comply with the
# flask-login UserMixin mixin.
class User(UserMixin):
def __init__(self, dn, username, data):
self.dn = dn
self.username = username
self.data = data
def __repr__(self):
return self.dn
def get_id(self):
return self.dn
# Declare a User Loader for Flask-Login.
# Simply returns the User if it exists in our 'database', otherwise
# returns None.
@login_manager.user_loader
def load_user(id):
pau=PAUser.query.filter(PAUser.dn==id).first()
return pau
# Declare The User Saver for Flask-Ldap3-Login
# This method is called whenever a LDAPLoginForm() successfully validates.
# Here you have to save the user, and return it so it can be used in the
# login controller.
@ldap_manager.save_user
def save_user(dn, username, data, memberships):
pau=PAUser.query.filter(PAUser.dn==dn).first()
# if we already have a valid user/session, and say the web has restarted, just re-use it, dont make more users
if pau:
return pau
pau=PAUser(dn=dn)
db.session.add(pau)
db.session.commit()
return pau
# default page, just the navbar
@app.route("/", methods=["GET"])
@login_required
def main_page():
# Redirect users who are not logged in.
if not current_user or current_user.is_anonymous:
return redirect(url_for('login'))
return render_template("base.html")
@app.route('/login', methods=['GET', 'POST'])
def login():
# Instantiate a LDAPLoginForm which has a validator to check if the user
# exists in LDAP.
form = LDAPLoginForm()
form.submit.label.text="Login"
# the re matches on any special LDAP chars, we dont want someone
# ldap-injecting our username, so send them back to the login page instead
if request.method == 'POST' and re.search( r'[()\\*&!]', request.form['username']):
print( f"WARNING: Detected special LDAP chars in username: {request.form['username']}")
return redirect('/login')
if form.validate_on_submit():
# Successfully logged in, We can now access the saved user object
# via form.user.
print( f"form user = {form.user}" )
login_user(form.user, remember=True) # Tell flask-login to log them in.
next = request.args.get("next")
if next:
return redirect(next) # Send them back where they came from
else:
return redirect('/')
return render_template("login.html", form=form)
if __name__ == "__main__":
if hostname == PROD_HOST:
app.run(ssl_context=('/etc/letsencrypt/live/book.depaoli.id.au/cert.pem', '/etc/letsencrypt/live/book.depaoli.id.au/privkey.pem'), host="0.0.0.0", debug=False)
else:
app.run(host="0.0.0.0", debug=True)