From 41726395893c5f95b25cc0ce5b5e7fe13c713661 Mon Sep 17 00:00:00 2001 From: Daniel Muckerman Date: Sun, 1 Nov 2020 16:50:52 -0500 Subject: [PATCH] Initial commit --- .gitea/template | 8 + .gitignore | 18 ++ Dockerfile | 13 ++ app.py | 100 +++++++++++ manifest.json | 22 +++ requirements.txt | 7 + static/app-icon.png | Bin 0 -> 44035 bytes static/feather-sprite.svg | 1 + static/style.css | 313 +++++++++++++++++++++++++++++++++ templates/about.j2 | 64 +++++++ templates/fragments/navbar.j2 | 37 ++++ templates/fragments/sidebar.j2 | 39 ++++ templates/home.j2 | 50 ++++++ templates/login.j2 | 48 +++++ 14 files changed, 720 insertions(+) create mode 100644 .gitea/template create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 app.py create mode 100644 manifest.json create mode 100644 requirements.txt create mode 100644 static/app-icon.png create mode 100644 static/feather-sprite.svg create mode 100644 static/style.css create mode 100644 templates/about.j2 create mode 100644 templates/fragments/navbar.j2 create mode 100644 templates/fragments/sidebar.j2 create mode 100644 templates/home.j2 create mode 100644 templates/login.j2 diff --git a/.gitea/template b/.gitea/template new file mode 100644 index 0000000..d2e0ffe --- /dev/null +++ b/.gitea/template @@ -0,0 +1,8 @@ +# All jinja files, anywhere in the repository +**.j2 + +# Manifest.json +manifest.json + +# Dockerfile +Dockerfile \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9b235d9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +.envrc +.DS_Store +.vscode/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4ad31a0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM python:3.7 + +LABEL maintainer="$REPO_OWNER" + +WORKDIR /project +ADD . /project +RUN rm /project/.envrc +RUN rm -rf /project/env + +RUN apt-get update && apt-get install -y python3-dev libldap2-dev libsasl2-dev libssl-dev + +RUN pip install -r requirements.txt +CMD ["flask","run","--host=0.0.0.0"] \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..8791d64 --- /dev/null +++ b/app.py @@ -0,0 +1,100 @@ +import ldap as l +from ldap3 import Server, Connection, ALL, MODIFY_REPLACE +from flask import Flask, g, request, session, redirect, url_for, render_template, send_from_directory +from flask_simpleldap import LDAP +from flask_bootstrap import Bootstrap +import os +from flask_cache_buster import CacheBuster + + +app = Flask(__name__) +Bootstrap(app) +app.secret_key = 'asdf' +app.debug = True + +# Base +app.config['LDAP_REALM_NAME'] = 'OpenLDAP Authentication' +app.config['LDAP_HOST'] = os.environ.get('LDAP_HOST') +app.config['LDAP_BASE_DN'] = os.environ.get('LDAP_BASE_DN') +app.config['LDAP_USERNAME'] = os.environ.get('LDAP_USERNAME') +app.config['LDAP_PASSWORD'] = os.environ.get('LDAP_PASSWORD') + +# OpenLDAP +app.config['LDAP_OBJECTS_DN'] = 'dn' +app.config['LDAP_OPENLDAP'] = True +app.config['LDAP_USER_OBJECT_FILTER'] = '(&(objectclass=posixAccount)(uid=%s))' + +ldap = LDAP(app) + +config = { + 'extensions': ['.js', '.css', '.csv'], + 'hash_size': 10 +} + +cache_buster = CacheBuster(config=config) +cache_buster.register_cache_buster(app) + +server = Server(app.config['LDAP_HOST']) +conn = Connection(server, app.config['LDAP_USERNAME'], app.config['LDAP_PASSWORD'], auto_bind=True) + +@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 = {} + + +@app.route("/manifest.json") +def manifest(): + return send_from_directory('./', 'manifest.json') + + +@app.route('/') +@ldap.login_required +def index(): + user_dict = ldap.get_object_details(session['user_id']) + + if 'user_id' in session: + 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'), + } + + return render_template('home.j2') + + +@app.route('/about') +@ldap.login_required +def about(): + return render_template('about.j2') + + +@app.route('/login', methods=['GET', 'POST']) +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('/') + return render_template('login.j2') + + +@app.route('/logout') +def logout(): + session.pop('user_id', None) + return redirect(url_for('index')) + + +if __name__ == '__main__': + app.run() \ No newline at end of file diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..166f7b1 --- /dev/null +++ b/manifest.json @@ -0,0 +1,22 @@ +{ + "short_name": "$REPO_NAME", + "name": "$REPO_NAME", + "description": "$REPO_DESCRIPTION", + "icons": [ + { + "src": "/images/icons-192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "/images/icons-512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": "/", + "background_color": "#343a40", + "theme_color": "#343a40", + "display": "standalone", + "scope": "/" + } \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..dd20e31 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +Flask==1.1.2 +Flask-Bootstrap4==4.0.2 +Flask-Login==0.5.0 +Flask-SimpleLDAP==1.4.0 +python-ldap==3.2.0 +ldap3==2.7 +Flask-Cache-Buster==1.0.1 \ No newline at end of file diff --git a/static/app-icon.png b/static/app-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b443cf601b7c9f1c1feba5425d8afb298462599b GIT binary patch literal 44035 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 literal 0 HcmV?d00001 diff --git a/static/feather-sprite.svg b/static/feather-sprite.svg new file mode 100644 index 0000000..6e20a2c --- /dev/null +++ b/static/feather-sprite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..6020049 --- /dev/null +++ b/static/style.css @@ -0,0 +1,313 @@ +/* Scroll fix, try to disable overflow scrolling on mobile to make this feel more native */ +html, body { + width: 100%; + height: 100%; + overflow: hidden; + position: fixed; + padding-bottom: 0; + overscroll-behavior-y: none; +} + +/* Hide bootstrap tooltip arrows +I did this so I wouldn't have to deal with toolbar button alignment but you can change that if you want */ +.tooltip .arrow { + display: none; +} + +/* Navbar */ +.navbar { + position: absolute; + top: 0; + width: 100%; +} + +.toolbar-button { + padding-left: 2rem; + padding-top: .375rem; + line-height: 1.5; + color: white; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.toolbar-button:hover { + color: #ddd; + text-decoration: none; +} + +/* Sidebar */ +#sidebar { + padding-left: 20px; + background-color: #ecedee; + height: 100vh; + padding-top: 20px; + border-right: 1px solid darkgray; + position: fixed; + width: 250px; +} + +#sidebar a:hover { + text-decoration: none; +} + +#sidebar ul > li { + padding-bottom: 10px; +} + +#sidebar ul > li > a { + color: rgba(0,0,0,.5); +} + +#sidebar ul > li.active > a { + color: black; +} + +#sidebar div > a { + color: rgba(0,0,0,.5); +} + +#sidebar div.active > a { + color: black; +} + +/* Content area */ +.wrapper { + height: calc(100% - 56px); + overflow: auto; + position: fixed; + top: 56px; + width: 100%; + -webkit-overflow-scrolling: touch; + align-items: stretch; +} + +#content { + width: 100%; + padding: 20px; +} + +/* Dark theming */ +@media (prefers-color-scheme: dark) { + /* Custom Bootstrap dark theming */ + + body { + background-color: #111 !important; + color: #eee; + } + + .jumbotron { + background-color: #333 !important; + } + + .modal-content { + background-color: #111 !important; + color: #eee; + } + + .modal-header { + border-bottom: 1px solid #555 !important; + } + + .modal-header .close { + color: #eee !important; + text-shadow: 0 1px 0 #555 !important; + } + + .modal-footer { + border-top: 1px solid #555 !important; + } + + .bg-light { + background-color: #333 !important; + } + + .bg-white { + background-color: #000 !important; + } + + .bg-black { + background-color: #eee !important; + } + + .form-control { + display: block; + width: 100%; + height: calc(1.5em + 0.75rem + 2px); + padding: 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #dee2e6; + background-color: #000; + background-clip: padding-box; + border: 1px solid #6c757d; + border-radius: 0.25rem; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + } + + @media (prefers-reduced-motion: reduce) { + .form-control { + transition: none; + } + } + + .form-control::-ms-expand { + background-color: transparent; + border: 0; + } + + .form-control:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #dee2e6; + } + + .form-control:focus { + color: #dee2e6; + background-color: #191d21; + border-color: #b3d7ff; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); + } + + .form-control::-webkit-input-placeholder { + color: #6c757d; + opacity: 1; + } + + .form-control::-moz-placeholder { + color: #6c757d; + opacity: 1; + } + + .form-control::-ms-input-placeholder { + color: #6c757d; + opacity: 1; + } + + .form-control::placeholder { + color: #6c757d; + opacity: 1; + } + + .form-control:disabled, + .form-control[readonly] { + background-color: #343a40; + opacity: 1; + } + + .card { + background-color: #000; + border: 1px solid #6c757d; + } + + /* Sidebar */ + #sidebar { + padding-left: 20px; + background-color: #1a1d20; + height: 100vh; + padding-top: 20px; + border-right: 1px solid black; + } + + #sidebar ul > li { + padding-bottom: 10px; + } + + #sidebar ul > li > a { + color: rgba(255,255,255,.5); + } + + #sidebar ul > li.active > a { + color: white; + } + + #sidebar div > a { + color: rgba(255,255,255,.5); + } + + #sidebar div.active > a { + color: white; + } +} + +/* PWA theming fixes +Tested basically only on iOS, so Android, get fucked maybe */ +@media all and (display-mode: standalone) { + .navbar { + position: absolute; + top: 0; + width: 100%; + padding-top: 44px !important; + } + + .wrapper { + height: calc(100vh - 92px); + overflow: auto; + position: fixed; + top: 92px; + width: 100%; + align-items: stretch; + padding-left: env(safe-area-inset-left); + padding-right: env(safe-area-inset-right); + padding-bottom: env(safe-area-inset-bottom); + } + + /* Tweaks for size classes above portait phones */ + @media (min-width: 577px) { + .navbar { + position: absolute; + top: 0; + width: 100%; + padding-top: 24px !important; + } + + #navbarNavDropdown { + padding-left: env(safe-area-inset-left); + padding-right: env(safe-area-inset-right); + } + + .wrapper { + height: calc(100% - 72px); + overflow: auto; + position: fixed; + top: 72px; + width: 100%; + align-items: stretch; + padding-left: env(safe-area-inset-left); + padding-right: env(safe-area-inset-right); + padding-bottom: env(safe-area-insert-bottom); + } + } +} + +/* Hide sidebar on phones and portrait tablets */ +@media (max-width: 991.98px) { + #sidebar { + display: none !important; + } +} + +/* Display sidebar and hide nav menu for landscape tablets and desktop */ +@media (min-width: 991.98px) { + .navbar-nav { + display: none !important; + } + + #sidebar { + min-width: 250px; + max-width: 250px; + display: block !important; + } + + #content { + left: 250px; + width: calc(100% - 250px) !important; + position: relative; + } + + #sidebar.active { + margin-left: -250px; + } +} \ No newline at end of file diff --git a/templates/about.j2 b/templates/about.j2 new file mode 100644 index 0000000..51c3eee --- /dev/null +++ b/templates/about.j2 @@ -0,0 +1,64 @@ +{% extends "bootstrap/base.html" %} +{% block title %}$REPO_NAME{% endblock %} + +{% block metas %} +{{super()}} + + + + + +{% endblock %} + +{% block styles %} +{{super()}} + +{% endblock %} + +{% block navbar %} + {% include "fragments/navbar.j2" %} +{% endblock %} + +{% block content %} +
+ + + + +
+
+ Made with + and + in Outer Austin! +
+
+
+{% endblock %} + +{% block scripts %} +{{ super() }} + +{% endblock %} \ No newline at end of file diff --git a/templates/fragments/navbar.j2 b/templates/fragments/navbar.j2 new file mode 100644 index 0000000..0da32e4 --- /dev/null +++ b/templates/fragments/navbar.j2 @@ -0,0 +1,37 @@ + \ No newline at end of file diff --git a/templates/fragments/sidebar.j2 b/templates/fragments/sidebar.j2 new file mode 100644 index 0000000..7f197e6 --- /dev/null +++ b/templates/fragments/sidebar.j2 @@ -0,0 +1,39 @@ +
    + {% if request.path == url_for('index') %} + +
+ +{% if request.path == url_for('about') %} +
+{% else %} +
+{% endif %} + + + About +
\ No newline at end of file diff --git a/templates/home.j2 b/templates/home.j2 new file mode 100644 index 0000000..8a6ab10 --- /dev/null +++ b/templates/home.j2 @@ -0,0 +1,50 @@ +{% extends "bootstrap/base.html" %} +{% block title %}$REPO_NAME{% endblock %} + +{% block metas %} +{{super()}} + + + + + +{% endblock %} + +{% block styles %} +{{super()}} + +{% endblock %} + +{% block navbar %} + {% include "fragments/navbar.j2" %} +{% endblock %} + +{% block content %} +
+ + + + +
+
+
+
+ This is your home page, have fun! +
+
+
+
+
+{% endblock %} + +{% block scripts %} +{{ super() }} + +{% endblock %} \ No newline at end of file diff --git a/templates/login.j2 b/templates/login.j2 new file mode 100644 index 0000000..00fda09 --- /dev/null +++ b/templates/login.j2 @@ -0,0 +1,48 @@ +{% extends "bootstrap/base.html" %} +{% block title %}$REPO_NAME{% endblock %} + +{% block metas %} +{{super()}} + + + + + +{% endblock %} + +{% block styles %} +{{super()}} + +{% endblock %} + +{% block navbar %} + +{% endblock %} + +{% block content %} +
+
+
+

Sign in for our awesome service

+

Forgot your password? Too bad! We don't have emails working yet!

+
+
+ + {% if error is defined %} + + {% endif %} + +
+ +
+ +
+ +
+
+
+{% endblock %} \ No newline at end of file